Search code examples
pythonclassenumsmetaclassrepr

custom __repr__ as a class method for a class derived from Enum


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}

Solution

  • 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.