python描述符理解

Python中的描述符是一个相对底层的概念

descriptor
Any object which defines the methods get(), set(), or delete(). When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, using a.b to get, set or delete an attribute looks up the object named b in the class dictionary for a, but if b is a descriptor, the respective descriptor method gets called. Understanding descriptors is a key to a deep understanding of Python because they are the basis for many features including functions, methods, properties, class methods, static methods, and reference to super classes. For more information about descriptors’ methods, see Implementing Descriptors.

描述符
任何实现了__get__(), __set__(), 或者 __delete__()方法的对象就是描述符。一个class的属性是一个描述符的时候,对这个属性的访问会触发特定的绑定行为。一般的我们使用a.b的方式访问,修改和删除属性,它通过查找class的字典来访问属性b,当时如果b是一个描述符,那么get,set和delete相关的描述符方法会被调用。理解描述符是深入理解python的关键,因为描述符是很多特性实现的基础,比如:方法,函数,属性,类方法,静态方法还有对父类方法的引用。详细的说明请看描述符的实现。--CooMark译

极客学院 - 描述符
Python方法绑定——Unbound/Bound method object的一些梳理
http://www.it165.net/pro/html/201406/15171.html - 最后总结的很好

在说描述符之前,先看一小段代码:

class Foo(object):
    """docstring for Foo"""

    def __init__(self, arg=''):
        super(Foo, self).__init__()
        self.arg = arg

    def foo(self):
        print(self)
        print('foo:', 123)


print(Foo.foo)

print(Foo().foo)

print(Foo.foo.__get__)

# 输出
# <function Foo.foo at 0x00550738>
# <bound method Foo.foo of <__main__.Foo object at 0x0054DB10>>
# <method-wrapper '__get__' of function object at 0x00550738>

主要想说的是这个__get__,他是一个method-wrapper。每次调用一个方法,其实都是调用这个__get__构造的method对象。我们定义的方法其实都是descriptor,它通过__get__控制了对相应method的访问

def __get__(self, instance, owner)
Foo.foo -- Foo.__dict__['foo'].__get__(None,Foo) # 隐式传递的self是Foo class对象, 一切皆对象,class在模块级别中也有instance

下面三种方式的调用输出的结果是一样的,这个None咋来的呢?,有一点可以确认:self都是class对象实例

# 两种访问方式是一样的
print(Foo.foo.__get__(Foo(), Foo)())
print(Foo.__dict__['foo'].__get__(Foo(), Foo)())

c = Foo()
print(Foo.foo.__get__(c, Foo)())

# <__main__.Foo object at 0x0032DB10>
# foo: 123
# None
# <__main__.Foo object at 0x0032DB10>
# foo: 123
# None
# <__main__.Foo object at 0x0032DB70>
# foo: 123
# None

再说说bound method和unbound method

区分依据就是是否给method绑定了实例对象,也就是调用的时候是否会隐式传递self参数

print(Foo.foo)
# 当时3.0之后不再叫unbound method,因为定义为function更贴切
# <function Foo.foo at 0x006406F0>

print(Foo().foo)
# <bound method Foo.foo of <__main__.Foo object at 0x0063DB30>>

描述符

我们定义的属性,方法其实都是描述符,只不过我们习以为常,而没有刻意的去了解背后的机制

class Bar(object):
    """docstring for Bar"""
    _name = 'Mark Xiao'

    def __init__(self, arg=''):
        super(Bar, self).__init__()
        self.arg = arg
        self._name = 'CooMark'

    def _get_name(self):
        return self._name

    def _set_name(self, value):
        self._name = value

    def _del_name(self):
        del self._name

    name = property(_get_name, _set_name, _del_name, 'description of property name')

print(Bar.name)
print(Bar().name)

# <property object at 0x004FBAE0>
# CooMark

描述符协议:

__get__(self, instance, owner) --> return value
__set__(self, instance, value)
__delete__(self, instance)

描述符对象以类型 (owner class) 成员的方式出现,且最少要实现一个协议方法。最常见的描述符有 property、staticmethod、classsmethod。访问描述符类型成员时,解释器会自动调用与行为相对应的协议方法。

  1. 实现 get 和 set 方法,称为 data descriptor。
  2. 仅有 get 方法的,称为 non-data descriptor。
  3. get 对 owner_class、owner_instance 访问有效。
  4. set、delete 仅对 owner_instance 访问有效。

instance method, class method, static method

实例方法bound到instance
类方法bound到class
静态方法没有绑定,仅仅是个方法

总结

描述符是一种协议,实现了相应的描述符方法便会有相应的描述符行为,property就是一个特定的描述符类型,我们可以自己实现类似的功能

原文地址:https://www.cnblogs.com/wancy86/p/descriptor.html