Python中Property内建函数深入理解

来源:互联网 发布:淘宝卖家刷信誉怎么刷 编辑:程序博客网 时间:2024/05/29 17:23

Property内建函数深入理解

先把python核心编程一句话搬过来:
属性是一种有用的特殊类型的描述符,它们是用来处理所有对实例属性的访问,其工作方式和我们前面说过的描述符相似,一般情况下,当你使用属性符号来处理一个实例属性时,其实你是在修改这个实例的__dict__ 属性。表面上看,property()访问和一般的属性访问没有什么区别,但是它使用了函数或者方法。property()内建函数有四个参数
property(fget=None, fset=None, fdel=None, doc=None)

四个参数都是函数,且在类声明的时候就已经调用了。

OK,还是云里雾里, 首先看一个简单例子

class T(object):    def __init__(self,x):            self.__x=x    def get_x(self):            return self.__x+1    x=property(get_x)
>>>t=T(2)>>>t.__dict__{'_T__x': 3}
>>>T.__dict__dict_proxy({'__module__': 'pro', 'get_x': <function get_x at 0x7f9864263668>, '__dict__': <attribute '__dict__' of 'T' objects>, 'x': <property object at 0x7f98642574c8>, '__weakref__': <attribute '__weakref__' of 'T' objects>, '__doc__': None, '__init__': <function __init__ at 0x7f98642635f0>})

如果这时候输入t.x 会产生什么样的结果呢?t.__dict__['_T__x']又是什么呢?

首先在t.__dict中,_T__x这个是什么呢?这就要说到python的私有变量 , python把两个下划线开头的变量当成私有变量,也就是在__init__(self,x)方法中的 self.__x ,私有变量在代码生成之前会自动转换为长格式,也就是说 __x 会转换为._T__x这种形式。所谓私有变量,希望外界不能随便访问,只能类中可以访问,所以t.__x是违法的,但是python提供一种访问机制,就是t._T__x,且这个出现在t.__dict__当中,说明在__init__方法中定义的属性是实例自定义属性。
顺便提下其他两种形式:
以单划线开头的,指定这个属性是私有的,并且不可以被import导入,也就是说当这个类被import后,不可以调用这个属性
前后都有双下划线,是系统自带变量/方法名

回到正事

>>>t._T__x3>>>t.x>4

第一个比较好理解,关于属性查找策略之前在我set和get方法那篇文章详细讲过,父类中没有_T__x这个属性,连__x这个属性都没有(是因为__init__方法中的属性,类不可以直接方法,必须要定义对应的实例).父类中没查到,就直接在自己的t.__dict__中查找,结果就找到了,OK,返回对应的value,就是3.

第二个当访问t.x的时候,先去访问父类的T.__dict__,之前讲过,只有x是数据描述符的时候,才能去返回__get__方法的结果,可以看到T的属性中有这x,这里注意了,'x': <property object at 0x7f98642574c8> 这个property 可以看成是一个特殊类型的描述符,与传统的数据描述符相比,最大的不同就是: 传统数据描述符必须要包含__set__ __get__ 方法,缺一不可,但是property就不同
property(fget=None, fset=None, fdel=None, doc=None)
四个参数可以可无,没有就None

所以访问t.x的时候,父类中有x属性,发现是一个property的描述符,且第一个参数是get_x, 就类似于数据描述符中转到__set__方法一样,转到get_x中得到属性,发现返回值是self.__x+1, 所以最后返回的是4.

如果尝试给t.x赋值的时候,会发生什么情况呢?

>>> t.x=100Traceback (most recent call last):  File "<stdin>", line 1, in <module>AttributeError: can't set attribute

会出错,原因就是property函数第二个参数为空。

同时存在fget, fset 参数的 property()

class Hidex(object):        def __init__(self,x):                self.x=x+1                self.y=2        def get_x(self):                return self.__y        def set_x(self,y):                self.__y=y+3        y=property(get_x,set_x)

查看一下

 >>>t = Hidex(20) >>> t.__dict__{'x': 21, '_Hidex__y': 5}

当去声明一个实例t的时候,__init__方法和 __set__方法都可以影响实例的自定义属性
其实我们发现:

>>> Hidex.__dict__dict_proxy({'set_x': <function set_x at 0x7f9cd2e8a6e0>, '__module__': 'property1', 'get_x': <function get_x at 0x7f9cd2e8a668>, '__dict__': <attribute '__dict__' of 'Hidex' objects>, 'y': <property object at 0x7f9cd2e7e4c8>, '__weakref__': <attribute '__weakref__' of 'Hidex' objects>, '__doc__': None, '__init__': <function __init__ at 0x7f9cd2e8a5f0>})

这时候已经发现y已经是一个描述符了,当在__init__方法中对y赋值后,发现y是一个数据描述符,2给y只是一个过渡,2作为value 传输到 set_x中,还会经过一个伪set,再添加到实例的自定义属性中去,

我们可以对t.y进行赋值

>>> t.y=10>>> t.__dict__{'x': 21, '_Hidex__y': 13}>>> Hidex.__dict__dict_proxy({'set_x': <function set_x at 0x7f9cd2e8a6e0>, '__module__': 'property1', 'get_x': <function get_x at 0x7f9cd2e8a668>, '__dict__': <attribute '__dict__' of 'Hidex' objects>, 'y': <property object at 0x7f9cd2e7e4c8>, '__weakref__': <attribute '__weakref__' of 'Hidex' objects>, '__doc__': None, '__init__': <function __init__ at 0x7f9cd2e8a5f0>})>>> t.y13

赋值的时候,就会调用set_x, 然后把t的属性_Hidex__y改为13.

分析下下面的代码:

class Hidex(object):        def __init__(self,x):                self.x=x+1                self.y=2        def get_x(self):                return self.__y        def set_x(self,z):                self.__y=z+3        z=6        z=property(get_x,set_x)

比如声明 t = Hidex(20) ,可以进行 t.z 的操作吗? 可以进行 t.z=30的操作吗?

>>> t=Hidex(20)>>> t.__dict__{'y': 2, 'x': 21}>>> t.zTraceback (most recent call last):  File "<stdin>", line 1, in <module>  File "property1.py", line 6, in get_x    return self.__yAttributeError: 'Hidex' object has no attribute '_Hidex__y'>>> t.z=20>>> t.__dict__{'y': 2, 'x': 21, '_Hidex__y': 23}>>> t.z23

为什么会出现上面的结果呢?
当声明一个实例t的时候,先进行初始化,声明了x,y 。当对t.z进行赋值的时候,根据赋值的策略,赋值首先是去父类中找这个属性. 恰好,发现了z 是一个property内建函数的实例,就会去调用里面的fset方法,也就是会产生个__y 的属性。

0 0
原创粉丝点击