私有属性禁止从类外部进行访问和设置,公有属性允许从类外部进行访问和设置。
装饰时传入的属性生成私有属性列表或公有属性列表。
在私有属性列表的禁止外部访问和设置,否则允许外部访问和设置。
不在公有属性列表的禁止外部访问和设置,否则允许外部访问和设置。
(1)描述:python的class语句内,开头有两个下划线,但结尾没有两个下划线的变量名,会自动在原始变量名开头加上单下划线和类名。
(2)用途:这样可以避免与同一层次中其它类创建的相同变量名相冲突。
详细内容参考之前介绍的《python类的伪私有属性__X》
描述
通过类装饰器将类属性装饰为私有或公有。
(1) 最外层定义一个访问控制函数accessCtrl,入参forbid为True则为私有属性;
(2) 次外层定义一个接受装饰类的函数onDecorator;
(3) 次外层函数主体定义一个委托类onInstance,拦截装饰类的属性访问和设置;
(4) 将装饰类实例赋值给装饰器实例属性self.__wrapped实现委托;
(5) 压缩变量__wrapped无法直接访问原变量名,因为自动变为_onInstance__wrapped;
(6) 通过__getattr__()拦截未定义属性的点号运算,即装饰类属性;
(7) 通过__setattr__()拦截全部属性的赋值运算;
(8) 在私有属性列表内的属性,禁止访问和设置,进行抛错处理;
(9) 非私有属性访问通过getattr()转到装饰类实例上返回;
(10) 非私有属性设置通过setattr()转到装饰类实例上设置;
(11) 装饰器实例属性设置,通过self.__dict__[attr]=val设置,避免循环;
示例
>>> trace=False
>>> def traceCall(*args):#跟踪调用
if trace:
print('['+','.join(map(str,args))+']')
>>> def accessCtrl(forbid):
def onDecorator(aCls):
class onInstance:
def __init__(self,*args,**kargs):
self.__wrapped=aCls(*args,**kargs)
def __getattr__(self,attr):
traceCall('getattr',attr)
if forbid(attr):
raise TypeError('禁止访问:'+attr)
else:
return getattr(self.__wrapped,attr)
def __setattr__(self,attr,value):
traceCall('setattr',attr,value)
# 压缩后的变量名为 _onInstance__wrapped
if attr=='_onInstance__wrapped':
self.__dict__[attr]=value
elif forbid(attr):
raise TypeError('禁止设置:'+attr)
else:
setattr(self.__wrapped,attr,value)
return onInstance
return onDecorator
>>> trace=True
描述
私有属性装饰器直接调用访问控制函数accessCtrl,来控制装饰类的私有属性。
装饰时传入的属性生成私有属性列表,访问和设置的属性在列表则为私有属性,否则为公有属性
私有属性装饰器定义
(1) 定义一个私有属性装饰器函数privateAttr;
(2) 入参*privates接收装饰类的私有属性;
(3) 函数主体调用accessCtrl函数,入参forbid为lambda定义的匿名函数;
执行过程
(1) @privateAttr(‘phone’)将privateAttr的返回值onDecorator绑定到Staff_Private变量,将入参’phone’保存到privates列表
(2) 调用Staff_Private(‘梯阅线条’,110)
即调用onDecorator->onInstance->__init__()
(3) self.__wrapped=aCls(*args,**kargs)触发__setattr__()
(4) __wrapped自动压缩为_onInstance. __wrapped,
触发self.__dict__[attr]=value,而self.__dict__的赋值不会触发__setattr__()
(5) s1.name触发__getattr__()后,调用forbid->lambda,接收入参name,不在privates列表里面,触发getattr(self.__wrapped,attr)获得装饰类实例属性name的值
(6) s1.name='tyxt.work’触发__setattr__(),调用forbid->bambda,不在privates列表里面,触发setattr(self.__wrapped,attr,value)设置装饰类实例属性name的值
(7) s1.phone触发__getattr__()后,调用forbid->lambda,接收入参phone,在privates列表里面,触发TypeError的错误;
(8) s1.phone=120触发__ setattr __()后,调用forbid->lambda,接收入参phone,在privates列表里面,触发TypeError的错误;
示例
>>> def privateAttr(*privates):
return accessCtrl(forbid=(lambda attr:attr in privates))
# privateAttr 示例
# 入参为私有属性
>>> @privateAttr('phone')
class Staff_Private:
def __init__(self,name,phone):
self.name=name
self.phone=phone
# __wrapped 压缩变量名自动变为 _onInstance__wrapped
>>> s1=Staff_Private('梯阅线条',110)
[setattr,_onInstance__wrapped,<__main__.Staff_Private object at 0x00000132016B29D0>]
>>> s1.name
[getattr,name]
'梯阅线条'
>>> s1.name='tyxt.work'
[setattr,name,tyxt.work]
>>> s1.name
[getattr,name]
'tyxt.work'
>>> s1.phone
[getattr,phone]
Traceback (most recent call last):
File "<pyshell#66>", line 1, in <module>
s1.phone
File "<pyshell#45>", line 9, in __getattr__
raise TypeError('禁止访问:'+attr)
TypeError: 禁止访问:phone
>>> s1.phone=120
[setattr,phone,120]
Traceback (most recent call last):
File "<pyshell#67>", line 1, in <module>
s1.phone=120
File "<pyshell#45>", line 17, in __setattr__
raise TypeError('禁止设置:'+attr)
TypeError: 禁止设置:phone
描述
公有属性装饰器直接调用访问控制函数accessCtrl,来控制装饰类的公有属性。
装饰时传入的属性生成公有属性列表,访问和设置的属性不在列表则为私有属性,否则为公有属性。
私有属性装饰器定义
(1) 定义一个私有属性装饰器函数publicAttr;
(2) 入参*publics接收装饰类的私有属性;
(3) 函数主体调用accessCtrl函数,入参forbid为lambda定义的匿名函数;
执行过程
(1) @publicAttr(‘name’)将publicAttr的返回值onDecorator绑定到Staff_Public变量,将入参’name’保存到publics列表
(2) 调用Staff_Public (‘梯阅线条’,110)
即调用onDecorator->onInstance->__init__()
(3) self.__wrapped=aCls(*args,****kargs)触发__setattr__()
(4) __wrapped自动压缩为_onInstance. __wrapped,
触发self.__dict__[attr]=value,而self.__dict__的赋值不会触发__setattr__()
(5) s1.name触发__getattr__()后,调用forbid->lambda,接收入参name,在publics列表里面,触发getattr(self.__wrapped,attr)获得装饰类实例属性name的值
(6) s1.name='tyxt.work’触发__setattr__(),调用forbid->bambda,在publics列表里面,触发setattr(self.__wrapped,attr,value)设置装饰类实例属性name的值
(7) s1.phone触发__getattr__()后,调用forbid->lambda,接收入参phone,不在publics列表里面,触发TypeError的错误;
(8) s1.phone=120触发__ setattr __()后,调用forbid->lambda,接收入参phone,不在publics列表里面,触发TypeError的错误;
示例
>>> def publicAttr(*publics):
return accessCtrl(forbid=(lambda attr:attr not in publics))
# publicAttr 示例
# 入参为公有属性,其他为私有属性
>>> @publicAttr('name')
class Staff_Public:
def __init__(self,name,phone):
self.name=name
self.phone=phone
>>> s1=Staff_Public('梯阅线条',110)
[setattr,_onInstance__wrapped,<__main__.Staff_Public object at 0x00000132016B2B20>]>>> s1.name
[getattr,name]
'梯阅线条'
>>> s1.name='tyxt.work'
[setattr,name,tyxt.work]
>>> s1.phone
[getattr,phone]
Traceback (most recent call last):
File "<pyshell#73>", line 1, in <module>
s1.phone
File "<pyshell#45>", line 9, in __getattr__
raise TypeError('禁止访问:'+attr)
TypeError: 禁止访问:phone
>>> s1.phone=120
[setattr,phone,120]
Traceback (most recent call last):
File "<pyshell#74>", line 1, in <module>
s1.phone=120
File "<pyshell#45>", line 17, in __setattr__
raise TypeError('禁止设置:'+attr)
TypeError: 禁止设置:phone