在超类构造函数中添加子类中的方法

发布于 2024-08-30 12:54:46 字数 523 浏览 5 评论 0原文

我想自动向 Python 子类添加方法(更具体地说:方法别名)。如果子类定义了一个名为“get”的方法,我想将方法​​别名“GET”添加到子类的字典中。

为了不重复我自己,我想在基类中定义这个修改例程。但是如果我检查基类 __init__ 方法,则没有这样的方法,因为它是在子类中定义的。通过一些源代码,它会变得更加清晰:

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'

有没有办法在基类中做到这一点?

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.

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))

Output:

['__doc__', '__init__', '__module__', 'get']

It should also contain 'GET'.

Is there a way to do it within the base class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

念﹏祤嫣 2024-09-06 12:54:46

您的类的 __init__ 方法将一个绑定方法作为属性添加到您的类的实例。这与将属性添加到类中并不完全相同。通常,方法的工作原理是将函数作为属性存储在类中,然后创建方法对象,因为这些函数作为属性从任一类中检索(创建只知道它们所属的类的未绑定方法)或实例(创建绑定方法,它知道它们的实例。)

这与您正在做的有什么不同?好吧,您分配给特定实例的GETinstance属性,而不是类。绑定方法成为实例数据的一部分:

>>> s.__dict__
{'GET': <bound method Sub.get of <__main__.Sub object at 0xb70896cc>>}

请注意该方法如何位于键 GET 下,而不是在 get 下。 GET 是一个实例属性,但 get 不是。这在很多方面都有细微的不同:类对象中不存在该方法,因此您无法执行 Sub.GET(instance) 来调用 SubGET 方法,即使您可以执行 Sub.get(instance)。其次,如果您有一个 Sub 子类,它定义了自己的 GET 方法,但没有定义自己的 get 方法,则实例属性将使用基类中绑定的 get 方法隐藏子类 GET 方法。第三,它在绑定方法和实例之间创建循环引用:绑定方法具有对实例的引用,并且实例现在存储对绑定方法的引用。通常绑定的方法不会存储在实例上,部分原因是为了避免这种情况。循环引用通常不是一个大问题,因为我们现在有 cycling-gc 模块 (gc) 来处理它们,但它不能总是清理引用循环(例如,当您的类也有一个 __del__ 方法。)最后,存储绑定方法对象通常会使您的实例不可序列化:大多数序列化程序(例如 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

Now、Sub.getSub.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__
{}

(当然,如果您不想覆盖其中一个而不是另一个,您只需在元类中将其设置为错误即可。)您可以使用 class 执行与元类相同的操作装饰器(在 Python 2.6 及更高版本中),但这意味着需要在 Base 的每个子类上使用类装饰器——类装饰器不会被继承。

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.)

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>>}

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

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__
{}

(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.

卷耳 2024-09-06 12:54:46

这是因为 Sub 类尚未启动,请在其实例中执行此操作,例如

>>> s=Sub()
>>> dir(s)
['GET', '__doc__', '__init__', '__module__', 'get']
>>>

Its because class Sub hasn't been initiated yet, do it in its instance like

>>> s=Sub()
>>> dir(s)
['GET', '__doc__', '__init__', '__module__', 'get']
>>>
白色秋天 2024-09-06 12:54:46

在派生类中创建一个派生构造函数来设置属性。

Create a derived constructor in your derived class which sets the attribute.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文