Enums are Singletons - but not always?

Kushal Kumaran kushal at locationd.net
Sat May 23 15:49:21 EDT 2020


"Ralf M." <Ralf_M at t-online.de> 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: 1>     En.A: <En.A: 1>
> 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: 1>     En.A: <En.A: 1>
> 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


More information about the Python-list mailing list