[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Re: [pygame] Please explain what I've done wrong.
[ I'm pretty sure this is off topic for this list, but the response you've had
might risk you writing code that's even harder for you to see what's
happening ]
On Saturday 17 September 2005 20:00, Jason wrote:
> I've looked through the code but can't find anything obvious.
It's because there isn't a problem with your code, per se, it's more to do
with python shutdown.
If you put your code, including the test cases, inside a script and run it,
the following happens:
* The class is created
* The instances are created
* We hit the end of the script, so python starts to shutdown
* The class and instances are __del__eted at shutdown, *in no particular
order*
* BANG!
If deletion is "forced" to happen in the following order - instances, followed
by class - by adding the following at the end of your code:
Jason = None # invalidate Jason, allow to be deleted
Sophie = None # invalidate Sophie, allow to be deleted
import time
t = time.time()
while time.time() - t < 1:
pass
Then the code works as you expect.
The problem you're seeing is the deletion appears to happen in the following
order - at least on my machine mirroring your error:
* Jason deleted
* class deleted
* Sophie deleted - BANG! (due to trying to reference something that is no
longer really valid)
Clearly this means that by the time Sophie is deleted "Person" no longer
really exists, and hence can't really be used for anything.
(Actually I suspect the actual nitty-gritties under the hood are a little more
complex, but the above APPEARS to be resulting behaviour. I'd have to look
inside the python code to know for certain)
If you change your tests to something slightly more idiomatic, such as:
def main():
Jason=Person("Jason")
Jason.sayHi()
Jason.howMany()
Sophie=Person("Sophie")
Sophie.sayHi()
Sophie.howMany()
if __name__ == "__main__":
main()
Then you find you get the right behaviour. The reason is because the locals in
main() are no longer valid the instant the function call exits.
I'd advise BTW against trying to access & update a class variable via the
alternate suggestion of self.population since you're likely to run into all
sorts of bizarre issues that way.
A better alternative to self.population is to do self.__class__.population -
which would update the class that self belongs to.
For example if I modify your example to also have a Frank, who is an employee:
class Frank(Person):
population = 0
And change all occurances of Person.population to self.__class__.population
and all occurances of the string "Person" with self.__class__.__name__, then
you get the following behaviour:
(Initialising Jason)
Hi, my name is Jason
I am on the only Person here.
(Initialising Sophie)
Hi, my name is Sophie
We have 2 Persons here.
(Initialising Jason)
Hi, my name is Frank
I am on the only Employee here.
Sophie says bye.
There are still 1 people left.
Jason says bye.
I am the last Person
Jason says bye.
I am the last Employee
When the following test harness runs:
def main():
Jason=Person("Jason")
Jason.sayHi()
Jason.howMany()
Sophie=Person("Sophie")
Sophie.sayHi()
Sophie.howMany()
Frank=Employee("Frank")
Frank.sayHi()
Frank.howMany()
(Full version of this one included at end)
Person.population would hardcode the class to specifically only update the
Person population, which is a valid design decision.
self.__class__.population means that you're keeping track of how many of each
kind of thing exists (number of employees for example vs other people). Again,
another equally valid design decision. (Though in the example below this
would imply that Employees aren't people, which isn't an idea I'd like
employers to take on :-)
self.population can mean different things depending on whether you're
reading or writing the value, which is not an ideal scenario :-) (If you
change all occurances of self.__class__.population below to self.population,
you'll see what I mean...)
I suppose the bottom line here (if the rest of this email has gone over your
head) is that your code was fine, BUT you may want to try running it
differently :)
If you really want to see why self.population is a bad idea, take the example
below and use:
class Employee(Person):
pass
Instead of :
class Employee(Person):
population = 0
Which again gives you another behaviour (due to, again, how values for names
are searched for and updated).
Finally, I suspect the best place for this question is actually the
python-tutor list - http://mail.python.org/mailman/listinfo/tutor
Best Regards,
Michael.
-----
#!/usr/bin/python
class Person:
population=0
def __init__(self,name):
self.name=name
print '(Initialising %s)' % self.name
self.__class__.population += 1
def __del__(self):
print "%s says bye." % self.name
self.__class__.population -= 1
if self.__class__.population == 0:
print "I am the last ",self.__class__.__name__
else:
print "There are still %d people left." %
self.__class__.population
def sayHi(self):
'''Greeting by the person.
That's all it does.'''
print "Hi, my name is %s" % self.name
def howMany(self):
if self.__class__.population==1:
print "I am on the only", self.__class__.__name__, " here."
else:
print ("We have %d "+self.__class__.__name__+"s here.") %
self.__class__.population
class Employee(Person):
population = 0
def main():
Jason=Person("Jason")
Jason.sayHi()
Jason.howMany()
Sophie=Person("Sophie")
Sophie.sayHi()
Sophie.howMany()
Frank=Employee("Frank")
Frank.sayHi()
Frank.howMany()
if __name__ == "__main__":
main()