23.1、包装介绍:
1、什么是包装(继承+派生):
python为大家提供了标准数据类型以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,
新增/改写方法,这就用到了继承/派生知识,标准类型均可以通过包装的方式进行二次加工。
2、授权:
授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有产品的功能,其它的则
保持原样。授权的过程即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖 __getattr__ 方法。
3、包装时用到的类方法:
(1)__getattr__
在调用对象属性且对象属性不存在的时候才会触发它的执行
(2)__setattr__
在添加或修改对象属性的时候会触发它的执行
(3)__delattr__
在删除对象属性的时候会触发它的执行
(4)补充:已上函数方法默认在类中是自带的,如果在类中声明会自动覆盖类中默认设置;
23.2、__getattr__、setattr、__delattr__ 示例:
1、基础:
class Foo:
x=1
def __init__(self,y):
self.y=y
# 在调用对象属性且对象属性不存在的时候才会触发它的执行
def __getattr__(self, item):
print('----> from getattr:你找的属性不存在')
# 在添加或修改对象属性的时候会触发它的执行
def __setattr__(self, key, value):
print('----> from setattr')
# self.key=value
# 这样赋值会无限递归,因为每当对象赋值时都会调用该函数,这样实际上又是
# 在为对象赋值,又会调用该函数,所以会无限的递归下去。
self.__dict__[key]=value
# 正确的赋值方法是直接操作对象的属性字典。
# 因为重写了 __setattr__ 如果不写,那么对象就会无法赋值;
# 在删除对象属性的时候会触发它的执行
def __delattr__(self, item):
print('----> from delattr')
# del self.item
# 这样赋值会无限递归,和 __setattr__ 原理一致。
self.__dict__.pop(item)
# 正确的赋值方法是直接操作对象的属性字典。
#__setattr__
f1=Foo(10)
# ----> from setattr
f1.z=3
# ----> from setattr
print(f1.__dict__)
# {'y': 10, 'z': 3}
# __delattr__
del f1.z
# ----> from delattr
print(f1.__dict__)
# {'y': 10}
# __getattr__
print(f1.x)
# 1
f1.xxxxxx
# ----> from getattr:你找的属性不存在
2、限制对象输入的类型:
class Foo:
def __init__(self, name,age):
self.name = name
self.age=age
def __getattr__(self, item):
print('你找的属性【%s】不存在' % item)
def __setattr__(self, key, value):
if type(value) is str:
self.__dict__[key] = value.upper()
else:
print('[%s]属性必须是字符串类型' %key)
def __delattr__(self, item):
print('不允许删除属性【%s】' % item)
f1 = Foo('lc','19')
# __setattr__
f1.age=22
# [age]属性必须是字符串类型
print(f1.__dict__)
# {'name': 'LC', 'age': '19'}
# __getattr__
f1.slary
# 你找的属性【slary】不存在
print(f1.__dict__)
# {'name': 'LC', 'age': '19'}
# __delattr__
del f1.age
# 不允许删除属性【age】
print(f1.__dict__)
# {'name': 'LC', 'age': '19'}
23.3、包装示例:
1、包装:
class List(list):
# 继承list所有的属性,也可以派生出自己新的,比如append和mid
def append(self, p_object):
'派生自己的append:加上类型检查'
if isinstance(p_object, int):
super(List,self).append(p_object)
else:
print(TypeError('must be int'))
@property
def mid(self):
'新增自己的属性'
index = len(self) // 2
return self[index]
l = List([1,2,3,4])
# 因为新定义的List类继承了list类,所以可以使用List类直接进行初始化
print(l)
# [1, 2, 3, 4]
l.append(5)
print(l)
# [1, 2, 3, 4, 5]
l.append('1')
# must be int
print(l.mid)
# 3
# 其余的方法都继承list的
l.insert(0, 'lc')
print(l)
# ['lc', 1, 2, 3, 4, 5]
l.clear()
print(l)
# []
2、授权:
import time
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
self.file=open(filename,mode,encoding=encoding)
def write(self,line):
t=time.strftime('%Y-%m-%d %X')
self.file.write('%s %s' %(t,line))
def __getattr__(self, item):
return getattr(self.file,item)
f1=FileHandle('b.txt','w+')
f1.write('你好啊')
f1.seek(0)
print(f1.read())
f1.close()