您可以在运行时更改 python 描述符的 __get__ 方法吗?

mkl*_*ber 3 python decorator descriptor

所有,正如标题所问,是否可以__get__在运行时更改描述符的方法。我处于这样一种情况,我有一个函数在运行时被装饰和未装饰。我希望这个函数的结果可以作为一个属性使用,类似于@property所做的。我研究了一下,发现它是一个描述符,但似乎描述符的__get__方法是只读的。

class Test( object ):
    def __init__( self ):
        self._x = 10

    def get_x( self ):
        return self._x

    @property
    def x( self ):
        return self.get_x()
Run Code Online (Sandbox Code Playgroud)

上面的代码大致上做了我想要的

  1. 该值在构造函数中设置
  2. 我可以随心所欲地装饰get_x方法
  3. instance.x 返回正确的值

我的问题是我宁愿不必创建该get_x方法,因为它基本上是不必要的。我只是无法装饰__get__x的方法,因为它是只读的。

背景

我正在编写一个回合制策略游戏,我正在使用装饰器来实现持久条件。当我使用测试用例时,我能够有效地实现这些装饰器,但问题是要获取计算值,您必须使用函数调用,而不是属性访问。这似乎是一个坏主意,因为获取描述单元的值会不一致地使用函数或属性。如果可以,我想对属性进行标准化。

Odo*_*ois 5

您可以使用简单继承覆盖property's__get__属性的默认“只读”特性:

class MyProperty( property ): pass

class Test( object ):
    def __init__( self ):
        self._x = 10

    def get_x( self ):
        return self._x

    @MyProperty
    def x( self ):
        return self.get_x()

test = Test()
Run Code Online (Sandbox Code Playgroud)

现在的问题是,即使您重新定义__get__Text.x 属性的属性,test.xpython 运行时也会根据请求调用MyProperty.__get__(Test.x, test, Test)

所以你只能在那里重写它

MyProperty.__get__ = lambda self,instance,owner: ""the x attribute"
Run Code Online (Sandbox Code Playgroud)

这里很好的选择是将调用委托给一些可重新定义的属性,例如

MyProperty.__get__ = lambda self,instance,owner: self.get(instance,owner)
Run Code Online (Sandbox Code Playgroud)

从现在开始,您可以完全控制您的财产的属性。为每个类似属性的对象生成单独的类型也有不好的选择。因此,在良好的情况下,您可以执行以下操作:

class MyProperty( property ):
    def __get__(self,instance,owner) :
        if not instance: return self
        else: return self.get(instance,owner)

class Test( object ):
    def __init__( self ):
        self._x = 10

    def get_x( self ):
        return self._x

    @MyProperty
    def x( self ): pass

    @MyProperty
    def y(self): pass

    x.get = lambda self,clazz: self.get_x()
    y.get = lambda self,clazz: "the y property of " + clazz.__name__ + " object"

>>> test = Test()
>>> test.x
10
>>> test.y
'the y property of Test object'
Run Code Online (Sandbox Code Playgroud)