重新定义对象的方法

win*_*win 5 python duck-typing metaprogramming

我有一个类,其中的方法只能运行一次。当然,可以很容易地使用人工has_executed = True/False标志来完成,但是如果您可以删除方法本身,为什么还要使用它呢? python鸭子式的语言,一切都是参考,bla-bla-bla,会出什么问题吗?

至少是这么想的。我实际上做不到:

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

a = A()
a.b()
Run Code Online (Sandbox Code Playgroud)

提高AttributeError: b。然而,执行self.__getattribute__('b')returns <bound method A.b of <__main__.A object at 0x000001CDC6742FD0>>,这对我来说听起来很愚蠢:为什么 amethod与 an 不同attribute,因为 in 中的所有内容都python只是对对象的引用?而为什么我可以__getattribute__,却不能__delattr__

重新定义也是如此。我可以轻松设置任何属性,但方法是不行的吗?

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

a = A()
a.b()
a.b()
Run Code Online (Sandbox Code Playgroud)

结果变成TypeError: <lambda>() missing 1 required positional argument: 'self'. 当然,这意味着现在python没有按预期使用点表示法。当然,self考虑到我们已经在b. 但这不是设计上不正确的吗?

我越是试图达到python极限,我就越感到沮丧。考虑到该语言的营销方式,一些强加的限制(或看似强加的? )看起来非常不自然。难道不应该允许这样做吗?为什么不起作用?

UPD

好的,考虑一下:

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)
Run Code Online (Sandbox Code Playgroud)

A.b它将工作并执行我们想要的操作:一旦一种对象类型覆盖其定义,其他对象都不会重新定义其方法b。(覆盖,因为到目前为止每个人都说你不能重新定义对象的方法,而只是将其隐藏在另一个同名属性后面的调用者手中,据我所知)。

这个好吗?

kay*_*ya3 6

它不起作用,因为它b不是属于实例的属性,而是属于类的属性。因此您无法在实例上删除它,因为它不存在可删除的地方。

>>> a = A()
>>> list(a.__dict__)
[]
>>> list(A.__dict__)
['__module__', 'b', '__dict__', '__weakref__', '__doc__']
Run Code Online (Sandbox Code Playgroud)

a.b评估时,Python 将发现a没有名为的实例属性b并回退到该类。(这有点复杂,因为当回退到类时,它不会简单地返回方法本身,而是绑定到实例的方法的版本a。)

由于您不想删除类上的方法,因此最好的方法是替换实例上的方法。我不知道你为什么尝试这样做__setattr__- 没有必要这样做,只需self.b = ...照常分配即可。您尝试失败的原因是您的 lambda 需要一个名为 的位置参数self,但是当您查找该参数时,该参数不会自动绑定到实例,因为它是实例属性,而不是类属性。

class A:
    def b(self):
        print('first')
        self.b = lambda: print('second')
Run Code Online (Sandbox Code Playgroud)

用法:

>>> a = A()
>>> a.b()
first
>>> a.b()
second
Run Code Online (Sandbox Code Playgroud)