How to apply @enum.nonmember?
Asked Answered
D

1

7

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?

Doenitz answered 11/5, 2023 at 20:20 Comment(3)
github.com/python/mypy/issues/12841. But you're right, the documentation on how or why is a bit lacking.Airtight
@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?!Doenitz
There is an example in the test suite.Stylobate
H
9

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.

Hyla answered 11/5, 2023 at 21:7 Comment(3)
Yes, it's for nested classes. The relevant change is: gh-78157: nested classes will not be members in 3.13Stylobate
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.Doenitz
Currently if you use nonmember on the nested class as above, then x = MyClass.MyNestedClass(), they type checker will flag that with: Object of type "nonmember[type[MyNestedClass]]" is not callableKnut

© 2022 - 2024 — McMap. All rights reserved.