python渐进-类4

13.6 私有性

python没有关键字可以定义一个类属性的私有性。任何类属性都是公开的。

在使用上,如果一个属性以_开头,比如说_a,那么就会被认为是一个私有的变量。这样并不能保证这个变量不被其它代码块访问,只能靠程序员的自觉。

如果一个属性以__开头,并且没有以__结束。那么python会自动给它改名。比如一个C类的__a属性,python会自动把它改名为_C__a。这样继承C类的子类就无法通过 子类.__a的方式访问到C类的属性。这个在一定程度上保护了父类的属性,不被子类的覆盖。

即使没有私有性的关键字,python也可以对类属性的访问进行控制。有三种途径可以控制访问,一是重写类的__getattribute__()和__getattr__()方法;二是使用property;三是使用descriptor。

在访问类属性的时候,python都会调用__getattribute__;如果类属性不存在,python还会调用__getattr__。因此重写这两个方法,可以拦截所有的属性访问,从而定制类属性访问权限。

class C(object): a=4 b=9 def __getattribute__(self,name): if name=='a': raise AttributeError else: return object.__getattribute__(self, name) def __getattr__(self,name): print("no attribute "+name)c=C()c.ac.bc.c

上面的代码中,访问a属性会引发AttributeError,而访问其他的属性则返回它的值。c属性因为并没有定义,所以也会出现AttributeError。有AttributeError,就会调用__getattr__方法,上面的代码运行结果为:

ano attribute abcno attribute c

重写__getattribute__和__getattr__如果处理得不好,很容易引发无限的递归。因为在__getattribuite__里面使用self.name访问属性的时候,又会调用__getattribute__方法,造成无限的循环。所以上面的代码使用了object.__getattribute__(self, name)来返回值。

如果想要控制对某个属性的访问,可以使用property内置函数。

property内置函数读入四个变量,fget读取函数、fset赋值函数,fdel删除函数、fdoc文档。并且返回一个句柄。这个句柄是一个类或实例属性的白手套。任何对这个句柄的访问实际上是访问了另外一个类或者实例属性。

class P(object): def __init__(self): self._h=5 def wget(self): return self._h def wset(self,value): self._h=value w=property(wget,wset,None,None)p=P()print(p.w)p.w=9print(p.w)

上面的代码中,任何对w的读取和设置,都会转给wget和wset方法。这两个方法操作的变量其实是self._h

这段代码运行结果为

59

property的功能可以使用修饰符进行。演示代码如下:

class T(object): def __init__(self): self._h=3 @property def w(self): return self._h @w.setter def w(self,value): self._h=valuet=T()print(t.w)t.w=6print(t.w)

上面的代码先使用progerty返回一个拥有读取函数的property对象,然后再使用这个property对象的setter方法来修饰一个赋值函数。这里所有的读取函数和赋值函数都用的同一个函数名字w,不然会出错。

property在做读写操作控制的时候,并没有封装被操作的变量。像上面的property对象w,就没有把_h封装到自己的定义域里面。使用descriptor的话,可以把数据都封装进去。

有着__get__、__set__、__delete__其中一个方法的类就可以作为一个descriptor。读取这个类实例的时候会调用__get__,给实例赋值会调用__set__,删除这个实例的时候会调用__delete__。演示代码如下:

class D(object):

def __init__(self):

self._h=6

def __get__(self, obj, type=None):

return self._h

def __set__(self, obj, value):

self._h=value

class U(object):

d=D()

u=U()

print(u.d)

u.d=2

print(u.d)

13.7 小结

类的知识点纷繁复杂,横七竖八的。实际上有很多知识点都属于设计模式的内容,python硬是把它们都当作背后的机制固定下来了。真正和语法相关的反而不是太多。如果不是要做大型的代码工程或者想要看懂别人的代码,只是小规模应用的话,知道怎么定义类,实例化一个类,读写实例变量,调用实例方法,懂得使用继承。基本上就能达到复用代码的目的了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180628G1XDSM00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券