exec, import and isinstance

Peter Otten __peter__ at web.de
Tue Feb 8 04:36:54 EST 2011


Michele Petrazzo wrote:

> Hi all,
> I'm playing with python internals and I'm discovering a strange behavior
> of isinstance. Considering the following test case:
> 
> test/bar.py
> test/b.py
> test/a/__init__.py
> test/a/foo.py
> 
> -- __init__.py -> empty
> 
> --- foo.py:
> class foo: pass
> c = foo
> 
> --- b.py
> def ret():
>    d = {}
>    #s = "import sys;sys.path.append('a');import foo" ## this cause the
> problem
>    s = "import sys;from a import foo"
>    exec s in d
>    return d["foo"].c
> 
> --- bar.py
> import a.foo
> import b
> 
> c1 = a.foo.foo
> c2 = b.ret()
> 
> print c1, c2
> print isinstance(c2(), c1)
> ---
> 
> Executing bar.py, I receive:
> a.foo.foo a.foo.foo
> True
> 
> But, if I replace the indicated line, python doesn't recognize anymore
> the instance and I receive:
> a.foo.foo foo.foo
> False
> 
> Why? I don't understand how python see the instance. Or
> better, does it see only the path? The classes (and the instances) are
> the same!
> 
> Thanks for all that help me to turn the light on...
> 
> Michele

You are importing the same module twice, once as foo and once as a.foo. 

To avoid importing a module that was already imported Python looks into the 
module cache sys.modules. If the module is already there it uses the cached 
version. 
In your case the cache contains the module, but under the key "a.foo" which 
Python cannot magically recognize as being the same as just "foo". Therefore 
the code in the module is executed twice and you get two distinct versions 
of every object in it.

Here is a simplified demo:

$ mkdir package
$ touch package/__init__.py
$ echo 'print "importing", __name__' > package/module.py
$ python
Python 2.6.4 (r264:75706, Dec  7 2009, 18:43:55)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path.append("package")
>>> from package import module
importing package.module
>>> import module
importing module

Conclusion: never allow a path in sys.path that leads into a package.
Also, never import the main script; again you'll get two versions of the 
contents, this time filed in the cache under the script's filename and under 
"__main__".

Peter



More information about the Python-list mailing list