8

我创建了一个名为 的 Python 类Liger,它扩展了一个名为 的类Lion和一个名为Tiger. 该类从andLiger继承了方法,但语法仍然有效 - 没有打印错误消息,而是方法的实现由继承。是否可以在 Python 中检测到像这样的方法名称冲突,以便在方法名称以这种方式冲突时打印错误消息?speak()LionTigerTigerspeak()Liger

'''
Conflicting method names in python
'''


class Tiger():
    @staticmethod
    def speak():
        print "Rawr!";

class Lion():
    @staticmethod
    def speak():
        print "Roar!";

class Liger(Tiger, Lion):
    pass
'''both superclasses define a speak() method, and I need a way to detect this type of conflict.'''    

Liger.speak(); ''' this prints "Rawr" instead of printing an error message. '''
'''Is there any way to detect method name collisions like this one?'''

代码可以在这里在线测试和调试:http: //ideone.com/xXOoVq

4

2 回答 2

4

我不确定继承是否是您用来解决问题的正确机制。继承通常被称为定义两个类之间的“IS-A”关系。也就是说,如果A继承自B,则 的每个实例都是B也的实例A

如果您使用多重继承,则该类最终会同时成为多种对象。在您的示例中,一个Liger实例同时是 aTiger和 a Lion

您可能不想使用类继承来模拟物种继承。尽管有名字,但它们的含义并不相同。例如,家猫可能是老虎的后代,但说家猫是老虎是不正确的。

您在类上使用静态方法的事实使我认为您可能根本不想使用类,而是更通用Animal类的实例:

class Animal(object):
    def __init__(self, sound):
        self.sound = sound

    def speak(self):
        print self.sound

lion = Animal("Roar!")

那里没有继承机制,但可以添加一种方法,以某种方式将一种动物变异为另一种动物。

现在,如果你真的开始使用类和多重继承,有两种不错的方法(使用常规实例方法,而不是静态方法):

首先是进行“协作多重继承”,让您的每个方法在 MRO 中的下一个类上调用相同的方法。通常你需要从一个通用的基类继承才能使这个工作(否则最后一次调用super将 get object,它不会定义方法):

class Animal(object):
    def speak(self):
        pass

class Lion(Animal):
    def speak(self):
        print("Roar!")
        super(Lion, self).speak()

class Tiger(Animal):
    def speak(self):
        print("Rawr!")
        super(Tiger, self).speak()

class Liger(Lion, Tiger):
    pass

在这种情况下,Liger实例的speak方法将同时打印Roarthen Rawr,这是有道理的,因为它同时是狮子和老虎。不过要小心这种编码风格,因为要让协作正常工作可能会很棘手。例如,您不能更改方法签名并期望它在协作多重继承情况下正常工作(除非您仅使用关键字参数,并传递任何无法识别的参数**kwargs)。

第二种解决方案是提供Liger它自己的speak实现,明确选择要做什么(可能是调用它的基类之一)。这在其他一些语言中是必需的,例如 C++。如果您请求的继承没有做您想要的,这可能是修复它的最直接方法:

class Liger(Lion, Tiger):
    def speak(self):
        Tiger.speak(self)
于 2013-03-10T23:20:43.427 回答
3

您可以使用元类检测这种冲突:

class ConflictCheck(type):
    def __new__(meta, name, bases, dct):
        # determine attributes per base class, except for magic ones
        attrs_per_base = [set(a for a in dir(b) if not a.startswith("__"))
                          for b in bases]
        if len(set.union(*attrs_per_base)) < sum(map(len, attrs_per_base)):
            raise ValueError("attribute conflict")
        return super(ConflictCheck, meta).__new__(meta, name, bases, dct)

class Liger(Lion, Tiger):
    __metaclass__ = ConflictCheck  # will raise an error at definition time

这是一个非常粗糙的第一个版本,错误消息很差,只要在继承树中覆盖上面的方法时,它实际上会引发错误Liger,但它应该足以让你开始。

于 2013-03-10T22:53:19.490 回答