为什么python描述符__get__方法接受所有者类作为arg?

时间:2012-01-03 22:32:54

标签: python

为什么python descriptor中的__get__方法接受所有者类作为第三个参数?你能给出一个使用它的例子吗?

第一个参数(self)是不言而喻的,第二个参数(instances)在通常显示的描述符模式的上下文中是有意义的(如下),但我从未真正看到过第三个(owner)使用。有人可以解释用例是什么吗?

只是通过参考和促进答案,这是我见过的描述符的典型用法:

class Container(object):
    class ExampleDescriptor(object):
        def __get__(self, instance, owner):
            return instance._name 
        def __set__(self, instance, value):
            instance._name = value
    managed_attr = ExampleDescriptor()

鉴于instance.__class__可用,我所能想到的是显式传递类与直接从类而不是实例(ex Container.managed_attr)访问描述符有关。即使这样,我也不清楚在这种情况下__get__会做什么。

4 个答案:

答案 0 :(得分:9)

从类中而不是类的实例访问属性时使用

owner,在这种情况下instance将为None

在您的示例中,尝试print(Container.managed_attr)之类的内容会失败,因为instanceNone因此instance._name会引发AttributeError

您可以通过检查是否instance is None来改进此行为,它可能有助于记录或引发更有用的异常以了解描述符所属的类,因此owner属性。例如:

        def __get__(self, instance, owner):
            if instance is None:
                # special handling for Customer.managed_attr
            else:
                return instance._name 

答案 1 :(得分:2)

从课程中访问描述符时,instance将为None。如果您没有考虑到这种情况(因为您的示例代码没有),那么此时将发生错误。

在这种情况下应该做什么?无论什么是明智的。 ;)如果没有别的意义,你可以按照property的例子,从类中访问时返回描述符本身。

答案 2 :(得分:1)

是的,它被使用,以便描述符可以在访问Container.managed_attr时看到Container。您可以返回一些适合用例的对象,如使用描述符实现方法时的未绑定方法。

答案 3 :(得分:0)

我认为 Python 中 owner 方法的 __get__ 参数最著名的应用是 classmethod decorator。这是一个纯 Python 版本:

import types

class ClassMethod:
    "Emulate PyClassMethod_Type() in Objects/funcobject.c."

    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner=None):
        if instance is None and owner is None:
            raise TypeError("__get__(None, None) is invalid")
        if owner is None:
            owner = type(instance)
        if hasattr(self.f, "__get__"):
            return self.f.__get__(owner)
        return types.MethodType(self.f, owner)

多亏了 owner 参数,classmethod 不仅适用于从实例而且还适用于类的属性查找:

class A:

    @ClassMethod
    def name(cls):
        return cls.__name__


A().name()  # returns 'A' so attribute lookup from an instance works
A.name()    # returns 'A' so attribute lookup from a class works too