2

I was trying to come up with a use case for the new @enum.nonmember decorator in Python 3.11. The docs clearly mention it is a decorator meant to be applied to members. However, when I tried literally decorating a member directly:

import enum


class MyClass(enum.Enum):
    A = 1
    B = 2

    @enum.nonmember
    C = 3

this results in an error as:

Traceback (most recent call last):
  File "C:\Program Files\Python311\Lib\code.py", line 63, in runsource
    code = self.compile(source, filename, symbol)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\codeop.py", line 153, in __call__
    return _maybe_compile(self.compiler, source, filename, symbol)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\codeop.py", line 73, in _maybe_compile
    return compiler(source, filename, symbol)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\codeop.py", line 118, in __call__
    codeob = compile(source, filename, symbol, self.flags, True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<input>", line 9
    C = 3
    ^
SyntaxError: invalid syntax

However, if I had declared an atribute as a property or a descriptor it also wouldn't become an Enum member... So how, when and why do you use @enum.nonmember?

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
bad_coder
  • 11,289
  • 20
  • 44
  • 72
  • 2
    https://github.com/python/mypy/issues/12841. But you're right, the documentation on how or why is a bit lacking. – Frank Yellin May 11 '23 at 20:27
  • @FrankYellin thanks, I had seen that post by AlexWaygood and it does give a good example of calling the decorator to set the value as a non-member (I hadn't thought of that). I'm guessing the main use of `@enum.nonmember` is providing a conciser syntax for declaring a @property?! – bad_coder May 11 '23 at 20:52
  • 1
    There is an example in the [test suite](https://github.com/python/cpython/blob/7d7dd4cd70ed997ed7c3cda867c4e7b1ab02b205/Lib/test/test_enum.py#L1116-L1134). – wim May 11 '23 at 21:37

1 Answers1

4

You would use it like so:

import enum


class MyClass(enum.Enum):
    A = 1
    B = 2

    C = enum.nonmember(3)

As far as I can tell, the only reason why it is called a decorator, is because of nested classes.

Currently,

class MyClass(enum.Enum):
    A = 1
    B = 2

    class MyNestedClass:
        pass

makes MyClass.MyNestedClass into one of the members of MyClass. This will change in 3.13. So if you want the new behaviour now, you can use:

class MyClass(enum.Enum):
    A = 1
    B = 2

    @enum.nonmember
    class MyNestedClass:
        pass

In 3.13, if you want the current behaviour of making nested classes members, you can use

class MyClass(enum.Enum):
    A = 1
    B = 2

    @enum.member
    class MyNestedClass:
        pass

There is no reason to use enum.nonmember as a decorator on a method, since methods are already excluded from being members, but I think you could use enum.member on one to be able to define a method member if you wanted. Not sure why you would, though.

Jasmijn
  • 9,370
  • 2
  • 29
  • 43
  • 3
    Yes, it's for nested classes. The relevant change is: [_gh-78157: nested classes will not be members in 3.13_](https://github.com/python/cpython/pull/92366) – wim May 11 '23 at 21:39
  • Great answer! I'm going to hold of on accepting the answer for a few days to see if someone else contributes additional insights. As I said in my comment under the question I'm also intrigued how `@enum.nonmember` compares to declaring a regular `@property` and if someone wants to do a comparison I think it would spare future readers having to wonder about it. – bad_coder May 11 '23 at 22:01