python类层次结构的问题

时间:2010-02-19 05:10:05

标签: python oop

我有一个类层次结构:

class ParentClass:

    def do_something(self):
        pass # child classes have their own implementation of this

class ChildClass1(ParentClass):

    def do_something(self):
        <implementation here>

class ChildClass2(ParentClass):

    def do_something(self, argument_x):
        <implementation here>

class ChildClass3(ParentClass):

    def do_something(self, argument_y):
        <implementation here>

这里有两个问题:

  • 方法do_something()在子类中有不同的接口:它接受子类2和3中的参数,但在子类1中没有参数
  • do_something()的参数有不同的名称,以强调它们在子类2和3中具有不同的含义。从使用示例
  • 下面将会更清楚。

这是使用类的方式:

有一个返回实例的工厂类:

class ChildFactory:

    def get_child(self, argument):
        if argument == '1':
            return ChildClass1()
        elif argument == '2':
            return ChildClass2()
        elif argument == '3':
            return ChildClass3()

稍后在代码中:

...
# pseudocode, not python
child_type = ? # can have values '1', '2' or '3' at this moment
var1 = 1
var2 = 'xxx'
# var1 and var2 have different types, just to emphasize the difference in their
# meaning when being passed as arguments to do_something()
# this was mentioned above (the second problem)
child = ChildFactory.get_child(child_type)
if child is an instance of ChildClass1, child.do_something() is called
if child is an instance of ChildClass2, child.do_something(var1) is called
if child is an instance of ChildClass3, child.do_something(var2) is called
# end of pseudocode

问题:

  1. 上面提到的两个问题是否是一个糟糕设计的标志?如果是这样,设计层次结构的正确方法是什么?
  2. 如何在python中统一编写伪代码片段?主要关注的是避免对每个特定情况使用巨大的if / else语句,因为它会使来自ChildFactory.get_child()的if / else语句加倍。

4 个答案:

答案 0 :(得分:10)

具有相同名称和不同参数的方法是代码气味。

“方法do_something()在子类中有不同的接口:它接受子类2和3中的参数,但在子类1中没有参数”

你没有说明原因。

有两个很好的理由
  • 子类1具有默认值。

  • 子类2忽略该值。

几乎任何其他原因都表明do_something 真正不同,并且应该有不同的名称。

如果子类1具有默认值,则只需在方法函数的参数中显式编写默认值。

class ChildClass1( ParentClass ):
    def do_something( argument_x= None )
        ....

如果子类1忽略该值,则只需忽略该值。不要站在你的头上忽视一个角色。

class ChildClass1( ParentClass ):
    def do_something( argument_x )
        return True

没有使用所有参数值的多态方法函数没有魔力。

“do_something()的参数具有不同的名称,以强调它们在子类2和3中具有不同的含义。”

这只是糟糕的设计。您不能使用具有不同参数名称的相同方法函数,因为它们执行不同的操作。

具有相同的方法函数名称是错误的。如果它们是类似事物的不同实现,那么参数将具有基本相同的含义。

如果他们实际上做了不同的事情,那么你就没有多态性,你不应该给这些方法赋予相同的名称。

当两个类中的方法做了根本不同的事情 - 需要使用不同名称的不同参数来表明这些 - 这些方法必须具有相同的名称。当该名称没有描述该方法的实际用途时,该名称就不再具有意义。

注意

你的代码,BTW,因为鸭子打字,将在Python中运行。只要方法名称匹配,参数类型就不必接近匹配。然而,这是非常糟糕的设计,因为这些方法之间的本质区别是如此巨大。

答案 1 :(得分:2)

这样的抽象问题很难回答。如果我们知道你试图解决什么问题,那么回答会容易得多。我可以告诉你,通常看到类似的东西是不好的迹象:

if isinstance(obj, cls1):
    ...
elif isinstance(obj, cls2):
    ...

通常,这意味着您应该定义一个新方法而不是使用if / elif。在Python中,如果您愿意,可以在类定义之外定义方法,如果这有帮助的话。如果两个可互换的类具有相同名称的方法但采用不同数量的参数,则通常也是一个不好的标志,这意味着这些类不是真正可互换的。不同的方法应该采用相同的参数,或者它们应该具有不同的名称。也没有必要定义从未调用过的方法,例如ParentClass中的do_something - 这是你从C ++ / Java / C#到Python的程序员看到的。

答案 2 :(得分:1)

如果要互换使用,它们应该具有相同的界面。对于方法,这意味着相同数量的参数具有相同的含义和相同的名称。如果它们的行为不完全相同,只需给它们不同的名称,使它们看起来可以互换。

答案 3 :(得分:-2)

你可以这样做,使签名相同:

class ParentClass:
    pass

class ChildClass1(ParentClass):

    def do_something(self, **kwargs):
        <implementation here>

class ChildClass2(ParentClass):

    def do_something(self, **kwargs):
        argument_x = kwargs[argument_x]
        <implementation here>

class ChildClass3(ParentClass):

    def do_something(self, **kwargs):
        argument_y = kwargs[argument_y]
        <implementation here>

工厂可能只是一个字典:

childfactory = {1:ChildClass1, 2:ChildClass2, 3:ChildClass3}

然后:

...
# pseudocode, not python
child_type = ? # can have values '1', '2' or '3' at this moment
var1 = 1
var2 = 'xxx'
# var1 and var2 have different types, just to emphasize the difference in their
# meaning when being passed as arguments to do_something()
# this was mentioned above (the second problem)
child = childfactory[child_type]()
child.do_something(argument_x=val1, argument_y=var2)
# end of pseudocode