Does python hate cathy?

Q

QS

Hi to all!
I am new to python, and I encountered a weird problem.

Here is my code

##########>8####################
#!/usr/bin/python
# Filename: objvar.py
class Person:
'''Represents a person.'''

population = 0
#sex = 'F'
#age = 22
# It is vague here: is this variable going to be a class, or
object, variable

def __init__(self, name, sex):
'''Initializes the person's data.'''
self.name = name
self.sex = sex
print '(Initializing %s )' % self.name
# When this person is created, he/she
# adds to the population
Person.population += 1

def __del__(self):
'''I am dying.'''

print '%s says bye.' % self.name
Person.population -= 1
if Person.population == 0:
print 'I am the last one.'
else:
print 'There are still %d people left.' %
Person.population

def sayHi(self):
'''Greeting by the person.

Really, that's all it does.'''

self.age = 25
print 'Hi, my name is %s, and I am %s, and I am age %d ' %
(self.name, self.sex, self.age)

def howMany(self):
'''Prints the current population.'''
if Person.population == 1:
print 'I am the only person here.'
else:
print 'We have %d persons here.' % Person.population

swaroop = Person('Swaroop', 'M')
swaroop.sayHi()
swaroop.howMany()
kalam = Person('Abdul Kalam', 'M')
kalam.sayHi()
kalam.howMany()
cathy = Person('Catherine', 'F')
cathy.sayHi()
cathy.howMany()
swaroop.sayHi()
swaroop.howMany()


############# 8< #########################


When I run this script, I got the following exception:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute 'population'" in <bound method Person.__del__ of
<__main__.Person instance at 0xb7d8ac6c>> ignored

To to newcomer like me, this message doesn't make much sense. What
seems weird to me is that, if I change the variable cathy to something
else, like cath, or even cat, then the script will finish gracefully.
Why "cathy" is not liked?!!

Some of you may have recognized that the code is derived from a sample
code in Swaroop's "A byte of python".

My python is of version 2.5.1, on Ubuntu.
 
G

George Sakkis

Hi to all!
I am new to python, and I encountered a weird problem.

Here is my code

##########>8####################
#!/usr/bin/python
# Filename: objvar.py
class Person:
'''Represents a person.'''

population = 0
#sex = 'F'
#age = 22
# It is vague here: is this variable going to be a class, or
object, variable

def __init__(self, name, sex):
'''Initializes the person's data.'''
self.name = name
self.sex = sex
print '(Initializing %s )' % self.name
# When this person is created, he/she
# adds to the population
Person.population += 1

def __del__(self):
'''I am dying.'''

print '%s says bye.' % self.name
Person.population -= 1
if Person.population == 0:
print 'I am the last one.'
else:
print 'There are still %d people left.' %
Person.population

def sayHi(self):
'''Greeting by the person.

Really, that's all it does.'''

self.age = 25
print 'Hi, my name is %s, and I am %s, and I am age %d ' %
(self.name, self.sex, self.age)

def howMany(self):
'''Prints the current population.'''
if Person.population == 1:
print 'I am the only person here.'
else:
print 'We have %d persons here.' % Person.population

swaroop = Person('Swaroop', 'M')
swaroop.sayHi()
swaroop.howMany()
kalam = Person('Abdul Kalam', 'M')
kalam.sayHi()
kalam.howMany()
cathy = Person('Catherine', 'F')
cathy.sayHi()
cathy.howMany()
swaroop.sayHi()
swaroop.howMany()

############# 8< #########################

When I run this script, I got the following exception:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute 'population'" in <bound method Person.__del__ of
<__main__.Person instance at 0xb7d8ac6c>> ignored

To to newcomer like me, this message doesn't make much sense. What
seems weird to me is that, if I change the variable cathy to something
else, like cath, or even cat, then the script will finish gracefully.
Why "cathy" is not liked?!!

Some of you may have recognized that the code is derived from a sample
code in Swaroop's "A byte of python".

My python is of version 2.5.1, on Ubuntu.

That's really weird... it's reproducible on Windows too. It doesn't
make any sense why the name of the variable would make a difference.
My guess is you hit some kind of obscure bug.

George
 
J

John Machin

That's really weird... it's reproducible on Windows too. It doesn't
make any sense why the name of the variable would make a difference.
My guess is you hit some kind of obscure bug.

My guess after reading the manual:
"""
Warning: Due to the precarious circumstances under which __del__()
methods are invoked, exceptions that occur during their execution are
ignored, and a warning is printed to sys.stderr instead. Also, when
__del__() is invoked in response to a module being deleted (e.g., when
execution of the program is done), other globals referenced by the
__del__() method may already have been deleted. For this reason,
__del__() methods should do the absolute minimum needed to maintain
external invariants.
"""
is that farnarkeling about in a __del__ method is *not* a good idea.
 
C

Carsten Haese

