Mapping class methods of python class

Question:

I created a message class, which stores an arbitrary message like so:

class MessageBase:
    """Wrapper class for messages from different resources"""
    
    def __init__(
        self,     
        subject,
        body,
        sender,
        recipient,
        received,
        to
    ):

        self.subject = subject
        self.body = body
        self.sender = sender
        self.recipient = recipient
        self.received = received
        self.to = to

Messages can come from different sources, and I want my class to be flexible with these resources. I want to create such a syntax:

message1 = Message.from_source(source='outlook', raw_outlook_message)
message2 = Message.from_source(source='sendinblue_email', raw_sendinblue_message)
message1.perform_action()
message2.perform_action()

od way to code a class, or whether this goes against some practices.

To achieve this, I created several class methods, and want to map them as such:

        self.sources = {
            'outlook': self.from_outlook,
            'sendinblue_email': self.from_sendinblue_email,
        }

    @classmethod
    def from_source(cls, source_name, *args):
        return cls.sources[source_name.lower()](*args)

    @classmethod
    def from_outlook(cls, raw_message: dict):
        return cls(<parse_outlook_message>)

    @classmethod
    def from_sendinblue_email(cls, raw_message: dict):
        return cls(<parse_outlook_message>)

I have one issue with this setup and one concern. The issue is that I cannot access the sources dictionary of self.sources, and I do not know how to achieve this functionality. The concern is whether this is a go

Asked By: Jeroen Vermunt

||

Answers:

You want a class attribute that maps strings to class methods.

Since the objects are instances of classmethod, you can’t call them directly in from_source; you’ll have to extract the underlying function
and call it with an explicit class argument.

class MessageBase:
    @classmethod
    def from_outlook(cls, ...):
        ...

    @classmethod
    def from_sendinblue_email(cls, ...):
        ...

    # When this dict is defined, the above are still just names
    # in the current namespace, not class attributes.
    sources = {
        'outlook': from_outlook,
        'sendinblue_email': from_sendinblue_email,
    }

    @classmethod
    def from_source(cls, source, ...):
        f = cls.sources[source.lower()].__func__
        return f(cls, ...) 

An alternative is to add the name of the class method to the mapping, and letting from_source perform attribute look up on the class.

class MessageBase:
    @classmethod
    def from_outlook(cls, ...):
        ...

    @classmethod
    def from_sendinblue_email(cls, ...):
        ...

    # When this dict is defined, the above are still just names
    # in the current namespace, not class attributes.
    sources = {
        'outlook': 'from_outlook',
        'sendinblue_email': 'from_sendinblue_email'
    }

    @classmethod
    def from_source(cls, source, ...):
        method_name = cls.sources[source.lower()].__func__
        return getattr(cls, method_name)(...) 
Answered By: chepner

Why not just define another class method

>>> class MessageBase:    
...     @classmethod
...     def sources(cls):
...         return { 'sendinblue_email': cls.from_sendinblue_email } 
...     @classmethod      
...     def from_sendinblue_email(cls, raw_message: dict):
...         return cls()                                             
... 
Answered By: HansQ
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.