updated 2010-06-15T09:45:00Z
我想知道如何在Python中实现原型继承。这个问题似乎有两种不同的方法:类作为实例,实例作为类。
第二种方法似乎更灵活,因为它可以应用于不同类型的现有对象,而第一种方法对于典型用例可能更方便。
这里的想法是使用元类来使实例化实际上是类,而不是对象。这种方法看起来像这样:
class ClassAsInstance(type):
""" ClassAsInstance(type)\n
>>> c = ClassAsInstance()
>>> c.prop = 6
It's sort of annoying to have to make everything a class method.
>>> c.jef = classmethod(lambda self: self.prop)
>>> c.jef()
6
>>> cc = c()
>>> cc.jef()
6
But it works.
>>> c.prop = 10
>>> cc.jef()
10
>>> c.jef = classmethod(lambda self: self.prop * 2)
>>> cc.jef()
20
"""
def __new__(self):
return type(self.__name__ + " descendant", (self, ), {})
我没有用这种方法测试任何复杂的东西,所以它可能有局限性。
使用这种方法,我们的想法是使用type
构造函数从对象创建类。这在Alex Martelli's answer中得到了例证,尽管他用于此方法的示例实现了复制原型,而不是允许后代继承其原型的后续更改。
我的方法是做这样的事情:
def createDescendant(obj):
return type(obj.__class__.__name__ + " descendant", (obj.__class__, ), obj.__dict__)()
它将以某种javascript-y方式工作:对给定对象的更改不会影响其后代,但对父对象__class__
(如javascript prototype
)的更改将会。我认为这是因为type
构造函数复制 obj.__dict__
而不是在某种mro-ish方案中引用它。
我尝试实现一个允许真正原型继承的改进版本,其中对象将继承对父对象的更新。我们的想法是将原型对象的__dict__
属性分配给新创建的类的同一属性,该属性成为后代对象的类。
然而,由于我发现__dict__
的{{1}}无法分配,因此无效。此限制也适用于从type
派生的类。我仍然很好奇是否有可能通过创建一个“实现类型协议”的对象来解决这个问题,就像迭代,序列等一样,但实际上并不是从type
继承。这可能会产生其他问题,例如Alex在答案的第一部分提到的委托方法所固有的问题。
Alex还提出了第三种方法,即委托方法,其中对象的状态通过type
魔术方法传播到后代对象。再次,请参阅Alex的答案,以及有关此方法局限性的详细信息。
特此要求进一步深入了解这些方法的实用性以及其他建议。
答案 0 :(得分:4)
如果您需要将原型对象的未来更改透明地反映在所有“后代”中,那么您必须求助于显式委派。在常规方法中,这可以通过__getattr__
轻松完成,例如,派生自:
class FromPrototype(object):
def __init__(self, proto):
self._proto = proto
def __getattr__(self, name):
return getattr(self._proto, name)
...只要您继承原型的状态,而不仅仅是行为。不幸的是,原型中未被重写的方法将不会感知任何可能在当前对象中被覆盖的状态。此外,特殊方法(魔术名称以双下划线开头和结尾的方法)在类中而不是实例中查找,不能简单地以这种方式委托。因此,解决这些问题可能需要大量工作。
如果你不关心“无缝继承”未来对原型的改动,但是在“原型继承”时间拍摄后者的快照是很好的,那就更简单了:
import copy
def proto_inherit(proto):
obj = copy.deepcopy(proto)
obj.__class__ = type(obj.__class__.__name__, (obj.__class__,), {})
return obj
以这种方式构建的每个对象都有自己的类,因此您可以在类上设置特殊方法(在从proto_inherit
获取对象之后)而不影响任何其他对象(对于普通方法,您可以在类或实例,虽然总是使用类会更规则和一致)。
答案 1 :(得分:0)
这是一种更强大的委托方法版本。 主要的改进是
当继承的成员是一个方法时,则返回一个具有相同底层函数但绑定到原始调用对象的方法。 这就是@AlexMartelli在他的回答中提出的问题:
...只要你还继承原型的状态,不是 只是行为。不幸的是,非重写方法来自 原型不会察觉任何可能被覆盖的状态 当前的对象。
遵循协作继承约定,以免破坏基于类的继承
一个限制是Proto
类必须在方法解析顺序中排在第一位才能使初始化正常工作
import types
import inspect
class Proto(object):
def __new__(self, proto, *args, **kw):
return super(Proto, self).__new__(self, *args, **kw)
def __init__(self, proto, *args, **kw):
self.proto = proto
super(Proto, self).__init__(*args, **kw)
def __getattr__(self, name):
try:
attr = getattr(self.proto, name)
if (inspect.ismethod(attr) and attr.__self__ != None):
attr = types.MethodType(attr.__func__, self)
return attr
except AttributeError:
return super(Proto, self).__getattr__(name)
下面对b.getY()
的调用说明了Alex的观点,如果使用他的答案中的FromPrototype
课而不是Proto
from delegate import Proto
class A(Proto):
x = "This is X"
def getY(self):
return self._y
class B(Proto):
_y = "This is Y"
class C(object):
def __getattr__(self, name):
return "So you want "+name
class D(B,C):
pass
if __name__ == "__main__":
a = A(None)
b = B(a)
print b.x
print b.getY()
d = D(a)
print d.x
print d.getY()
print d.z