Python装饰器泛化公有和私有属性

1 Python装饰器泛化公有和私有属性

私有属性禁止从类外部进行访问和设置,公有属性允许从类外部进行访问和设置。

装饰时传入的属性生成私有属性列表或公有属性列表。

在私有属性列表的禁止外部访问和设置,否则允许外部访问和设置。

不在公有属性列表的禁止外部访问和设置,否则允许外部访问和设置。

1.1 关于变量名压缩__X

(1)描述:python的class语句内,开头有两个下划线,但结尾没有两个下划线的变量名,会自动在原始变量名开头加上单下划线和类名。

(2)用途:这样可以避免与同一层次中其它类创建的相同变量名相冲突。

详细内容参考之前介绍的《python类的伪私有属性__X》

1.2 访问控制函数

描述

通过类装饰器将类属性装饰为私有或公有。

(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

1.3 私有属性装饰器

描述

私有属性装饰器直接调用访问控制函数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 "", line 1, in <module>
    s1.phone
  File "", line 9, in __getattr__
    raise TypeError('禁止访问:'+attr)
TypeError: 禁止访问:phone
>>> s1.phone=120
[setattr,phone,120]
Traceback (most recent call last):
  File "", line 1, in <module>
    s1.phone=120
  File "", line 17, in __setattr__
    raise TypeError('禁止设置:'+attr)
TypeError: 禁止设置:phone

1.4 公有属性属性示例

描述

公有属性装饰器直接调用访问控制函数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 "", line 1, in <module>
    s1.phone
  File "", line 9, in __getattr__
    raise TypeError('禁止访问:'+attr)
TypeError: 禁止访问:phone
>>> s1.phone=120
[setattr,phone,120]
Traceback (most recent call last):
  File "", line 1, in <module>
    s1.phone=120
  File "", line 17, in __setattr__
    raise TypeError('禁止设置:'+attr)
TypeError: 禁止设置:phone

你可能感兴趣的:(python,python)