I want to know how you can delete a instance within the class. I am trying del self, but it doesn't seem to work. Here's my code:
class Thing:
def __init__(self):
self.alive = True
self.age = 0
def update(self):
self.age += 1
def kill(self):
if self.age >= 10:
del self
def all(self):
self.update()
self.kill()
things = []
for i in range(0, 10):
thing.append(Thing())
while True:
for thing in things:
thing.all()
I specifically want to delete the instance inside the class. I have also replaced del self with self = None, but this statement doesn't seem to have any effect. How can I do this?
You can't do quite what you're asking for. Python's del statement doesn't work like that. What you can do however, is mark your instance as dead (you already have an attribute for this!), and then later, filter the list of objects to drop the dead ones:
class Thing:
def __init__(self):
self.alive = True # use this attribute!
self.age = 0
def update(self):
self.age += 1
def kill(self):
if self.age >= 10:
self.alive = False # change it's value here rather than messing around with del
def all(self):
self.update()
self.kill()
things = []
for i in range(0, 10):
things.append(Thing())
while True:
for thing in things:
thing.all()
things = [thing for thing in things if thing.alive] # filter the list
Note that the loops at the end of this code run forever with no output, even after all the Thing instances are dead. You might want to modify that so you can tell what's going on, or even change the while loop to check if there's are any objects left in things. Using while things instead of while True might be a reasonable approach!
I had the same trouble last time. I searched some answers of and got one. You can use self.__delete__(). This delete method that has two underscores around it can only delete an object, so you are good to go!!!
Related
I'm currently learning how to write object oriented programming in python and I have this code I wrote (well part of it, some of it is part of the class I'm following)
class Toolbox:
def __init__(self, tool):
pass
class Hammer:
def __init__(self, color):
pass
class Screwdriver:
def __init__(self, size):
pass
class Screw:
MAX_TIGHTNESS = 5
def __init__(self):
self.tightness = 0
def loosen(self):
if (self.tightness > 0):
self.tightness -= 1
def tighten(self):
if (self.tightness < self.MAX_TIGHTNESS):
self.tightness += 1
def __str__(self):
return "Screw with tightness {}".format(self.tightness)
class Nail:
def __init__(self):
self.in_wall = False
def nail_in(self):
if (not self.in_wall):
self.in_wall = True
def remove(self):
if (self.in_wall):
self.in_wall = False
def __str__(self):
return "Nail {}in wall.".format("" if self.in_wall else "not ")
hammer = Hammer("blue")
sdriver = Screwdriver("300")
#tbox = Toolbox()
tool1 = Toolbox(hammer)
tool2 = Toolbox(sdriver)
screw1 = Screw()
print(screw1)
screw1.tighten()
print(screw1)
nail1 = Nail()
print(nail1)
nail1.nail_in
print(nail1)
The output of print(screw1) works but print(nail1) gives me the same output (Nail in wall.) twice instead Nail in wall. I guess there's a problem in my logic and I can't seem to figure it out. Can someone help me?
Thanks!
Marc
You need to call nail_in - just as you are already doing with screw1.tighten. So do this (note the brackets here):
nail1.nail_in()
With this fix, your code works:
Screw with tightness 0
Screw with tightness 1
Nail not in wall.
Nail in wall.
By referring to the function but without the brackets, it is a syntactically valid line of code, but it is just an expression for the function object itself, and as you are not doing anything with it (such as assigning it to a variable), it is then simply discarded.
I've made a simple game using pygame and livewires, where a sprite has to avoid falling mushrooms. The number of mushrooms falling at a certain time is meant to increase as the score increases. Here is what I mean:
from livewires import games,color
import random
games.init(screen_width=633,screen_height=479,fps=50)
class Stick_Man(games.Sprite):
def update(self):
self.x=games.mouse.x
if self.left<0:
self.left=0
if self.right>games.screen.width:
self.right=games.screen.width
self.check_collision()
def check_collision(self):
if self.overlapping_sprites:
self.over_message()
def over_message(self):
b=games.Message(value="Game Over", size=100, color=color.red,x=games.screen.width/2,y=games.screen.height/2,lifetime=250,after_death=games.screen.quit)
games.screen.add(b)
class Mushroom(games.Sprite):
score=0
start=200
score_required=100
level=1
total_score=0
speed=1
mushroom=games.load_image("mushroom.jpg")
x_position=random.randrange(640)
#staticmethod
def next_level():
indicate='Level ', + Mushroom.level, ' cleared'
message=games.Message(value=indicate,size=50,color=color.red,x=games.screen.width/2,y=games.screen.height/2, lifetime=150)
games.screen.add(message)
Mushroom().score_required+=50
Mushroom().score-=Mushroom.score_required
Mushroom().start-=150
Mushroom().speed+=5
Mushroom().level+=1
if Mushroom().start==20:
Mushroom().start+=10
def __init__(self):
super(Mushroom,self).__init__(image=Mushroom.mushroom,x=games.mouse.x,y=0)
def update(self):
self.dy=Mushroom.speed
self.check()
self.check2()
def check(self):
if self.bottom==games.screen.height:
self.destroy()
Mushroom.score+=50
Mushroom.total_score+=Mushroom.score
if Mushroom().score==Mushroom.score_required:
self.next_level()
def check2(self):
if self.top==Mushroom.start:
self.duplicate()
def duplicate(self):
new_mush=Mushroom()
games.screen.add(new_mush)
background_image=games.load_image("background.jpg", transparent=False)
games.screen.background=background_image
stickman_image=games.load_image("stickman.png", transparent=True)
stickman=Stick_Man(image=stickman_image,left=1,bottom=480)
games.screen.add(stickman)
games.mouse.is_visible=False
b=Mushroom()
c=Mushroom()
a=Mushroom()
games.screen.add(b)
games.screen.add(a)
games.screen.add(c)
games.screen.event_brab=True
games.screen.mainloop()
The code is pretty self explanatory and whenever one of the mushrooms is equal to start, then a new object is created thus meaning a new mushroom comes in. However, what happens is that code doesn't function properly a second time and the mushrooms don't get faster spawn much faster either. Also, when the game first starts, the minute the first mushroom hits the bottom it says level one cleared, when it should be after two mushrooms. The sprite is just a red mushroom and also a stickman which can be found on g images if you want to simulate.
So my question is how do i make the object's STATS carry on from where it left off whenever another mushroom appears and also display the message at the right time
Your problem is in all of the lines that look like this:
Mushroom().score_required+=50
There are a number of problems here, which all together add up to make this have no useful effect:
Mushroom() creates a new Mushroom instance (which goes away as soon as this line is done).
Assigning (including update-assigning) to an attribute through an instance always creates or updates an instance attribute, even if there was a class attribute of the same name.
The += operator doesn't mutate immutable values like integers in-place (because that would be impossible); a += b is effectively the same as a = a + b.*
So, when you put that together, what you're doing is creating a new value equal to Mushroom.score_required + 50, then assigning that value to a new instance attribute of a temporary instance (which immediately goes away). This has no effect on the class attribute, or on any of the other instances.
You have a related, but different, problem in the lines like this:
x_position=random.randrange(640)
Unless you want all of the mushrooms to have the same x_position, this should not be a class attribute, but an instance attribute, and you're going to run into all kinds of strange problems.
Storing game stats as class attributes of a random class is a strange thing to do. There are ways you could make that work, but there's no good reason to even try. Class attributes are useful for constants that all instances of the class might as well share, but they're not useful as a substitute for global variables.
A better design would be something like this:
class Game(object):
def __init__(self):
self.score = 0
self.start = 200
self.score_required = 100
self.level = 1
self.total_score = 0
def next_level(self):
indicate = 'Level ', + Mushroom.level, ' cleared'
message = games.Message(value=indicate, size=50, color=color.red,
x=games.screen.width/2, y=games.screen.height/2,
lifetime=150)
games.screen.add(message)
self.score_required += 50
self.score -= self.score_required
self.start -= 150
self.speed += 5
self.level += 1
if self.start == 20:
self.start += 10
def update_score(self, n):
game.score += n
game.total_score += game.score
if self.score == self.score_required:
self.next_level()
class Mushroom(games.Sprite):
mushroom=games.load_image("mushroom.jpg")
def __init__(self, game):
self.x_position=random.randrange(640)
self.game = game
super(Mushroom,self).__init__(image=Mushroom.mushroom,x=games.mouse.x,y=0)
def update(self):
self.dy=Mushroom.speed
self.check()
self.check2()
def check(self):
if self.bottom == games.screen.height:
self.destroy()
game.update_score(50)
def check2(self):
if self.top == Mushroom.start:
self.duplicate()
def duplicate(self):
games.screen.add(Mushroom(self.game))
game = Game()
games.screen.add(Mushroom(game))
games.screen.add(Mushroom(game))
games.screen.add(Mushroom(game))
games.screen.event_brab=True
* That's not completely true. In fact, a = a + b is equivalent to a = a.__add__(b), while a += b is equivalent to a = a.__iadd__(b) if such a method exists, falling back to __add__ only if it doesn't. For mutable objects like lists, this makes a big difference, because __iadd__ can change self in-place and then return it, meaning you end up assigning the same object back to a that was already there. But for immutable objects, there's no difference.
I am trying to simply get the value out of my class using a simple function with a return value, I'm sure its a trivial error, but im pretty new to python
I have a simply class set up like this:
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(num):
self.score = num
# Enemy Info
def getEnemies():
return self.num_enemies
# Lives Info
def getLives():
return self.getLives
etc.....
Than I create an instance of the class as such:
scoreObj = score()
for enemies in range(0, scoreObj.getEnemies):
enemy_sprite.add(enemy())
I get the error saying that an integer is expected, but it got an instancemethod
What is the correct way to get this information?
Thanks!
scoreObj.getEnemies is a reference to the method. If you want to call it you need parentheses: scoreObj.getEnemies().
You should think about why you are using a method for this instead of just reading self.num_enemies directly. There is no need for trivial getter/setter methods like this in Python.
The first parameter for a member function in python is a reference back to the Object.
Traditionally you call it "self", but no matter what you call the first parameter, it refers back to the "self" object:
Anytime I get weird errors about the type of a parameter in python, I check to see if I forgot the self param. Been bit by this bug a few times.
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(self, num):
self.score = num
# Enemy Info
def getEnemies(self):
return self.num_enemies
# Lives Info
def getLives(foo): #foo is still the same object as self!!
return foo.num_lives
#Works but don't do this because it is confusing
This code works:
class score():
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
def setScore(self, num):
self.score = num
def getEnemies(self):
return self.num_enemies
def getLives(self):
return self.getLives
scoreObj = score()
for enemy_num in range(0, scoreObj.getEnemies()):
print enemy_num
# I don't know what enemy_sprite is, but
# I commented it out and just print the enemy_num result.
# enemy_sprite.add(enemy())
Lesson Learned:
Class functions must always take one parameter, self.
That's because when you call a function within the class, you always call it with the class name as the calling object, such as:
scoreObj = score()
scoreObj.getEnemies()
Where x is the class object, which will be passed to getEnemies() as the root object, meaning the first parameter sent to the class.
Secondly, when calling functions within a class (or at all), always end with () since that's the definition of calling something in Python.
Then, ask yourself, "Why am I not fetching 'scoreObj.num_lives' just like so instead? Am I saving processing power?" Do as you choose, but it would go faster if you get the values directly from the class object, unless you want to calculate stuff at the same time. Then your logic makes perfect sense!
You made a simple mistake:
scoreObj.getEnemies()
getEnemies is a function, so call it like any other function scoreObj.getEnemies()
As an example, just a couple of dummy objects that will be used together. FWIW this is using Python 2.7.2.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)
That's probably enough code, you see where I'm going with this. If I call Hammer.break(), I'm calling it on the same instance of the object; if Billy's hammer is broken, so is Tommy's (it's really the same Hammer after all).
Now obviously if the program were limited to just Billy and Tommy as instances of Students, the fix would be obvious - instantiate more Hammers. But clearly I'm asking because it isn't that simple, heh. I would like to know if it's possible to create objects which show up as unique instances of themselves for every time they're called into being.
EDIT: The kind of answers I'm getting lead me to believe that I have a gaping hole in my understanding of instantiation. If I have something like this:
class Foo(object):
pass
class Moo(Foo):
pass
class Guy(object):
def __init__(self, thing):
self.thing = thing
Bill = Guy(Moo())
Steve = Guy(Moo())
Each time I use Moo(), is that a separate instance, or do they both reference the same object? If they're separate, then my whole question can be withdrawn, because it'll ahve to make way for my mind getting blown.
You have to create new instances of the Tool for each Student.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
# Instead of instance, make it a callable that returns a new one
def Hammer():
return Tool('hammer')
# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
I'll try to be brief. Well.. I always try to be brief, but my level of success is pretty much random.randint(0, never). So yeah.
Lol. You even failed to be brief about announcing that you will try to be brief.
First, we need to be clear about what "called into being" means. Presumably you want a new hammer every time self.tool = object happens. You don't want a new instance every time, for example, you access the tool attribute, or you'd always a get a new, presumably unbroken, hammer every time you check self.tool.broken.
A couple approaches.
One, give Tool a copy method that produces a new object that should equal the original object, but be a different instance. For example:
class Tool:
def __init__(self, kind):
self.kind = kind
self.broken = False
def copy(self):
result = Tool(self.kind)
result.broken = self.broken
return result
Then in Student's init you say
self.tool = tool.copy()
Option two, use a factory function.
def makehammer():
return Tool(hammer)
class Student:
def __init__(self, factory):
self.tool = factory()
Billy = Student(makehammer)
I can't think any way in Python that you can write the line self.tool = object and have object automagically make a copy, and I don't think you want to. One thing I like about Python is WYSIWYG. If you want magic use C++. I think it makes code hard to understand when you not only can't tell what a line of code is doing, you can't even tell it's doing anything special.
Note you can get even fancier with a factory object. For example:
class RealisticFactory:
def __init__(self, kind, failurerate):
self.kind = kind
self.failurerate = failurerate
def make(self):
result = Tool(self.kind)
if random.random() < self.failurerate:
result.broken = True
if (self.failurerate < 0.01):
self.failurerate += 0.0001
return result
factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
You could change your lines like this:
Billy = Student(Tool('hammer'))
Tommy = Student(Tool('hammer'))
That'll produce a distinct instance of your Tool class for each instance of the Student class. the trouble with your posted example code is that you haven't "called the Tool into being" (to use your words) more than once.
Just call Tool('hammer') every time you want to create a new tool.
h1 = Tool('hammer')
h2 = Tool('hammer')
Billy = Student(h1)
Tommy = Student(h2)
Oh wait, I forgot, Python does have magic.
class Student:
def __setattr__(self, attr, value):
if attr == 'tool':
self.__dict__[attr] = value.copy()
else:
self.__dict__[attr] = value
But I still say you should use magic sparingly.
After seeing the tenor of the answers here and remembering the Zen of Python, I'm going to answer my own dang question by saying, "I probably should have just thought harder about it."
I will restate my own question as the answer. Suppose I have this tiny program:
class Item(object):
def __init__(self):
self.broken = False
def smash(self):
print "This object broke."
self.broken = True
class Person(object):
def __init__(self, holding):
self.holding = holding
def using(self):
if self.holding.broken != True:
print "Pass."
else:
print "Fail."
Foo = Person(Item())
Bar = Person(Item())
Foo.holding.smash()
Foo.using()
Bar.using()
The program will return "Fail" for Foo.using() and "Pass" for Bar.using(). Upon actually thinking about what I'm doing, "Foo.holding = Item()" and "Bar.holding = Item()" are clearly different instances. I even ran this dumpy program to prove it worked as I surmised it did, and no surprises to you pros, it does. So I withdraw my question on the basis that I wasn't actually using my brain when I asked it. The funny thing is, with the program I've been working on, I was already doing it this way but assuming it was the wrong way to do it. So thanks for humoring me.
Every once in a while I like to take a break from my other projects to try to make a classic adventure text-based-game (in Python, this time) as a fun project, but I always have design issues implementing the item system.
I'd like for the items in the game to descend from one base Item class, containing some attributes that every item has, such as damage and weight. My problems begin when I try to add some functionality to these items. When an item's damage gets past a threshold, it should be destroyed. And there lies my problem: I don't really know how to accomplish that.
Since del self won't work for a million different reasons, (Edit: I am intentionally providing the use of 'del' as something that I know is wrong. I know what garbage collection is, and how it is not what I want.) how should I do this (And other similar tasks)? Should each item contain some kind of reference to it's container (The player, I guess) and 'ask' for itself to be deleted?
The first thing that comes to mind is a big dictionary containing every item in the game, and each object would have a reference to this list, and both have and know it's own unique ID. I don't like this solution at all and I don't think that it's the right way to go at all. Does anybody have any suggestions?
EDIT: I'm seeing a lot of people thinking that I'm worried about garbage collection. What I'm talking about is not garbage collection, but actually removing the object from gameplay. I'm not sure about what objects should initiate the removal, etc.
I would have your object keep a reference to all of its parents. Then, when it should be destroyed, it would notify its parents. If you're already using an event system, this should integrate nicely with the rest of the game.
A nice way to avoid forcing your parent to explicitly notify the object whenever the reference is dropped or added is to use some sort of proxy. Python supports properties that will allow for code like self.weapon = Weapon() to actually hand off the duty of setting the weapon attribute to the new weapon to a user defined function.
Here's some example code using properties:
class Weapon(object):
def __init__(self, name):
self.name = name
self.parent = None
def destroy(self):
if self.parent:
self.parent.weaponDestroyed()
def WeaponRef():
def getWeapon(self):
return self._weapon
def setWeapon(self, newWeapon):
if newWeapon == None: #ensure that this is a valid weapon
delWeapon(self)
return
if hasattr(self, "weapon"): #remove old weapon's reference to us
self._weapon.parent = None
self._weapon = newWeapon
newWeapon.parent = self
def delWeapon(self):
if hasattr(self, "weapon"):
self._weapon.parent = None
del self._weapon
return property(getWeapon, setWeapon, delWeapon)
class Parent(object):
weapon = WeaponRef()
def __init__(self, name, weapon=None):
self.name = name
self.weapon = weapon
def weaponDestroyed(self):
print "%s deleting reference to %s" %(self.name, self.weapon.name)
del self.weapon
w1 = Weapon("weapon 1")
w2 = Weapon("weapon 2")
w3 = Weapon("weapon 3")
p1 = Parent("parent 1", w1)
p2 = Parent("parent 2")
w1.destroy()
p2.weapon = w2
w2.destroy()
p2.weapon = w3
w3.destroy()
Now if you're doing some sort of inventory system, where a player can have more than 1 weapon and any one of them can be destroyed at any time, then you're going to have to write your own collection class.
For something like that, just keep in mind that x[2] calls x.__getitem__(2), x[2] = 5 calls x.__setitem__(2, 5) and del x[2] calls x.__delitem__(2)
You're conflating two meanings of the "destroying" idea. The Item should get destroyed in a "gameplay" sense. Let the garbage collector worry about when to destroy it as an object.
Who has a reference to the Item? Perhaps the player has it in his inventory, or it is in a room in the game. In either case your Inventory or Room objects know about the Item. Tell them the Item has been destroyed (in a gameplay sense) and let them handle that. Perhaps they'll now keep a reference to a "broken" Item. Perhaps they'll keep track of it, but not display it to the user. Perhaps they'll delete all references to it, in which case the object in memory will soon be deleted.
The beauty of object-oriented programming is that you can abstract these processes away from the Item itself: pass the messages to whoever needs to know, and let them implement in their own way what it means for the Item to be destroyed.
One option would be to use a signal system
Firstly, we have a reusable class that lets you define a signal
class Signal(object):
def __init__(self):
self._handlers = []
def connect(self, handler):
self._handlers.append(handler)
def fire(self, *args):
for handler in self._handlers:
handler(*args)
Your item class uses this signal to create a destroyed signal that other classes can listen for.
class Item(object):
def __init__(self):
self.destroyed = Signal()
def destroy(self):
self.destroyed.fire(self)
And inventory listens to the signals from the items and updates its internal state accordingly
class Inventory(object):
def __init__(self):
self._items = []
def add(self, item):
item.destroyed.connect(self.on_destroyed)
self._items.add(item)
def on_destroyed(self, item):
self._items.remove(item)
Assuming you call a method when the item is used, you could always return a boolean value indicating whether it's broken.
How about:
from collections import defaultdict
_items = defaultdict(set)
_owner = {}
class CanHaveItems(object):
#property
def items(self):
return iter(_items[self])
def take(self, item):
item.change_owner(self)
def lose(self, item):
""" local cleanup """
class _nobody(CanHaveItems):
def __repr__(self):
return '_nobody'
_nobody = _nobody()
class Destroyed(object):
def __repr__(self):
return 'This is an ex-item!'
class Item(object):
def __new__(cls, *a, **k):
self = object.__new__(cls)
_owner[self] = _nobody
_items[_nobody].add(self)
self._damage = .0
return self
def destroy(self):
self.change_owner(_nobody)
self.__class__ = Destroyed
#property
def damage(self):
return self._damage
#damage.setter
def damage(self, value):
self._damage = value
if self._damage >= 1.:
self.destroy()
def change_owner(self, new_owner):
old_owner = _owner[self]
old_owner.lose(self)
_items[old_owner].discard(self)
_owner[self] = new_owner
_items[new_owner].add(self)
class Ball(Item):
def __init__(self, color):
self.color = color
def __repr__(self):
return 'Ball(%s)' % self.color
class Player(CanHaveItems):
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Player(%s)' % self.name
ball = Ball('red')
ball = Ball('blue')
joe = Player('joe')
jim = Player('jim')
print list(joe.items), ':', list(jim.items)
joe.take(ball)
print list(joe.items), ':', list(jim.items)
jim.take(ball)
print list(joe.items), ':', list(jim.items)
print ball, ':', _owner[ball], ':', list(jim.items)
ball.damage += 2
print ball, ':', _owner[ball], ':', list(jim.items)
print _items, ':', _owner
at first: i don't have any python experience, so think about this in a more general way
your item should neither know or care ... your Item should have an interface that says it is something destroyable. containers and other objects that care about things that can be destroyed, can make use of that interface
that destroyable interface could have some option for consuming objects to register a callback or event, triggered when the item gets destroyed