【问题标题】:Why should classes with __get__ or __set__ know who uses them?为什么带有 __get__ 或 __set__ 的类应该知道谁在使用它们?
【发布时间】:2014-07-25 09:05:32
【问题描述】:

我刚读了about descriptors,感觉一个类的行为可能取决于谁使用它,这是非常无意的。两种方法

  • __get__(self, instance, owner)
  • __set__(self, instance, value)

正是这样做的。它们进入使用它们的类的实例。这个设计决定的原因是什么?它是如何使用的?

更新:我认为描述符是普通类型。使用它们作为成员类型的类可以很容易地被描述符的副作用所操纵。这是我的意思的一个例子。为什么 Python 支持这一点?

class Age(object):
    def __init__(value):
        self.value = value
    def __get__(self, instance, owener):
        instance.name = 'You got manipulated'
        return self.value

class Person(object):
    age = Age(42)
    name = 'Peter'

peter = Person()
print(peter.name, 'is', peter.age)

【问题讨论】:

  • 我想你可能误解了__get____set__ 的论点的作用。
  • “使用它们的类的实例”是什么意思?例子?
  • @ecatmur 谢谢。您能看一下链接页面上提供的示例吗?它有两个描述符meterfoot 作为成员,其中一个使用instance.meter 访问用户。我认为,从封装的角度来看,这应该是不可能的。我的理解正确吗?

标签: python python-3.x encapsulation descriptor magic-methods


【解决方案1】:

__get____set__ 不会收到有关谁在呼叫他们的信息。 3 个参数分别是描述符对象本身、正在访问其属性的对象以及对象的类型。

我认为最好的解决方法是举个例子。所以,这里有一个:

class Class:
    def descriptor(self):
        return

foo_instance = Foo()
method_object = foo_instance.descriptor

函数是描述符。当你访问一个对象的方法时,方法对象是通过找到实现该方法的函数并调用__get__来创建的。这里,

method_object = foo_instance.descriptor

调用descriptor.__get__(foo_instance, Foo) 来创建method_object__get__ 方法不接收有关谁调用它的信息,只接收执行其属性访问任务所需的信息。

【讨论】:

  • 在调用描述符时,Python 传入 just 实例和类。碰巧大多数__get__ 实现 都是方法,所以self 是由方法对象传入的。但原则上,您可以使用带有 __get__ function 的模块,它仍然可以用作描述符。
  • @MartijnPieters:嗯?你确定那是准确的吗?你能举个例子吗?我很确定 __get__ 遵循正常的特殊方法规则,只在类上查找等等,所以我认为模块级 __get__ 函数不会做任何事情。
  • __get____set__ 没有收到有关谁在呼叫他们的信息。 好的,但他们知道他们是谁。由于描述符只是类型(是吗?),他们不应该知道使用它们作为成员的类。你可以编写一个带有副作用的描述符,它会无意中覆盖类的其他属性。
  • @danijar:方法应该不知道它们是谁的方法吗?我想你可能会看到 instanceowner 参数比它们更神奇。描述符不会凭空提取上下文。当你访问一个属性时,属性访问机制找到描述符并告诉它,“嘿,这个类型的对象需要计算它的属性。”
  • @danijar:方法并不特别。其他描述符与方法一样“存在于类中”。事实上,你可以在类之外定义一个函数,在你的程序架构中途,将它分配给Class.method_name,它会像Class类的method_name方法一样无缝运行。
【解决方案2】:

描述符用于实现绑定行为;描述符需要上下文,即它们所作用的对象。

那个对象就是传入的instance对象。

注意,如果没有描述符,对对象的属性访问直接作用于对象属性(设置或删除时实例__dict__,否则类和基类属性也会被搜索)。

描述符允许您将访问完全委托给单独的对象,封装获取、设置和删除。但为了能够做到这一点,该对象需要访问上下文,即实例。因为获取属性通常也会搜索类及其基类,所以__get__ 描述符方法也传递给实例的类(owner)。

以函数为例。函数也是一个描述符,将它们绑定到一个实例会产生一个方法。一个类可以有任意数量的实例,但是在创建实例时在所有这些实例上存储绑定方法是没有意义的,这会很浪费。

相反,函数是动态绑定的;您在实例上查找函数名称,而是在类中找到该函数,并通过调用__get__ 将函数绑定到实例,返回一个方法对象。然后,此方法对象可以在调用时将实例传递给函数,从而产生 self 参数。

【讨论】:

  • 描述符本身不是一个类吗?我认为你应该从一个类型继承来改变它的绑定行为,而不是让使用类担心这个。
  • @danijar:是的,但它不是实例。它是一个可重用的对象;函数也是描述符,这就是方法绑定到实例的方式,但是函数可以根据需要绑定到任何实例。
  • 所以在我的示例中设置instance.name 将解析为peter.age.name 而不是peter.name?
  • 不,应该是peter.name这是设计使然
【解决方案3】:

描述符协议的一个例子是绑定方法。当您访问实例方法o.foo 时,您可以立即调用它或将其保存到变量中:a = o.foo。现在,当您调用a(x, y, z) 时,实例o 将作为第一个self 参数传递给foo

class C(object):
    def foo(self, x, y, z):
        print(self, x, y, z)
o = C()
a = o.foo
a(1, 2, 3)   # prints <C instance at 0x...> 1 2 3

这是因为函数实现了描述符协议;当您 __get__ 对象实例上的函数时,它会返回绑定方法,实例绑定到函数。

如果没有描述符协议允许访问对象实例,上述方法将无法工作。

【讨论】:

  • 伙计,每个人都举了同样的例子。也许我应该使用property
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-09
  • 1970-01-01
  • 2020-11-07
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 2018-06-16
相关资源
最近更新 更多