简体   繁体   中英

What is the alternative to dict as way to parameterize python class?

I usually see the following python code around the codebase I work with:

class A:
    def do_something(self):
        pass

class B:
    def do_something(self):
        pass


class C:
    _magic_dict = {'option1' : A, 'option2' : B}

   def do_something_else(self, option):
       # call some other methods, functiosn
       my_option = _magic_dict['option']()
       my_option.do_something()

       self.do_more_things(my_option)

So the basic idea is to make class C generic to A or B. Is this a common practice? I feel like it is an overuse of dicts and the fact that everything (in this case the class) is an object that can be passed around.

For a more specific problem, the following example might help. There is a class responsible to take a given metric object, which in the end is a dict of objects holding the info for that type of metric. And there is a statistics reporter which will take a given metric object, choose a type of data it is interested to report (one of the entries in the dict, let's say) and output that in a pretty format. So:

class FoodMetric:
    def __init__(self, path_to_my_stock_files):
        self._path = path_to_my_stock_files
        self._data = {}

    def parse(self):
        # let's assume that after parsing the files, the following data would have been obtained:
        # self.data = {'cheese' : {'cheddar' : 10, 'goat' : 20}}

class WoodFastenerMetric:
    def __init__(self, path_to_my_stock_files):
        self._path = path_to_my_stock_files
        self._data = {}

    def parse(self):
        # let's assume that after parsing the files, the following data would have been obtained:
        # self.data = {'nails' : {'round' : 10, 'oval' : 20}}

class StatsReporter:

    _magic_dict = {'food' : (FoodMetric, ['column1', 'column2'], 'cheese')
                   'wood_fastener' : (WoodFastener, ['columnA', 'columnB'], 'nail')
                  }      

    def present_data(metric_type):
        the_class, columns, data_set_name = _magic_dict(metric_type)
        metric = the_class(self._path) # assume I have the path for the files here
        metric.parse()
        print(self._convert_to_table(metric, columns, data_set_name))

I do have an alternative implementation in mind which creates C by passing an instance of either of A or B to it, therefore eliminating this dictionary lookup inside C.

What are other alternatives, or is the solution stated in the question a common way to solve this in python?

PS: I hope the example make the intent more clear.

Using the same class and method names as in the original question, and lacking any information about the actual problem being solved, I would refactor the code into the following:

class C:
   def do_something_else(self):
       self.do_something()
       self.do_more_things()

    def do_something(self):
        raise NotImplementedError()

class A(C):
    def do_something(self):
        pass

class B(C):
    def do_something(self):
        pass

def make_instance(option):
    return {'option1' : A, 'option2' : B}[option]()

instance = make_instance(option)
instance.do_something_else()

With that approach, the design is clear: C implements common functionality, whereas A and B are specializations of it.

The only ugly part remaining is the make_instance function, which can probably also be better, but not the way the question was stated, because it is not clear where option comes from.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM