简体   繁体   English

重定义对象的方法

[英]Redefine Method of an Object

I've got a class, where a method should only run once.我有一个类,其中一个方法应该只运行一次。 Of course, it could easily be done with artificial has_executed = True/False flag, but why use it, if you can just delete the method itself?当然,它可以很容易地用人工has_executed = True/False标志来完成,但为什么要使用它,如果你可以删除方法本身呢? python 'sa duck-typed language, everything is a reference, bla-bla-bla , what can go wrong? python是一种鸭式语言,一切都是参考, bla-bla-bla ,会出什么问题?

At least it was the thought.至少是这样的想法。 I couldn't actually do it:我实际上无法做到:

class A:    
    def b(self):
        print("empty")
        self.__delattr__('b')

a = A()
a.b()

raises AttributeError: b .引发AttributeError: b However, executing self.__getattribute__('b') returns <bound method Ab of <__main__.A object at 0x000001CDC6742FD0>> , which sounds stupid to me: why is a method any different from an attribute , since everything in python is just a reference to an object?但是,执行self.__getattribute__('b')返回<bound method Ab of <__main__.A object at 0x000001CDC6742FD0>> ,这对我来说听起来很愚蠢:为什么methodattribute有任何不同,因为python中的所有内容都只是一个对对象的引用? And why can I __getattribute__ , but not __delattr__ ?为什么我可以__getattribute__而不是__delattr__

The same goes to redefinition.重新定义也是如此。 I can easily set any attribute, but methods are a no-no?我可以轻松设置任何属性,但方法是禁忌吗?

class A:
    def b(self):
        print("first")
        self.__setattr__('b', lambda self: print(f"second"))

a = A()
a.b()
a.b()

results into TypeError: <lambda>() missing 1 required positional argument: 'self' .结果为TypeError: <lambda>() missing 1 required positional argument: 'self' Which, of course, means, that now python isn't using dot-notation as intended.这当然意味着,现在python没有按预期使用点符号。 Of course, we could ditch the self attribute in the lambda altogether, considering we've got the reference to it already in b .当然,我们可以完全放弃 lambda 中的self属性,考虑到我们已经在b获得了对它的引用。 But isn't it incorrect by design?但这不是设计不正确吗?

The further I'm trying to take python to the limit, the more frustrated I become.我越是试图将python发挥到极致,我就越沮丧。 Some imposed limitations ( or seemingly imposed? ) seem so unnatural, considering the way the language is marketed.考虑到语言的营销方式,一些强加的限制(或看似强加的? )似乎很不自然。 Shouldn't it allow this?这不应该允许吗? Why doesn't it work?为什么不起作用?

UPD UPD

Ok, consider this:好的,考虑一下:

class A:
    def __init__(self):
        self.variable = 1

    def b(self):
        print("old")
        self.variable += 1
        def new_b():
            print("new")
            self.variable += 15
        self.__setattr__('b', new_b)

It will work and do what we want: none of other objects will have their Ab method redefined once one object kind of overlays its b definition.它会工作并做我们想要的:一旦一种对象覆盖了它的b定义,其他任何对象都不会重新定义它们的Ab方法。 ( overlays , since everyone so far says that you cannot redefine a method for an object, but instead only kind of hide it from the caller behind another attribute with the same name, as far as I understand). overlays ,因为到目前为止每个人都说你不能为一个对象重新定义一个方法,而只是将它隐藏在另一个同名属性后面的调用者中,据我所知)。

Is this good?这个好吗?

It doesn't work because b isn't an attribute belonging to the instance, it belongs to the class.它不起作用,因为b不是属于实例的属性,它属于类。 So you can't delete it on the instance because it isn't there to be deleted.所以你不能在实例上删除它,因为它不是要删除的。

>>> a = A()
>>> list(a.__dict__)
[]
>>> list(A.__dict__)
['__module__', 'b', '__dict__', '__weakref__', '__doc__']

When ab is evaluated, Python will see that a has no instance attribute named b and fall back to the class.ab被求值时,Python 将看到a没有名为b实例属性并回退到该类。 (It's a little more complicated because when falling back to the class, it will not simply return the method itself, but a version of the method which is bound to the instance a .) (这有点复杂,因为当回退到类时,它不会简单地返回方法本身,而是绑定到实例a的方法版本。)

Since you don't want to delete the method on the class, the way to go is to replace the method on the instance.既然不想删除类上的方法,那么要走的路就是替换实例上的方法。 I don't know why you tried to do this with __setattr__ - there is no need for that, simply assign self.b = ... as normal.我不知道你为什么试图用__setattr__来做到这__setattr__ - 没有必要,只需像往常一样分配self.b = ... The reason your attempt failed is because your lambda requires a positional parameter named self , but this parameter will not be automatically bound to the instance when you look it up, because it is an instance attribute, not a class attribute.你的尝试失败的原因是你的 lambda 需要一个名为self的位置参数,但是当你查找它时,这个参数不会自动绑定到实例,因为它是一个实例属性,而不是一个类属性。

class A:
    def b(self):
        print('first')
        self.b = lambda: print('second')

Usage:用法:

>>> a = A()
>>> a.b()
first
>>> a.b()
second

Well in python you have 2 types of attributes那么在python中你有两种类型的属性

  • A class attribute is a variable that belongs to a certain class, and not a particular object.类属性是属于某个类而不是特定对象的变量。 Every instance of this class shares the same variable.此类的每个实例共享相同的变量。 These attributes are usually defined outside the init constructor这些属性通常在init构造函数之外定义
  • An instance/object attribute is a variable that belongs to one (and only one) object. instance/object attribute是属于一个(且仅一个)对象的变量。 Every instance of a class points to its own attributes variables.类的每个实例都指向它自己的属性变量。 These attributes are defined within the init constructor.这些属性在init构造函数中定义。

In case of a class attribute its part of the class descriptor, so you cannot delete it from the object attributes like self.__deleteattr__ or add new one with __setattr__ as it alters the class descriptor and reflects on all objects.如果类属性是类描述符的一部分,因此您不能将其从对象属性中删除,例如self.__deleteattr__或使用__setattr__添加新属性,因为它会更改类描述符并反映在所有对象上。 Such an operation can have devastating effects.这样的操作可能会产生破坏性的影响。

Its very similar to a class variable as well.它也非常类似于类变量。 You can however change the behavior with overriding or reassigning like below但是,您可以通过覆盖或重新分配来更改行为,如下所示

class A:    
    def b(self):
        print("empty")
        A.b =  lambda self: print(f"second")

a = A()
a.b()
a.b()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM