Messing up with classes and their namespace
Dave Angel
davea at ieee.org
Sat Jun 6 06:45:13 EDT 2009
Jean-Michel Pichavant wrote:
> <div class="moz-text-flowed" style="font-family: -moz-fixed">Scott
> David Daniels wrote:
>> Jean-Michel Pichavant wrote:
>>> Hello world,
>>>
>>> I had recently a very nasty bug in my python application. The
>>> context is quite complex, but in the end the problem can be resume
>>> as follow:
>>>
>>> 2 files in the same directory :
>>>
>>> lib.py:
>>> >import foo
>>> >foo.Foo.BOOM='lib'
>>>
>>> foo.py:
>>> >class Foo:
>>> > BOOM = 'Foooo'
>>> >
>>> >if __name__=='__main__':
>>> > import lib # I'm expecting BOOM to be set to 'lib'
>>> > print Foo.BOOM
>>>
>>> I was expecting 'lib' as output, but I got 'Fooo'. I don't really
>>> understand what python mechanism I'm messing with but I have the
>>> feeling I've misunderstood a very basic concept about class,
>>> namespace or whatever import notion.
>>>
>>
>>> I guess there is 2 different objects for the same class Foo. How I
>>> do I make both Foo objects the same object ?
>>
>> OK, here is one solution (from which you may infer the problem):
>>
>> lib.py:
>> import __main__
>> __main__.Foo.BOOM = 'lib'
>>
>> foo.py:
>> class Foo:
>> BOOM = 'Foooo'
>>
>> if __name__ == '__main__':
>> import lib # I'm expecting BOOM to be set to 'lib'
>> print(Foo.BOOM)
>>
>> Here is another solution:
>>
>> lib.py:
>> import foo
>> foo.Foo.BOOM = 'lib'
>>
>> foo.py:
>> class Foo:
>> BOOM = 'Foooo'
>>
>> if __name__ == '__main__':
>> import sys
>> sys.modules['foo'] = sys.modules['__main__']
>> import lib # I'm expecting BOOM to be set to 'lib'
>> print(Foo.BOOM)
>>
>> Here is a demo of what is actually going wrong:
>>
>> foo.py:
>> class Foo:
>> inside = __name__
>>
>> import foo
>>
>> if __name__ == '__main__':
>> print(Foo is foo.Foo)
>> print(Foo.inside, foo.Foo.inside)
>>
>> And here is a fix
>> foo.py:
>> if __name__ == '__main__':
>> import sys
>> sys.modules['foo'] = sys.modules['__main__']
>>
>> class Foo:
>> inside = __name__
>>
>> import foo
>>
>> if __name__ == '__main__':
>> print(Foo is foo.Foo)
>> print(Foo.inside, foo.Foo.inside)
>>
>>
>> --Scott David Daniels
>> Scott.Daniels at Acm.Org
>
> Thanks for the explanation. I'll have to give it a second thought, I'm
> still missing something but I'll figure it out.
>
> Jean-Michel
>
> </div>
>
In general, two or more modules with mutual imports can cause problems.
If modulea.py imports moduleb, and moduleb.py imports modulea, you can
end up with difficulties.
But even more gross difficulties come from importing the module that
started the execution, as it's got another name, "__main__" If you
import it again with its traditional name, these two do not get
combined, and you end up with multiple instances of things you thought
were safe.
Scott has given you a solution for the second problem. But the first
problem is more general, and should perhaps be the one you try first.
The simplest answer in many cases is to factor the script into two
separate files. One will have "library" functions and classes, like the
Foo in your example. And the other will have the logic for parsing
command line arguments and suchlike.
The general problem isn't unique to Python. You can also end up with
problems in C, where one header includes a second one, which re-includes
the first. The workarounds for it are well known, but most of them also
contain subtle difficulties.
If it's possible, factor out mutual dependencies into a separate module.
More information about the Python-list
mailing list