That's really weird... it's reproducible on Windows too. It doesn't
make any sense why the name of the variable would make a difference.
My guess is you hit some kind of obscure bug.

This is not a bug, just an unexpected feature:
http://mail.python.org/pipermail/python-list/2005-January/304873.html

What's happening is that at the end of the script, all objects in the
global namespace are set to None (in order to decrease their reference
count and trigger garbage collection). This happens in the order in
which the names appear as keys in the globals dictionary. It randomly
happens that "swaroop", "kalam", and "cath" all are hashed in front of
"Person", but "cathy" is hashed after "Person".

Hence, if Catherine is named cath, Python "None's" all the instances
first and then the type last, and all is well. However, if Catherine is
called cathy, Person is set to None before cathy. Then, in the lookup of
the global name "Person" during cathy.__del__, Person is None, which
doesn't have a "population" attribute, causing the AttributeError.

Possible workarounds are:
1) Explicitly delete the global names for the instances before the
script runs out.
2) Don't refer to the "Person" type by its global name in __del__, but
indirectly as type(self). (This requires Person to be a new-style class,
though.)
 
T

Tim Chase

When I run this script, I got the following exception:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute 'population'" in <bound method Person.__del__ of
<__main__.Person instance at 0xb7d8ac6c>> ignored

To to newcomer like me, this message doesn't make much sense. What
seems weird to me is that, if I change the variable cathy to something
else, like cath, or even cat, then the script will finish gracefully.
Why "cathy" is not liked?!!

My python is of version 2.5.1, on Ubuntu.

When I first read this, I thought you were crazy. I ran the
code, and you're not. I tried under versions 2.3, 2.4, and 2.5
on my Debian box, and got the same results. Neither "cathy" nor
"ca" works, but "c", "cat", "cath", "cath_y" and "cathy_" all
work just fine.

Even more infuriating is that if I try and use pdb to debug,
Swaroop drops into pdb, but Abdul and Cathy both error out.

That's just plain nuts. Something odd is happening with __del__
calls.

-tkc
 
Q

QS

Thanks to you all! It's good to know...

This is not a bug, just an unexpected feature:http://mail.python.org/pipermail/python-list/2005-January/304873.html

What's happening is that at the end of the script, all objects in the
global namespace are set to None (in order to decrease their reference
count and trigger garbage collection). This happens in the order in
which the names appear as keys in the globals dictionary. It randomly
happens that "swaroop", "kalam", and "cath" all are hashed in front of
"Person", but "cathy" is hashed after "Person".

Hence, if Catherine is named cath, Python "None's" all the instances
first and then the type last, and all is well. However, if Catherine is
called cathy, Person is set to None before cathy. Then, in the lookup of
the global name "Person" during cathy.__del__, Person is None, which
doesn't have a "population" attribute, causing the AttributeError.

Possible workarounds are:
1) Explicitly delete the global names for the instances before the
script runs out.
2) Don't refer to the "Person" type by its global name in __del__, but
indirectly as type(self). (This requires Person to be a new-style class,
though.)
 
C

Carsten Haese

is that farnarkeling about in a __del__ method is *not* a good
idea.

Ok that having been said, is accessing an unbound variable of a class
and using it to coordinate between instances of that class common
practice?

I'm not sure what you mean by "accessing an unbound variable" means in
the context of this thread, or any context for that matter. Nothing of
the kind is happening in the OP's example. What's happening is that a
class attribute is initialized, incremented, and decremented. Using
class attributes is common practice.

The problem, as I explained in my previous post, is that the class
attribute is looked up via a global name of the class, and that name may
or may not have disappeared in a puff of logic by the time the
instance's __del__ method is run in the VM shutdown sequence.
 
C

Carsten Haese

I'm not sure what you mean by "accessing an unbound variable" means in
the context of this thread

I'm starting to sound like castironpi. Time to go to sleep.
 
T

Terry Reedy

From the title, I assumed this was spam like others with similar titles --
and that the perl newsgroup, for instance, would have 'Does perl hate
cathy?

| I am new to python, and I encountered a weird problem.

Observation: as you will learn, the form of the error message was different
from the standard tracebacks one gets during program execution. This was a
clue that it was a cleanup message and actually did make sense.

Summary lessons.

1. What a Python interpreter does after it executes the last statement is
undefined by the language spec. It could do absolutely nothing (and I wish
some programs that wastefully spend minutes 'cleaning up' did just that!).
CPython tries to do some cleanup when requested but the results are
sometimes seemingly arbitrary.

2. If you use __del__, do so for a reason (and keeping a population count
is one, though rare*), and explicitly delete the objects for which you want
dependable behavior.

*A population count is a good example of a class attribute. But it seems
to be rare in practice because if one wants that, it seems that a
population collection (with a len() method) is usually also wanted --
perhaps so one can iterate thru the population.

