2

I have a class which is derived from enum.Enum. Now repr in enum.Enum refers to the member of the enum.Enum not the entire class.

For Example

from enum import Enum
class endpoints(Enum):
""" shop """
    shop = 'shop'
    countries = 'countries'
    policies = 'policies'
    
print(endpoints)

Result

<enum 'endpoints'>

What I tried

from enum import Enum
class endpoints(Enum):
    shop = 'shop'
    countries = 'countries'
    policies = 'policies/{object_id}'
    @classmethod
    def __repr__(cls):
        return  '\n'.join([str(member.name) + ':' + (member.value) for member in cls.__members__])

print(endpoint)

        

Result

<enum 'endpoints'>

This indicates that repr is effectively endpoints.member.repr what I need is endpoint.repr?

I understand this can be achieved by a metaclass here, but since Enum itself has a metaclass, I cannot inherit/assign from another metaclass.

stopping short of modifying enum.Enum how can I achieve my objective.

The desired output is as follows.

print(endpoints)
shop : shop
countries : countries 
policies : policies/{object_id}
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
Siddharth Chabra
  • 448
  • 6
  • 22

2 Answers2

3

To modify a the way a class object is printed, like for any object, you need to modify it's classes __repr__, i.e. the metaclass __repr__, not simply decorate MyClass.__repr__ with classmethod. In this case, you need a custom EnumMeta:

In [1]: from enum import EnumMeta, Enum

In [2]: class CustomEnumMeta(EnumMeta):
   ...:     def __repr__(self):
   ...:         return '\n'.join([f"{name}: {value}" for name, value in self.__members__.items()])
   ...:

In [3]: class Endpoints(Enum, metaclass=CustomEnumMeta):
   ...:     shop = 'shop'
   ...:     countries = 'countries'
   ...:     policies = 'policies'
   ...:

In [4]: Endpoints
Out[4]:
shop: Endpoints.shop
countries: Endpoints.countries
policies: Endpoints.policies

Basically,

but since Enum itself has a metaclass, I cannot inherit/assign from another metaclass.

Is wrong. You can provide a custom metaclass if it derives from the bases' metaclass just fine even if your base class has a metaclass. It has to work this way because all classes have a metaclass, by default, type.

From the documentation:

3.3.3.3. Determining the appropriate metaclass

  • if no bases and no explicit metaclass are given, then type() is used;

  • if an explicit metaclass is given and it is not an instance of type(), then it is used directly as the metaclass;

  • if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used.

The most derived metaclass is selected from the explicitly specified metaclass (if any) and the metaclasses (i.e. type(cls)) of all specified base classes. The most derived metaclass is one which is a subtype of all of these candidate metaclasses.

Emphasis added. So in this simple case of single inheritance, since CustomEnumMeta is the most derived metaclass between CustomEnumMeta and EnumMeta, then it is used as the metaclass.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • This is the answer I was looking for. You are correct is stating I am wrong. When i tried the solution with a custom meta class i got a syntax error stating a conflict between my metaclass and the Enum metaclass, since my metaclass was incomplete and could not create an object, deriving from the orignal metaclass is a more sensible solution. – Siddharth Chabra Mar 20 '21 at 20:02
0

__repr__ is called only on class instance like endpoints('shop') not on class itself.

Alternatively instead of __repr__ you may use your own class-method like enum_members(cls), then you can call this own method on class itself instead of instance.

Also you have to do print(repr(obj)) instead of just print(obj) to use __repr__ method. If you want just to print(obj) then create __str__ method instead of __repr__.

Instead of cls.__members__ use list(cls) to enumerate all enum-members.

Try it online!

from enum import Enum
class endpoints(Enum):
    shop = 'shop'
    countries = 'countries'
    policies = 'policies/{object_id}'
    @classmethod
    def __repr__(cls):
        return '\n'.join([str(member.name) + ':' + (member.value) for member in list(cls)])

print(repr(endpoints('shop')))

Output:

shop:shop
countries:countries
policies:policies/{object_id}
Arty
  • 14,883
  • 6
  • 36
  • 69