'Python derived enum missing required positional argument

I'm trying to define an object-valued enum, and I'm running in an error of "missing 1 required positional argument" in enum_member.__init__(*args)

A basic example of my code, following the documentation from enum, let's call it problem.py which is imported in upper.py

import enum

class test(object):
   def __init__(self, par1, par2):
      # do stuff with the parameters and assign to object fields

   # more object methods

class testlist(test, enum.Enum):
   A = test(1,2)
   B = test(3,4),
   # a few more of these

When importing the module in which these classes are defined, I get the following error (edited to match the example):

 File "upper.py", line 14, in <module>
    from problem import test
  File "problem.py", line 75, in <module>
    class testlist(test, enum.Enum):
  File "/usr/lib/python3.8/enum.py", line 252, in __new__
    enum_member.__init__(*args)
TypeError: __init__() missing 1 required positional argument: 'par2'

I'm guessing that I'm misunderstanding how derived enums work, but from the documentation I can't figure out how I'm missing an argument.

(edit) The goal is to have testlist values behave as test objects, which also define a few methods and override arithmetic operators, so I can do e.g., testlist.A + testlist.B. The enum isn't strictly necessary, but it's convenient for iteration and groups the values better than having them as global variables in the module.



Solution 1:[1]

note that enum has its own way for __new__ and __init__, in your case, you do not need to have test as your parent class. if you do want, follow the documents https://docs.python.org/3/library/enum.html#when-to-use-new-vs-init

without test class as your parent class, it will work

import enum

class test:
    def __init__(self, par1, par2):
        print(self, par1, par2)

class testlist(enum.Enum):
    A = test(1, 2)
    B = test(3, 4)

EDIT: below code to support + operator check the output to understand more how enum subclassing work.

import enum

class test(object):
    def __init__(self, par1, par2):
        print(self,':', par1, par2)
        self.par1 = par1
        self.par2 = par2

    def __add__(self, rval):
        print("test:", self, "+", rval)

class testlist(test, enum.Enum):
    A = (1,2)
    B = (3,4)
    # a few more of these
    def __new__(cls, par1, par2):
        obj = object.__new__(cls)
        obj._value_ = test(par1, par2)
        return obj
    def __init__(self, *args):
        print(self.__class__, args)


# call __add__ of `testlist` if exist, otherwise __add__ of `test`
testlist.A + testlist.B
# value is `test` object
testlist.A.value + testlist.B.value

output

<__main__.test object at 0x7f112f1d7430> : 1 2
<enum 'testlist'> (1, 2)
<__main__.test object at 0x7f112f1f52e0> : 3 4
<enum 'testlist'> (3, 4)
test: testlist.A + testlist.B
test: <__main__.test object at 0x7f112f1d7430> + <__main__.test object at 0x7f112f1f52e0>

Solution 2:[2]

Aside: in your example of B = test(3,4), that final comma should not be there.


What you are running into is that when you subclass another data type, whatever you assign to the member name will be passed to the subclassed type's __new__ and/or __init__.

In other words, by inheriting from test, your testlist enum now has an __init__ that is expecting two arguments: one for par1 and one for par2 -- but you are only supplying one argument, e.g. test(1, 2). What you need to provide is simply 1, 2:

class testlist(test, enum.Enum):
    A = 1, 2
    B = 3, 4
    # a few more of these

In the above code the value for A will be calculated by calling test(1, 2).


Another example that might help: int can be called with two arguments, the first being a str, and the second a base in which to interpret that string. So:

class Count(IntEnum):
    ONE = 1              # value generated by int(1)
    TWO = '2'            # value generated by int('2')
    THREE = '3', 10      # value generated by int('3', 10)
    TEN = 'A', 16        # value generated by int('A', 16)

and when listed:

>>> list(Count)
[<Count.ONE: 1>, <Count.TWO: 2>, <Count.THREE: 3>, <Count.TEN: 10>]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2