您的类的__init__ 方法将一个绑定方法 作为属性添加到您的类的instances。这与将属性添加到类中并不完全相同。通常,方法通过将函数作为属性存储在类中,然后创建方法对象,因为这些函数作为属性从任一类中检索(创建只知道它们所属类的未绑定方法)或实例(创建绑定方法,这些方法知道它们的实例。)
这与您所做的有什么不同?好吧,您分配给 specific 实例的GET instance 属性,而不是类。绑定的方法成为实例数据的一部分:
>>> s.__dict__
{'GET': <bound method Sub.get of <__main__.Sub object at 0xb70896cc>>}
请注意该方法是如何存在于键 GET 下的,但不在 get 下。 GET 是实例属性,但 get 不是。这在许多方面有细微的不同:该方法在类对象中不存在,因此您不能使用Sub.GET(instance) 来调用Sub 的GET 方法,即使您可以使用Sub.get(instance) .其次,如果您有一个 Sub 的子类定义了自己的 GET 方法但没有定义自己的 get 方法,则实例属性将隐藏子类 GET 方法 使用来自基类的绑定 get 方法。第三,它在绑定方法和实例之间创建循环引用:绑定方法具有对实例的引用,而实例现在存储对绑定方法的引用。通常绑定的方法不存储在实例上,部分是为了避免这种情况。循环引用通常不是什么大问题,因为我们现在有 cyclic-gc 模块 (gc) 来处理它们,但它不能总是清理引用循环(例如,当你的类也有一个 @ 987654337@ 方法。)最后,存储绑定方法对象通常会使您的实例不可序列化:大多数序列化程序(例如pickle)无法处理绑定方法。
您可能不关心这些问题,但如果您关心,那么有一种更好的方法可以解决您正在尝试做的事情:元类。您可以在创建类时将普通函数分配给类属性,而不是在创建实例时将绑定方法分配给实例属性:
class MethodAliasingType(type):
def __init__(self, name, bases, attrs):
# attrs is the dict of attributes that was used to create the
# class 'self', modifying it has no effect on the class.
# So use setattr() to set the attribute.
for k, v in attrs.iteritems():
if not hasattr(self, k.upper()):
setattr(self, k.upper(), v)
super(MethodAliasingType, self).__init__(name, bases, attrs)
class Base(object):
__metaclass__ = MethodAliasingType
class Sub(Base):
def get(self):
pass
现在,Sub.get 和 Sub.GET 确实是别名,在子类中覆盖一个而不是另一个可以按预期工作。
>>> Sub.get
<unbound method Sub.get>
>>> Sub.GET
<unbound method Sub.get>
>>> Sub().get
<bound method Sub.get of <__main__.Sub object at 0xb708978c>>
>>> Sub().GET
<bound method Sub.get of <__main__.Sub object at 0xb7089a6c>>
>>> Sub().__dict__
{}
(当然,如果你不想想要覆盖一个而不是另一个工作,你可以简单地在你的元类中把它作为一个错误。)你可以做同样的事情使用类装饰器的元类(在 Python 2.6 及更高版本中),但这意味着在 Base 的每个子类上都需要类装饰器——类装饰器不会被继承。