我创建了一个名为Liger
的Python类,它扩展了一个名为Lion
的类和一个名为Tiger
的类。类Liger
继承了speak()
和Lion
中的方法Tiger
,但语法仍然有效 - 不会打印错误消息,而是Tiger
speak()
方法的实现由Liger
继承。是否有可能在Python中检测类似于此的方法名称冲突,以便在方法名称以这种方式冲突时打印错误消息?
'''
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
答案 0 :(得分:2)
我不确定继承是否是您用来解决问题的正确机制。通常认为继承定义了两个类之间的“IS-A”关系。也就是说,如果A
继承自B
,B
的每个实例也都是A
的实例。
如果您正在使用多重继承,则该类最终会同时成为多种对象。在您的示例中,Liger
实例同时是Tiger
和Lion
。
您可能不希望使用类继承来模拟物种继承。尽管有这个名字,但它们并不意味着同样的事情。例如,家猫可能是一种非常古怪的东西,但是说一只家猫是一只老虎是不正确的。
你在类上使用静态方法的事实让我觉得你可能根本不想使用类,而是更通用的Animal
类的实例:
class Animal(object):
def __init__(self, sound):
self.sound = sound
def speak(self):
print self.sound
lion = Animal("Roar!")
那里没有继承机制,但是可以添加一种方法,以某种方式将一种Animal变异为另一种。
现在,如果您真的开始使用类和多继承,那么有两种不错的方法(使用常规实例方法,而不是静态方法):
第一个是做“协作多重继承”,并让你的每个方法在MRO的下一个类中调用相同的方法。通常,您需要从公共基类继承才能使其工作(否则对super
的最后一次调用将获得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
方法会打印Roar
然后Rawr
,这是有道理的,因为它既是狮子又是老虎。但是要注意这种编码风格,因为让协作正常工作可能很棘手。例如,您无法更改方法签名并期望它在协作多重继承情况下正常工作(除非您仅使用关键字参数,并传递**kwargs
中任何未识别的方法)。
第二个解决方案是给Liger
它自己的speak
实现,它明确地选择要做什么(可能是调用它的一个基类)。这在某些其他语言中是必需的,例如C ++。如果您请求的继承没有按照您的意愿执行,那么它可能是最直接的解决方法:
class Liger(Lion, Tiger):
def speak(self):
Tiger.speak(self)
答案 1 :(得分:1)
您可以使用元类检测此类冲突:
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
以上的方法时,实际上就会引发错误,但它应该足以让你开始。