Search code examples
pythonpython-class

Mapping class methods of python class


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


Solution

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