EschersEnigma EschersEnigma - 5 months ago 15
Python Question

Creating a set of Python classes

I have a class representing a Request. I have a set of very specific requests such as "I-95", "P-22", etc. which perform distinct functions and are called by a controller class. What is the best way to do this so that one could add more requests easily down the road?

I have something like this at the moment:

class Requests:
def __init__(self):
self.types = [
'I-95',
'P-22',
...
]

def generate_request(self,type, Data):
# Here I would call the appropriate case for the type, e.g. P-22


A Request case would be in its own separate file and look like this:

class P-22:
# Members
def __init__(self, Data):
# Set data members
def request(self):
# Submit request


And I would be able to create a request in the controller

f = Requests()
f.generate_request('RC75')


I'm striving for something that's as clean and easily extendable as possible. Thank you!

Answer

For convenience rename self.types to something else, because type is a reserved keyword. I'de suggest self.requests. Try something like:

class BaseRequest:
    name = None


class FooRequest(BaseRequest):
    name = 'I-95'

    def response(self):
        return "foo"


class BarRequest(BaseRequest):
    name = 'P-22'

    def response(self):
        return "bar"


class RequestManager:

    def __init__(self):
        self.requests = {
            FooRequest.name: FooRequest,
            BarRequest.name: BarRequest
        }

    def generate_request(self, name):
        if name in self.requests:
            return self.requests[name]()

    def register_request(self, request_class):
        assert issubclass(request_class, BaseRequest), \
            'Request class not a subclass of BaseRequest'
        assert hasattr('name', request_class) and isinstance(request_class.name, str), \
            'Request name not correctly configured'
        self.requests[request_class.name] = request_class

And then:

manager = RequestManager()
request = manager.generate_request('I-95')
if request is not None:
    print(request.response()) # "foo"

And for registering new requests:

class NewRequest(BaseRequest):
    name = 'N-1'

    def response(self):
        return "new"

manager = RequestManager()
manager.register_request(NewRequest)
request = manager.generate_request('N-1')
if request is not None:
    print(request.response()) # "new"

I personally think this is better done using a Singleton-pattern for the RequestManager (untested!):

class RequestManager:

    instance = None

    class __RequestManager:
        requests = {
            FooRequest.name: FooRequest,
            BarRequest.name: BarRequest
        }

        def generate_request(self, name):
            if name in self.requests:
                return self.requests[name]()

        def register_request(self, request_class):
            assert issubclass(request_class, BaseRequest), \
                'Request class not a subclass of BaseRequest'
            assert hasattr('name', request_class) and isinstance(request_class.name, str), \
                'Request name not correctly configured'
            self.requests[request_class.name] = request_class

    def __new__(cls):
        if not cls.instance:
            cls.instance = cls.__RequestManager()
        return cls.instance

    @staticmethod
    def getInstance():
        return RequestManager()

This creates a statically accessible RequestManager instance:

manager = RequestManager.getInstance()
# Rest same as before, register some requests, etc.

manager2 = RequestManager.getInstance() # This is actually the same manager ie. the same instance!