3. Experimenting with Python is a good way to learn. Keep it up!

tjr
 
E

Edward A. Falk

Interestingly, if you change

swaroop = Person('Swaroop', 'M')
swaroop.sayHi()
swaroop.howMany()
kalam = Person('Abdul Kalam', 'M')
kalam.sayHi()
kalam.howMany()
cathy = Person('Catherine', 'F')
cathy.sayHi()
cathy.howMany()
swaroop.sayHi()
swaroop.howMany()

to

def main():
swaroop = Person('Swaroop', 'M')
swaroop.sayHi()
swaroop.howMany()
kalam = Person('Abdul Kalam', 'M')
kalam.sayHi()
kalam.howMany()
cathy = Person('Catherine', 'F')
cathy.sayHi()
cathy.howMany()
swaroop.sayHi()
swaroop.howMany()
return 0


if __name__ == "__main__":
sys.exit(main())


The problem goes away. (This is a good coding practice in any event.)

As others have pointed out, the order in which local variables are deleted
is undefined. It looks to me as if the class Person got deleted before
Catherine did.

Adding

del kalam
del cathy
del swaroop

to the end of the program did fix the problem, presumably by forcing
kalam, cathy, and swaroop from being deleted before Person.

However, Adding

self.myclass = Person

to the __init__() method didn't stop the problem. I thought it might, because
then each of the objects would have held a reference to Person. Actually, I would
have thought they'd hold a reference by merely existing, so is this not a bug?
 
G

Gabriel Genellina

However, Adding

self.myclass = Person

to the __init__() method didn't stop the problem. I thought it might,
because
then each of the objects would have held a reference to Person.
Actually, I would
have thought they'd hold a reference by merely existing, so is this not
a bug?

Yes, they already have a reference (self.__class__). The problem is that
__del__ references Person by *name*, and that name is reset to None (like
all other module globals) as part of the interpreter shutdown sequence.
Using self.__class__ would avoid this particular bug, but in general, it's
better not to rely on __del__ at all.

See http://bugs.python.org/issue1513802 and
http://bugs.python.org/issue1717900
 
P

Patrick Mullen

It seems like this is mostly a non-issue. The original code actually
works correctly (of course the updated versions that solve the
exception problem are probably better). The only thing that is going
haywire is the interpreter shutdown process. I think it is going a
bit overboard to "consider __del__ harmful" because it might throw an
exception as the program is quitting. It is important to not rely on
__del__ too much but there can be some designs where you need to know
when something is gone, and you don't know exactly where or when
something is deleted.

Then again, I can count the number of times I have ever needed __del__
with no fingers (never used it!). Still, quite interesting to
explore.
 
E

Edward A. Falk

Patrick Mullen said:
Then again, I can count the number of times I have ever needed __del__
with no fingers (never used it!). Still, quite interesting to
explore.

I used it once, for an object that had a doubly-linked list.
The __del__() method walked the list, setting all the elements'
prev/next pointers to None to make sure the elements of the list would
get garbage-collected.
 
M

Marc 'BlackJack' Rintsch

I used it once, for an object that had a doubly-linked list.
The __del__() method walked the list, setting all the elements'
prev/next pointers to None to make sure the elements of the list would
get garbage-collected.

Without the `__del__()` the elements would get garbage collected just
fine. If you don't want to wait until the garbage collector in CPython
detects the cycle, you can use `weakref`\s for one of the two "pointers"
in each element.

Ciao,
Marc 'BlackJack' Rintsch
 
H

Hrvoje Niksic

Marc 'BlackJack' Rintsch said:
Without the `__del__()` the elements would get garbage collected just
fine.

It gets even worse than that: the __del__ method actually *prevents*
objects that participate in cycles from getting garbage-collected.
Python's GC doesn't deallocate objects that define __del__. (The way
to handle those is to grep for them in gc.garbage, break the cycles in
a way that works for the application -- e.g. by removing the prev and
next references -- and finally del gc.garbage[:].)
 
C

castironpi

When I run this script, I got the following exception:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute 'population'" in <bound method Person.__del__ of
<__main__.Person instance at 0xb7d8ac6c>> ignored
To to newcomer like me, this message doesn't make much sense. What
seems weird to me is that, if I change the variable cathy to something
else, like cath, or even cat, then the script will finish gracefully.
Why "cathy" is not liked?!!
My python is of version 2.5.1, on Ubuntu.

When I first read this, I thought you were crazy.  I ran the
code, and you're not.  I tried under versions 2.3, 2.4, and 2.5
on my Debian box, and got the same results.  Neither "cathy" nor
"ca" works,  but "c", "cat", "cath", "cath_y" and "cathy_" all
work just fine.

Even more infuriating is that if I try and use pdb to debug,
Swaroop drops into pdb, but Abdul and Cathy both error out.
[snip]
Does Python hate Cathy?

Does Pothon?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top