【问题标题】:Why do people default owner parameter to None in __get__?为什么人们在 __get__ 中默认所有者参数为 None?
【发布时间】:2015-07-25 12:53:54
【问题描述】:

我经常看到这种情况:

def __get__(self, instance, owner=None):

为什么有些人对owner参数使用默认值None

这甚至在Python docs中完成:

descr.__get__(self, obj, type=None) --> value

【问题讨论】:

  • 有点相关的问题:stackoverflow.com/q/8719585
  • @BasSwinckels 确实,但不幸的是,这没有提供关于我的问题的信息。
  • 不相关,但第三个参数不是“所有者”。这意味着所有者是描述符所在的对象。相反,它是调用描述符的类型。也就是说,如果描述符是通过子类访问的,那么第三个参数将是子类而不是父类(它“拥有”描述符)。
  • @Dunes 我猜人们只是不喜欢使用type 作为参数名称。我一直认为它是“拥有”实例(而不是描述符)的类,因此是所有者。
  • 啊,这个推理更有意义。不过,想想拥有一个实例的类仍然觉得有点奇怪。就个人而言,我使用objtype。没有歧义,也没有隐藏名称。

标签: python python-3.x descriptor python-descriptors


【解决方案1】:

因为所有者可以很容易地从实例派生,所以第二个参数是可选的。只有当没有实例可以从中派生所有者时,才需要所有者参数。

这在引入描述符的提案中有所描述,PEP 252 - Making Types Look More Like Classes

__get__: 一个可以用一个或两个参数调用的函数 从对象中检索属性值。这也是 称为“绑定”操作,因为它可能返回一个 “绑定方法”对象在方法描述符的情况下。这 第一个参数X 是属性必须来自的对象 被检索或必须绑定到的。 XNone 时, 可选的第二个参数,T,应该是元对象和 绑定操作可能会返回一个 unbound 方法,仅限于 T的实例

(我的粗体强调).

绑定,从第一天开始,就意味着只适用于实例,类型是可选的。例如,方法不需要它,因为它们可以单独绑定到实例:

>>> class Foo: pass
... 
>>> def bar(self): return self
... 
>>> foo = Foo()
>>> foo.bar = bar.__get__(foo)  # look ma! no class!
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x10a0c2710>>
>>> foo.bar()
<__main__.Foo object at 0x10a0c2710>

此外,第二个参数可以很容易地从第一个参数派生;见证 classmethod 仍然绑定到类,即使我们没有传递一个:

>>> classmethod(bar).__get__(foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> classmethod(bar).__get__(foo)()
<class '__main__.Foo'>

参数存在的唯一原因是支持绑定到类,例如当没有实例可以绑定时。又是类方法;绑定到None 因为实例不起作用,它只有在我们实际传入类时才起作用:

>>> classmethod(bar).__get__(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __get__(None, None) is invalid
>>> classmethod(bar).__get__(None, Foo)
<bound method type.bar of <class '__main__.Foo'>>

【讨论】:

  • 我的印象是 owner 参数始终是在其字典中实际找到描述符对象的祖先类,但在进一步的实验和查看源代码后,它似乎只是是type(X)。不像我想的那样,我们真的总是可以从实例中推断出所有者。
  • @user2357112:这会违背@classmethod 的目的,它承诺在子类化时将方法绑定到当前类,而不是定义它的类。
  • @Maggyero 这将导致无法使用在元类上定义的描述符。请注意,type 本身也是一个元类,因此您不能将 issubclass(instance, type) 用作后备(是的,type(type) is type 是 True,issubclass(type, type) isinstance(type, type) 也是如此)。
  • @Maggyero:2 个原因:__get__ 需要特例以支持classmethod,基本上,并且这将导致无法删除错误的描述符对象来自一个类型(例如descriptor.__delete__(None, cls) 抛出异常),因为classobject.__dict__ 是一个只读代理。如果必须挂钩设置或删除类的属性,则始终可以在元类上使用描述符,并且用例很少见。
  • @Maggyero:我不知道,这是我自己的解释。 __get__ 的另一个原因是特殊情况:Python 2 有未绑定和绑定的方法;未绑定方法仅在 isinstance(self, owner) 为真时才接受 self,并且这种关系需要从旧类到新类保留。
【解决方案2】:

这是执行此操作的标准方法;我见过的所有 Python 内置描述符都是这样做的,包括函数、属性、静态方法等。我知道在描述符​​协议中没有任何情况会在没有所有者参数的情况下调用 __get__,但是如果你想调用 @ 987654323@ 手动,不必传递所有者可能很有用。所有者参数通常没有多大作用。

例如,you might want 是一种为单个对象提供新方法的更简洁的方法。下面的装饰器清理了语法并让方法可以访问self

def method_of(instance):
    def method_adder(function):
        setattr(instance, function.__name__, function.__get__(instance))
        return function
    return method_adder

@method_of(a)
def foo(self, arg1, arg2):
    stuff()

现在a 有一个foo 方法。我们手动使用foo 函数的__get__ 方法来创建一个绑定的方法对象,就像其他任何方法一样,除了因为此方法不与类关联,我们没有传递__get__ 类。几乎唯一的区别是,当您打印方法对象时,您会看到 ?.foo 而不是 SomeClassName.foo

【讨论】:

  • 如果这是owner=None默认值背后的唯一原因,我认为它是没有用的。这些似乎是极其罕见的情况,我可以直接写function.__get__(instance, None)。在得出结论认为没有真的充分理由使用owner=None 而不仅仅是owner 之后,我接受了你的回答。
【解决方案3】:

因为这是指定描述符协议的方式:

descr.__get__(self, obj, type=None) --&gt; value

cfhttps://docs.python.org/2/howto/descriptor.html#descriptor-protocol

type 参数允许在查找描述符而不是实例时访问查找描述符所在的类。由于您可以从实例中获取类,因此在实例上查找描述符时它在某种程度上是多余的,因此它已成为可选的,以允许较少冗长的 desc.__get__(obj) 调用(而不是 desc.__get__(obj, type(obj)))。

【讨论】:

  • 我认为这仍然不能解释原因,为什么有人会这样做desc.__get__(obj)pastebin.com/YwNWXR7c 似乎所有者无论如何都被传递了,即使通过实例使用也是如此。
猜你喜欢
  • 2017-12-09
  • 1970-01-01
  • 2013-05-31
  • 2014-07-18
  • 2012-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多