前置认识
'''
学习用python的高级特性2:描述器descriptor
'''
# 1 example
class Ten:
def __get__(self, instance, owner):
return 10
class A:
x = 5
y = Ten()
print(A.x, A.y)
#描述器A.y和属性差别就是 直接调用了 __get__; 相当于每次.()引用属性就调用get, call方法?
# 2 listdir, 统计数目的get函数
import os
class DirectorySize:
def __get__(self, instance, owner):
return len(os.listdir(instance.dirname))
class Directory:
size = DirectorySize()
def __init__(self, dirname):
self.dirname = dirname
s = Directory('../')
g = Directory('../DeepPython')
print(s.size, g.size)
#从输出看,这个--get--三个入参含义是self为DirectorySize实例,instance是调用者实例(s,g), owner是类Directory
‘’‘
output:
5 10
13 3
‘’’
- 利用了类的自动调用get机制,完成某些计算、存储、读写任务在专门类中,实现类似于java那种面向对象的“封装机制”或者说python中更像的“外包机制”;
- 装饰器是由别人替我完成边边角角工作,而描述器更像替我完成核心、专业化的工作。
描述器举例之一:托管
# 3 描述器目标是提供一个hook钩子,将对象存储在类变量中,控制属性查找时间(. dot引发);
# example:托管属性,descriptor作为托管器,创建私有的属性,功其它类调用;需要get、set属性来自动化读写
import logging
logging.basicConfig(level = logging.INFO)
class Trusteeship:
def __get__(self, instance, owner):
value = instance._age
logging.info('Access %r get %r','age', value) #%r means cannoical string representation of object
return value
def __set__(self, instance, value):
logging.info('Update age as %r', value)
instance._age = value
class DesEx:
age = Trusteeship() # call descriptor
def __init__(self, name, age):
self.age = age #写了一次,call get()
self.name = name
def incAge(self):
self.age = self.age + 1 #读写各一次,call get(),set()
alice = DesEx('alice', 23)
alice.incAge()
'''
Output:
5 10
13 3
INFO:root:Update age as 23
>>alice.incAge()
INFO:root:Access 'age' get 23
INFO:root:Update age as 24
'''
- 显然,敏感的“年龄”数据告诉Trusteesheet管理,person类本身可以不知道,不碰同时完成年龄增加、年龄获取任务。
- 关键方法是: get(), set()一对,每次读都调用get,每次写都调用set(比如等式左边就是写,右边就是读,自加需读写都要);内部默认参量self,instance和owner分别表示托管描述器本身self(name,age属性,乃至下面的public_name属性都是self这边建立。)instance表示调用托管描述器的实例,owner是实例的类对象
描述器:属性托管的推广到一般属性
# 4 托管任意属性,制定init方法,设置getattr,setattr,
class TrusteesheetA:
def __set_name__(self, owner, name):
self.pubblic_name = name #public_name只是一个对外展示(存储了初始的名字,其实没有真正赋值或者刷新
self.private_name = '_' + name
def __get__(self, instance, owner):
value = getattr(instance, self.private_name)
logging.info('New Trusteesheet access %r, get %r', self.pubblic_name, value)
return value
def __set__(self, instance, value):
setattr(instance, self.private_name, value)
logging.info('Updating %r to %r', self.pubblic_name, value)
class Person:
name = TrusteesheetA() # set_name
age = TrusteesheetA()
def __init__(self, name, age):
self.name = name #set once
self.age = age #set
def birthday(self):
self.age = self.age + 1 # get,set
def changeName(self):
self.name = self.name + ' Karl'
bob = Person('bob',30)
bob.birthday()
bob.changeName()
'''
output:
INFO:root:Updating 'name' to 'bob'
INFO:root:Updating 'age' to 30
INFO:root:New Trusteesheet access 'age', get 30
INFO:root:Updating 'age' to 31
INFO:root:New Trusteesheet access 'name', get 'bob'
INFO:root:Updating 'name' to 'bob Karl'
'''
- set_name用于初次调用,保存属性对外名称 self.public_name, 对内名称为self.private_name 分离操作!