Python的属性搜索
admin
2023-07-30 20:41:38
0

这是我总结的Python属性搜索的加长版,完整的描述了Python在做属性访问时的过程。
了解Python属性搜索的整个过程,有助于更深入地理解Python属性访问。


1. 对Python中属性和方法的看法

理解Python时有一点很重要:一切皆对象
为什么呢?
先看个例子:

>>> class Foo(object):
...    clsattr = \'this is class attribute\'
...    def clsmethod(self):
...        print \'this is method\'

在这个例子中,定义了class Foo
通常我们称clsattr属性clsmethod方法
这种叫法容易造成一种误会:认为对于类来说属性和方法是两种东西。
其实,由于在Python中一切皆对象,所以,拿上例来说,clsattrFoo的一个str类型对象,而clsmethodFoo的一个function类型对象。
也就是对于Foo来讲,clsattrclsmethod都是对象,除了类型不同,其他的没有什么不一样。
这是一个看法上的细微区别,但是对后面理解Python的属性搜索却很重要。

2. Python属性搜索的精简版

Python存在类对象和实例对象之分:

>>> class Foo(object):
...    clsattr = \'this is class attribute\'
...    def clsmethod(self):
...        print \'this is method\'
...
>>> f = Foo()

在这个例子中Foo是类对象,f是实例对象。
但是我们知道,在OOP的理论中,类是抽象描述,实例是其具象。Python支持OOP,所以,也能通过自己的方式来“模拟并实现” OOP的要求。
其中最重要的就是属性搜索规则。
当不考虑描述符/描述器的时候,可以将Python属性搜索精简如下:

  1. Python的属性搜索是按照继承树从下到上进行的。
  2. 继承树以类对象为中心,向上是其基类,向下是其实例对象。
  3. 当通过实例.属性的形式访问某属性时,首先查找实例对象自身是否存在该属性,如果存在,那么直接返回;如果不存在,那么向上查找,直到找到为止,否则返回异常。

还是拿上面的例子来说明:

>>> f.__dict__
{}
>>> Foo.__dict__
dict_proxy({\'__module__\': \'__main__\', \'clsattr\': \'clsattr\', \'__dict__\': , \'foo\': , \'__weakref__\': , \'__doc__\': None})

Python中对象通过__dict__内置属性来动态管理其所有属性。__dict__是一个字典,其内部为对象所有属性名与属性对象的对应关系。
可以看到clsattr在类对象Foo中存在,在实例对象f中不存在。
因此,当通过f.clsattr的形式访问属性clsattr的时候,首先,查找实例对象f,没有找到,所以继续向上查找,于是查找类对象Foo,找到,返回其值。

按照这个规则,如果f中存在clsattr属性,则不会去查找Foo,即:

>>> f.clsattr = \'this is an instance attr\'
>>> f.__dict__
{\'clsattr\': \'this is an instance attr\'}
>>> f.clsattr
\'this is an instance attr\'

OK,了解了精简版 的属性搜索规则,再来看看加长版 的规则。

3. Python属性搜索的加长版

正如上面提到的,在不考虑描述符/描述器的时候,才可以按照精简版的规则来,那么考虑到描述符的时候呢?
关于描述符就不详细展开了。简单的说,描述符就是一个实现了描述符协议 的类。
描述符协议包括__get____set____delete__方法。
其中,只实现了__get__方法的描述符称为non-data descriptor
而实现了__get____set__方法的描述符称为data descriptor

当把类的一个属性为描述符这种特殊的对象时,会发生一件神奇的事情:
Python在类的__dict__中找到的对象如果拥有__get__()方法,不会直接返回这个对象,而是返回其调用__get__()方法后的结果
注意:这里特别要强调是类的属性,不是实例的属性。
举例说明:

>>> class Foo(object):
...    clsattr = Descriptor()         # 假设Descriptor是自定义的描述符
...    def func(self):
...        print \'this is func\'
...

那么当调用Foo.__dict__[\'clsattr\']时,由于clsattr是描述符,所以,返回的是Foo.__dict__[\'clsattr\'].__get__(None, Foo)
这个时候,有趣的事情就来了,当调用函数funcFoo.__dict__[\'func\']时,返回的是Foo.__dict__[\'func\'].__get__(None, Foo), 在命令行中这是一个unbound method

所以,在Python中函数其实是一个描述符,只不过,由于描述符必须在通过属性访问的情况下才起作用,所以,并没有察觉。

看到这里,可能你已经恍然大悟:其实方法只是一个特殊的属性而已。

好了,理解到这里,就可以说说属性搜索加长版

  1. 当通过实例.属性访问一个属性时,首先,从其类对象开始向上查找,如果存在并且是data-descriptor,那么返回查找结果;否则,进行步骤2
  2. 在实例中查找属性是否存在,如果存在,则返回其值,否则,进行步骤3
  3. 向上查找,直到找到,返回其值,否则,返回异常

由于描述符只能是类属性,所以,可以有以下结论:

  1. data descriptor对于同名的实例属性(非描述器)有屏蔽作用:
    例如:
#!/usr/bin/env python
# -*- conding: utf-8 -*-
class RevealAccess(object):
    \"\"\"A data descriptor that sets and returns values
       normally and prints a message logging their access.
    \"\"\"

    def __init__(self, initval=None, name=\'var\'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print \'Retrieving\', self.name, self.val
        return self.val

    def __set__(self, obj, val):
        print \'Updating\', self.name, self.val 

class Foo(object):
    attr = RevealAccess(10, \'var \"x\"\')      #设置类属性attr为描述符
    def __init__(self):
        self.attr = 20        #调用描述符的__set__方法,而不是为实例设置属性

f = Foo()
print f.attr

执行结果如下:

Updating var \"x\" 10
Retrieving var \"x\" 10
10

2.non-data descriptor对于同名的属性,会被屏蔽掉。这一点与正常的类属性相同。

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Prometheus+Graf... 一,Prometheus概述 1,什么是Prometheus?Prometheus是最初在Sound...
python绘图库Matplo... 本文简单介绍了Python绘图库Matplotlib的安装,简介如下: matplotlib是pyt...