Question: I have an enum for which some of the members are deprecated: from enum import Enum class Foo(Enum): BAR = "bar" BAZ = "baz" # deprecated  How do it get the following behavior:  When somebody writes Foo.BAR, everything behaves normally When somebody writes Foo.BAZ, a DeprecationWarning is issued using warnings.warn("BAZ is deprecated", DeprecationWarning). Afterwards everything behaves normally. The same behavior should apply when members are accessed in other ways, e.g. Foo("baz") and Foo["BAZ"] should raise a DeprecationWarning.  Things I have tried, but failed:  Overwrite _missing_ and don't define BAZ. Does not work, because in the end I still need to return an existing member for a while (until our DB is cleaned of the deprecated value). But I can not dynamically add members to an enum. If I define it, _missing_ is not called. overwrite any of __getattr__, __getattribute__. These are called when accessing attributes of a member, e.g. Foo.BAZ.boo, not when accessing Foo.BAZ. I guess this could work if I could overwrite __getattr__ of EnumMeta and then make Enum use the child meta class. However, I don't see how that can be done either overwrite __class_getitem__: Reserved for static typing and not called anyways. Abuse _generate_next_value_. This function is only called on class creation, so I can get a deprecation warning when the class is called once, regardless of whether the deprecated member is called or not. But that is not what I want. Look at this question. It does not solve my problem, as the goal there is filtering of deprecated members during iteration.  TLDR: How can I detect and invoke a function when an enum member is accessed? I am working with python 3.8, so new features are fine.

Answer: This appears to be one of those times when subclassing EnumMeta is the right thing to do. The new metaclass will run an _on_access method, if it exists, whenever a member is accessed: class OnAccess(EnumMeta): """ runs a user-specified function whenever member is accessed """ # def __getattribute__(cls, name): obj = super().__getattribute__(name) if isinstance(obj, Enum) and obj._on_access: obj._on_access() return obj # def __getitem__(cls, name): member = super().__getitem__(name) if member._on_access: member._on_access() return member # def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): obj = super().__call__(value, names, module=module, qualname=qualname, type=type, start=start) if isinstance(obj, Enum) and obj._on_access: obj._on_access() return obj  The new base Enum treats any extra arguments on member creation as arguments for a deprecate function, and sets the _on_access attribute to that function only if extra arguments are given: class DeprecatedEnum(Enum, metaclass=OnAccess): # def __new__(cls, value, *args): member = object.__new__(cls) member._value_ = value member._args = args member._on_access = member.deprecate if args else None return member # def deprecate(self): args = (self.name, ) + self._args import warnings warnings.warn( "member %r is deprecated; %s" % args, DeprecationWarning, stacklevel=3, )  And our example Enum with deprecated members: class Foo(DeprecatedEnum): BAR = "bar" BAZ = "baz", "use something else"  And the warnings (from a test script): # no warning here list(Foo) # nor for non-deprecated members Foo.BAR # but direct use of deprecated members does generate warnings Foo.BAZ /home/ethan/test:74: DeprecationWarning: member 'BAZ' is deprecated; use something else Foo.BAZ Foo('baz') /home/ethan/test:75: DeprecationWarning: member 'BAZ' is deprecated; use something else Foo('baz') Foo['BAZ'] /home/ethan/test:76: DeprecationWarning: member 'BAZ' is deprecated; use something else Foo['BAZ']  And all the deprecated members in Foo: >>> print([m.name for m in Foo if m._args]) ['BAZ']   Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.