将类的功能组合到另一个类的最合适的方法?

时间:2013-08-23 01:13:36

标签: python oop dependency-injection mixins

嘿伙计们我是新来的,但希望我的问题很清楚。

我的代码是用Python编写的。 我有一个代表一般网站的基类,这个类包含一些从网站获取数据并保存的基本方法。该类由许多其他类扩展,每个类代表一个不同的网站,每个类都包含特定于该网站的属性,每个子类使用基类方法来获取数据。所有站点都应该在其上解析数据,但许多站点共享相同的解析功能。所以我创建了几个解析类,它们包含不同解析方法的功能和属性(我有大约六个)。我开始思考将这些类与需要它们的网站类集成的最佳方法。

起初我认为每个网站类都会包含一个类变量,其中包含与之对应的解析器类,但我认为必须有更好的方法来实现它。

我读了一下,并认为我可能会更好地依靠Mixins为每个网站集成解析器,但后来我认为虽然这样可行但它并不“正确”,因为网站类没有继承的业务解析器类(甚至认为它只是一个Mixin而不是完整的类继承),因为除了网站使用解析器功能之外它们没有任何关联。

然后我想我可能依赖于我为python看到的一些依赖注入代码将解析器注入到每个网站,但它听起来有点过分。

所以我想我的问题基本上是,什么时候最好使用每个案例(在我的项目和任何其他项目中),因为他们都做了工作,但似乎不是最合适的。

感谢您提供任何帮助,我希望我很清楚。

添加一个小的模拟示例来说明:

class BaseWebsite():
    def fetch(): # Shared by all subclasses websites
       ....
    def save(): # Shared by all subclasses websites
       ....

class FirstWebsite(BaseWebsite): # Uses parsing method one
    ....
class SecondWebsite(BaseWebsite): # Uses parsing method one
    ....
class ThirdWebsite(BaseWebsite): # Uses parsing method two
    ....

等等

1 个答案:

答案 0 :(得分:1)

我认为你的问题是你正在使用你应该使用实例的子类。

根据您的描述,每个网站都有一个类,其中包含一系列属性。大概你可以创建每个类的单例实例。在Python中很少有理由这样做。如果每个网站需要不同的数据 - 基本URL,解析器对象/工厂/功能等 - 您只需将其存储在实例属性中,因此每个网站都可以是同一类的实例。

如果网站实际上需要以不同的方式覆盖基类方法,那么将它们作为不同的类是有意义的(尽管在那里,你应该考虑是否将该功能转移到外部函数或可以是的对象中)正如您已经使用解析器一样,网站使用的)。但如果没有,那就没有充分的理由去做。

当然我在这里可能是错的,但事实上你定义了旧式类,从你的方法中留下了self参数,讨论了类属性,并且通常使用Java术语而不是Python术语使我认为这个错误不太可能。

换句话说,你想要的是:

class Website:
    def __init__(self, parser, spam, eggs):
        self.parser = parser
        # ...
    def fetch(self):
        data = # ...
        soup = self.parser(data)
        # ...

first_website = Website(parser_one, urls[0], 23)
second_website = Website(parser_one, urls[1], 42)
third_website = Website(parser_two, urls[2], 69105)

假设您有20个网站。如果您正在创建20个子类,那么每个人都会编写六行样板文件,并且可能会出现很多问题,这些细节可能会让调试变得很麻烦。如果您正在创建20个实例,那么它只是样板的几个字符,并且错误要少得多:

websites = [Website(parser_one, urls[0], 23),
            Website(parser_two, urls[1], 42),
            # ...
           ]

或者您甚至可以将数据移动到数据文件中。例如,像这样的CSV:

url,parser,spam
http://example.com/foo,parser_one,23
http://example.com/bar,parser_two,42
…

您可以更轻松地编辑它 - 甚至可以使用电子表格程序进行编辑 - 无需任何无关的输入。您可以使用几行代码将其导入Python:

with open('websites.csv') as f:
    websites = [Website(**row) for row in csv.DictReader(f)]