Odd behavior of object equality/identity in the context of relative vs fully qualified imports

Peter Otten __peter__ at web.de
Thu Dec 15 13:23:37 EST 2011


Nathan Rice wrote:

> I just ran into this yesterday, and I am curious if there is a
> rational behind it...
> 
> I have a class that uses a dictionary to dispatch from other classes
> (k) to functions for those classes (v).  I recently ran into a bug
> where the dictionary would report that a class which was clearly in
> the dictionary's keys was giving a KeyError.  id() produced two
> distinct values, which I found to be curious, and
> issubclass/isinstance tests also failed.  When I inspected the two
> classes, I found that the only difference between the two was the
> __module__ variable, which in one case had a name relative to the
> current module (foo), and in another case had the fully qualified name
> (bar.foo).  When I went ahead and changed the import statement for the
> module to import bar.foo rather than import foo, everything worked as
> expected.  My first thought was that I had another foo module in an
> old version of the bar package somewhere on my pythonpath;  After a
> thorough search this proved not to be the case.
> 
> Has anyone else run into this?  Is this intended behavior?  If so, why?

Not exactly intended, but a logical side effect of how Python identifies its 
modules. The problem is that you have an entry in sys.path that reaches into 
the bar package. When you import foo Python does not check whether 
[path1]/bar/foo.py points to the same file as [path2]/foo.py, it just 
verifies that the name "foo" is not in the sys.modules cache before it 
physically imports the file.

Therefore you get two distinct imports of "foo.py" stored in the cache as 
"foo" and "bar.foo". As Python's classes are not declarations, but objects 
themselves you get distinct classes just as with the following

>>> classes = set()
>>> for i in range(3):
...     class A: pass
...
>>> classes = set()
>>> for i in range(3):
...     class A: pass
...     classes.add(A)
...
>>> classes
set([<class __main__.A at 0x7f4f5a044b30>, <class __main__.A at 
0x7f4f5a044ad0>, <class __main__.A at 0x7f4f5a044a70>])

To avoid the problem just remove the offending [path2] from sys.path and 
always import the foo subpackage with 

import bar.foo # everywhere

or

from . import foo # inside bar

PS: You may run into similar problems when you import the main module of a 
program. It will end up in the module cache as filename_sans_extension and 
"__main__".





More information about the Python-list mailing list