Re: Enums are Singletons - but not always?
On 5/23/2020 2:21 PM, Ralf M. wrote: # Code of mod1.py # import enum, mod2 class En(enum.Enum): A = 1 B = 2 def main(): a = mod2.getA() print("a is En.A:", a is En.A) print("a:", repr(a), " En.A:", repr(En.A)) print("id(a), id(a.__class__)", id(a), id(a.__class__)) print("id(En.A), id(En) ", id(En.A), id(En)) if __name__ == "__main__": main() > # Code of mod2.py # > import mod1 > def getA(): > return mod1.En.A As a couple of people mentioned, the issue is the circular import. When you run mod1 from a command line, its name is __main__. Its execution of mod1 code stops to execute enum code and then mod2 code. Execution of mod2 stops to execute mod1 code in a new module named 'mod1'. When the second execution of mod1 code does the import of enum and mod2, sys.modules['enum'] and sys.modules['mod2'] exist, are found, and used to satify the import. So enum and mod2 code are not executed again. In the 'mod1' module, 'mod2' is linked to the *incomplete* mod2. This 'works' in your code because the reference to mod2 within def main is not used because the name is 'mod1', not '__main__'. But if in mod1, you had written import enum from mod2 import getA the second execution of mod1 would fail because mod2.getA does not exist because execution of mod2 is paused to import mod1 When the mod1 import in mod2 finishes, the execution of mod2 code for 'mod2' finishes without issue. Then the execution of 'mod1' code in '__main__' finishes with the call to main. When you instead interactively import mod1, it is executed just once and you have to explicitly call main. In idlelib, pyshell currently has the IDLE Shell code, the main() function, and a couple of classes used elsewhere. (An atrocious design that I inherited and have only partly fixed.) As a *temporary* fix to an issue due to there being duplicate '__main__' and 'pyshell' modules, I added the following to the top of pyshell. import sys if __name__ == "__main__": sys.modules['idlelib.pyshell']=sys.modules['__main__'] But doing what others suggested, limiting the main or start module to one-time code, is better. -- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
Richard Damon schreef op 23/05/2020 om 20:57: On 5/23/20 2:21 PM, Ralf M. wrote: Hello, recently I wrote a small library that uses an Enum. That worked as expected. Then I added a main() and if __name__ == "__main__" to make it runable as script. Now Enum members that should be the same aren't identical any more, there seem to be two instances of the same Enum. I think I know what's going on, but cannot find a good and elegant way to avoid the problem. I hope someone here can help me there. I don;'t think Python anywhere defines that a enum will be a singleton, and you should be checking for equality (==) not identity (is) So much this. Always check for equality instead of identity, unless you know what you're doing and you have a very good reason to use identity. Everywhere I see questions all the time from people new to Python, confused about stuff that happens when comparing using identity. I would like to know where that comes from ... are there tutorials that encourage using identity checks? -- "Honest criticism is hard to take, particularly from a relative, a friend, an acquaintance, or a stranger." -- Franklin P. Jones Roel Schroeven -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
On 05/23/2020 11:57 AM, Richard Damon wrote: I don't think Python anywhere defines that a enum will be a singleton, and you should be checking for equality (==) not identity (is) If you're not sure, please do a little research first. We have enough bad information on the 'nets already. According to Kushal's link: The most interesting thing about Enum members is that they are singletons. The only time using `==` is necessary is for mixed-Enums, such as IntEnum, and you need to compare what could be an IntEnum member in code that could be using actual `int`s. -- ~Ethan~ -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
On Sun, May 24, 2020 at 5:58 AM Kushal Kumaran wrote: > > "Ralf M." writes: > > > Below are a simplified code sample, the results when I run it and my > > thoughts. > > > > # Code of mod1.py # > > import enum, mod2 > > def main(): > > a = mod2.getA() > > # End of mod1.py # > > > > # Code of mod2.py # > > import mod1 > > def getA(): > > return mod1.En.A > > # End of mod2.py # > > > I think of circular dependencies as a code smell. You can move the enum > definition into a separate module, and have both mod1 and mod2 import > that, or like you say, by moving the main function into its own module. > Maybe you can explain why you don't want this. Correct - and especially since you're using the main module also as a module. Currently, that is something you just plain shouldn't do. You end up with one copy of the module called __main__ and another one called mod1, and they're completely independent. Just don't do that. However, there HAVE been some proposals to make this sort of thing work. In general, they'd end up doing something like having the module inserted into sys.modules twice, once as __main__ and once as mod1, so you end up getting the same module in both cases. That would work, since (with only one module) there'd only be one En class. I don't think any such proposal has gotten fully serious yet, but if this is something you're interested in (or feel strongly about), dig through the python-ideas archives to see some of the arguments for and against. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
"Ralf M." writes: > Hello, > > recently I wrote a small library that uses an Enum. That worked as > expected. Then I added a main() and if __name__ == "__main__" to make > it runable as script. Now Enum members that should be the same aren't > identical any more, there seem to be two instances of the same Enum. > > I think I know what's going on, but cannot find a good and elegant way > to avoid the problem. I hope someone here can help me there. > > Below are a simplified code sample, the results when I run it and my > thoughts. > > # Code of mod1.py # > import enum, mod2 > class En(enum.Enum): > A = 1 > B = 2 > def main(): > a = mod2.getA() > print("a is En.A:", a is En.A) > print("a:", repr(a), "En.A:", repr(En.A)) > print("id(a), id(a.__class__)", id(a), id(a.__class__)) > print("id(En.A), id(En) ", id(En.A), id(En)) > if __name__ == "__main__": > main() > # End of mod1.py # > > # Code of mod2.py # > import mod1 > def getA(): > return mod1.En.A > # End of mod2.py # > > # Results when run: # > C:\tmp>py mod1.py > a is En.A: False > a: En.A: > id(a), id(a.__class__) 33305864 7182808 > id(En.A), id(En) 33180552 7183752 > > C:\tmp>py > Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC > v.1916 64 bit > (AMD64)] on win32 > Type "help", "copyright", "credits" or "license" for more information. import mod1 mod1.main() > a is En.A: True > a: En.A: > id(a), id(a.__class__) 49566792 44574280 > id(En.A), id(En) 49566792 44574280 > > So: When run as script there are two instances of En (different ids), > but when mod1 is imported and mod1.main() is run it works as expected > (just one instance of En, same ids). > BTW: py -m mod1 doesn't work either. > > What I thing is happening: > When the script is run, mod1.py is executed and an instance of En and > its members is created. During the same run mod1 is also imported (via > mod2), the file mod1.py is executed again as part of the import and > another, different instance of En and its members is created. > > How do I have to change mod1.py to avoid the problem? > Currently I have moved main() into a new file script.py. That works, > but is not what I wanted. > I think of circular dependencies as a code smell. You can move the enum definition into a separate module, and have both mod1 and mod2 import that, or like you say, by moving the main function into its own module. Maybe you can explain why you don't want this. The note in the documentation about enum members being singletons only applies to the members. It does not apply to the enum class itself. In your case, __main__.En is not the same as mod1.En. The members being singleton means that, for the same enum class En, En.A will refer to the same object as En(1), or, perhaps an unpickled object that has the same value. > I doubt it's a bug in the enum module, but should that be the case, > I'm willing to open an issue on the bug tracker. > > Or can nothing be done about it? > It is not specific to the Enum class. You'll see the same behaviour with any class that gets imported under two different module names. > Looking forward to any ideas > Ralf M. > > P.S.: > As I was about to send this post the following modification occured to > me (see below). It works, but it doesn't feel right to import a module > directly from inside itself. > # Modified code of mod1.py (one line added) # > import enum, mod2 > class En(enum.Enum): > A = 1 > B = 2 > from mod1 import En # NEW LINE, overwrite just defined En > def main(): > a = mod2.getA() > print("a is En.A:", a is En.A) > print("a:", repr(a), "En.A:", repr(En.A)) > print("id(a), id(a.__class__)", id(a), id(a.__class__)) > print("id(En.A), id(En) ", id(En.A), id(En)) > if __name__ == "__main__": > main() I agree this is unpleasant to read. -- regards, kushal -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
Peter Otten wrote: >> # Code of mod2.py # > import __main__ as mod1 >> def getA(): >>return mod1.En.A >> # End of mod2.py # > > but that would hardcode the assumption that __main__ is always mod1.py. I should have mentioned the cyclic dependency -- if two modules import each other one may not be completely set up when the other tries to access it. -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
Richard Damon writes: > On 5/23/20 2:21 PM, Ralf M. wrote: >> Hello, >> >> recently I wrote a small library that uses an Enum. That worked as >> expected. Then I added a main() and if __name__ == "__main__" to make >> it runable as script. Now Enum members that should be the same aren't >> identical any more, there seem to be two instances of the same Enum. >> >> I think I know what's going on, but cannot find a good and elegant way >> to avoid the problem. I hope someone here can help me there. > > I don;'t think Python anywhere defines that a enum will be a singleton, > and you should be checking for equality (==) not identity (is) > https://docs.python.org/3/library/enum.html#enum-members-aka-instances -- regards, kushal -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
Ralf M. wrote: > Hello, > > recently I wrote a small library that uses an Enum. That worked as > expected. Then I added a main() and if __name__ == "__main__" to make it > runable as script. Now Enum members that should be the same aren't > identical any more, there seem to be two instances of the same Enum. > > I think I know what's going on, but cannot find a good and elegant way > to avoid the problem. I hope someone here can help me there. > > Below are a simplified code sample, the results when I run it and my > thoughts. > > # Code of mod1.py # > import enum, mod2 > class En(enum.Enum): > A = 1 > B = 2 > def main(): > a = mod2.getA() > print("a is En.A:", a is En.A) > print("a:", repr(a), "En.A:", repr(En.A)) > print("id(a), id(a.__class__)", id(a), id(a.__class__)) > print("id(En.A), id(En) ", id(En.A), id(En)) > if __name__ == "__main__": > main() > # End of mod1.py # > > # Code of mod2.py # > import mod1 > def getA(): > return mod1.En.A > # End of mod2.py # > > # Results when run: # > C:\tmp>py mod1.py > a is En.A: False > a: En.A: > id(a), id(a.__class__) 33305864 7182808 > id(En.A), id(En) 33180552 7183752 > > C:\tmp>py > Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 > 64 bit > (AMD64)] on win32 > Type "help", "copyright", "credits" or "license" for more information. > >>> import mod1 > >>> mod1.main() > a is En.A: True > a: En.A: > id(a), id(a.__class__) 49566792 44574280 > id(En.A), id(En) 49566792 44574280 > >>> > > So: When run as script there are two instances of En (different ids), > but when mod1 is imported and mod1.main() is run it works as expected > (just one instance of En, same ids). > BTW: py -m mod1 doesn't work either. > > What I thing is happening: > When the script is run, mod1.py is executed and an instance of En and > its members is created. During the same run mod1 is also imported (via > mod2), the file mod1.py is executed again as part of the import and > another, different instance of En and its members is created. > > How do I have to change mod1.py to avoid the problem? > Currently I have moved main() into a new file script.py. That works, but > is not what I wanted. But that is exactly the right approach. Avoid importing the main script under its name because that will run all code that is not guarded by if __name__ == "__main__": ... twice. In your case it's enum, but the same goes for every class, function or object and the module itself. In Python they are all created rather than declared. > I doubt it's a bug in the enum module, but should that be the case, I'm > willing to open an issue on the bug tracker. > > Or can nothing be done about it? mod2 could import the __main__ module, e. g. > # Code of mod2.py # import __main__ as mod1 > def getA(): > return mod1.En.A > # End of mod2.py # but that would hardcode the assumption that __main__ is always mod1.py. The only sane option is to not put anything in the main script that needs to be imported by other modules. > Looking forward to any ideas > Ralf M. > > P.S.: > As I was about to send this post the following modification occured to > me (see below). It works, but it doesn't feel right to import a module > directly from inside itself. > # Modified code of mod1.py (one line added) # > import enum, mod2 > class En(enum.Enum): > A = 1 > B = 2 > from mod1 import En # NEW LINE, overwrite just defined En > def main(): > a = mod2.getA() > print("a is En.A:", a is En.A) > print("a:", repr(a), "En.A:", repr(En.A)) > print("id(a), id(a.__class__)", id(a), id(a.__class__)) > print("id(En.A), id(En) ", id(En.A), id(En)) > if __name__ == "__main__": > main() -- https://mail.python.org/mailman/listinfo/python-list
Re: Enums are Singletons - but not always?
On 5/23/20 2:21 PM, Ralf M. wrote: > Hello, > > recently I wrote a small library that uses an Enum. That worked as > expected. Then I added a main() and if __name__ == "__main__" to make > it runable as script. Now Enum members that should be the same aren't > identical any more, there seem to be two instances of the same Enum. > > I think I know what's going on, but cannot find a good and elegant way > to avoid the problem. I hope someone here can help me there. I don;'t think Python anywhere defines that a enum will be a singleton, and you should be checking for equality (==) not identity (is) -- Richard Damon -- https://mail.python.org/mailman/listinfo/python-list
Enums are Singletons - but not always?
Hello, recently I wrote a small library that uses an Enum. That worked as expected. Then I added a main() and if __name__ == "__main__" to make it runable as script. Now Enum members that should be the same aren't identical any more, there seem to be two instances of the same Enum. I think I know what's going on, but cannot find a good and elegant way to avoid the problem. I hope someone here can help me there. Below are a simplified code sample, the results when I run it and my thoughts. # Code of mod1.py # import enum, mod2 class En(enum.Enum): A = 1 B = 2 def main(): a = mod2.getA() print("a is En.A:", a is En.A) print("a:", repr(a), "En.A:", repr(En.A)) print("id(a), id(a.__class__)", id(a), id(a.__class__)) print("id(En.A), id(En) ", id(En.A), id(En)) if __name__ == "__main__": main() # End of mod1.py # # Code of mod2.py # import mod1 def getA(): return mod1.En.A # End of mod2.py # # Results when run: # C:\tmp>py mod1.py a is En.A: False a: En.A: id(a), id(a.__class__) 33305864 7182808 id(En.A), id(En) 33180552 7183752 C:\tmp>py Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import mod1 >>> mod1.main() a is En.A: True a: En.A: id(a), id(a.__class__) 49566792 44574280 id(En.A), id(En) 49566792 44574280 >>> So: When run as script there are two instances of En (different ids), but when mod1 is imported and mod1.main() is run it works as expected (just one instance of En, same ids). BTW: py -m mod1 doesn't work either. What I thing is happening: When the script is run, mod1.py is executed and an instance of En and its members is created. During the same run mod1 is also imported (via mod2), the file mod1.py is executed again as part of the import and another, different instance of En and its members is created. How do I have to change mod1.py to avoid the problem? Currently I have moved main() into a new file script.py. That works, but is not what I wanted. I doubt it's a bug in the enum module, but should that be the case, I'm willing to open an issue on the bug tracker. Or can nothing be done about it? Looking forward to any ideas Ralf M. P.S.: As I was about to send this post the following modification occured to me (see below). It works, but it doesn't feel right to import a module directly from inside itself. # Modified code of mod1.py (one line added) # import enum, mod2 class En(enum.Enum): A = 1 B = 2 from mod1 import En # NEW LINE, overwrite just defined En def main(): a = mod2.getA() print("a is En.A:", a is En.A) print("a:", repr(a), "En.A:", repr(En.A)) print("id(a), id(a.__class__)", id(a), id(a.__class__)) print("id(En.A), id(En) ", id(En.A), id(En)) if __name__ == "__main__": main() -- https://mail.python.org/mailman/listinfo/python-list