在超类构造函数中的子类中添加方法 [英] add methods in subclasses within the super class constructor
问题描述
我想将方法(更具体地说:方法别名)自动添加到Python子类。如果子类定义一个名为get的方法,我想添加一个方法别名'GET'到子类的字典。
I want to add methods (more specifically: method aliases) automatically to Python subclasses. If the subclass defines a method named 'get' I want to add a method alias 'GET' to the dictionary of the subclass.
为了不重复自己,我想在基类中定义这个修改例程。但是如果我检查基类 __ init __
方法,没有这样的方法,因为它是在子类中定义的。它将变得更清楚一些源代码:
To not repeat myself I'd like to define this modifation routine in the base class. But if I check in the base class __init__
method, there is no such method, since it is defined in the subclass. It will become more clear with some source code:
class Base:
def __init__(self):
if hasattr(self, "get"):
setattr(self, "GET", self.get)
class Sub(Base):
def get():
pass
print(dir(Sub))
输出:
['__doc__', '__init__', '__module__', 'get']
它还应包含'GET'
It should also contain 'GET'
.
有没有办法在基类中做?
Is there a way to do it within the base class?
推荐答案
您的类的 __ init __
方法将 bound方法添加为实例的类。这与将属性添加到类完全不同。通常,方法通过将函数存储在类中作为属性,然后创建方法对象来工作,因为这些函数作为属性从类中检索(创建仅绑定方法,只知道它们属于哪个类)或实例(创建绑定方法,它们知道它们的实例。)
Your class's __init__
method adds a bound method as an attribute to instances of your class. This isn't exactly the same as adding the attribute to the class. Normally, methods work by storing functions in the class, as attributes, and then creating method objects as these functions are retrieved as attributes from either the class (creating unbound methods which only know the class they belong to) or the instance (creating bound methods, which know their instance.)
这与你在做什么有什么不同?您可以分配给特定实例的 GET
实例属性,而不是类。绑定方法成为实例数据的一部分:
How does that differ from what you're doing? Well, you assign to the GET
instance attribute of a specific instance, not the class. The bound method becomes part of the instance's data:
>>> s.__dict__
{'GET': <bound method Sub.get of <__main__.Sub object at 0xb70896cc>>}
请注意在 GET
下的方法是否存在,但不在 get
。 GET
是一个实例属性,但 get
不是。这在一些方面是微妙的不同:该方法不存在于类对象,所以你不能做 Sub.GET(instance)
调用<$即使你可以做 Sub.get(instance)$ c $ c> c $ c> Sub
' c $ c>。其次,如果你有一个Sub子类定义自己的
GET
方法但不是自己的 get
方法,实例属性将隐藏 get 子类
GET
方法 >方法从基础类。第三,它在绑定方法和实例之间创建一个循环引用:绑定方法具有对实例的引用,实例现在存储对绑定方法的引用。通常绑定的方法不存储在实例上部分地避免。循环引用通常不是一个大问题,因为我们现在有循环gc模块( gc
)来处理它们,但它不能总是清除引用循环(例如,当你的类也有一个 __ del __
方法。)最后,存储绑定的方法对象通常使你的实例unserializable:大多数序列化(如 pickle
)无法处理绑定的方法。
Notice how the method is there under the key GET
, but not under get
. GET
is an instance attribute, but get
is not. This is subtly different in a number of ways: the method doesn't exist in the class object, so you can't do Sub.GET(instance)
to call Sub
's GET
method, even though you can do Sub.get(instance)
. Secondly, if you have a subclass of Sub that defines its own GET
method but not its own get
method, the instance attribute would hide the subclass GET
method with the bound get
method from the baseclass. Thirdly it creates a circular reference between the bound method and the instance: the bound method has a reference to the instance, and the instance now stores a reference to the bound method. Normally bound methods are not stored on the instance partly to avoid that. Circular references are usually not a big issue, because we nowadays have the cyclic-gc module (gc
) that takes care of them, but it can't always clean up reference cycles (for instance, when your class also has a __del__
method.) And lastly, storing bound method objects generally makes your instances unserializable: most serializers (such as pickle
) can't handle bound methods.
你可能不在乎这些问题,但如果你这样做,方法到你想要做的:元类。在创建实例时,不必为实例属性分配绑定方法,您可以在创建类时为类属性分配正常函数:
You may not care about any of these issues, but if you do, there's a better approach to what you're trying to do: metaclasses. Instead of assigning bound methods to instance attributes as you create instances, you can assign normal functions to class attributes as you create the class:
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
c $ c> Sub.get 和 Sub.GET
真的是别名,重写一个而不是子类中的另一个工作原理。
Now, Sub.get
and Sub.GET
really are aliases, and overriding the one and not the other in a subclass works as expected.
>>> 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__
{}
(当然, em> 覆盖一个,而不是另一个工作,你可以简单地使这是一个错误在你的元类。)你可以做类似的东西,使用类装饰器(在Python 2.6和更高版本)这意味着要求Base类装饰器的每个子类上的类装饰器不被继承。
(Of course, if you don't want overriding the one and not the other to work, you can simply make this an error in your metaclass.) You can do the same thing as the metaclass using class decorators (in Python 2.6 and later), but it would mean requiring the class decorator on every subclass of Base -- class decorators aren't inherited.
这篇关于在超类构造函数中的子类中添加方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!