(一)内置函数补充
1、isinstance(obj,cls)
检查obj是否是类 cls 的对象,如果cls有父类,也是其父类的实例
class Foo(object): pass obj = Foo() isinstance(obj, Foo) # True isinstance(obj, object) # True
2、issubclass(sub,super)
检查sub类是否是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass print(issubclass(Bar, Foo)) # True print(issubclass(Bar, object)) # True
(二)反射
1、反射的概念
- 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
- 这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2、python面向对象中的反射
- 通过字符串的形式操作对象相关的属性。
- python中的一切事物都是对象(都可以使用反射)
3、实现反射的四个函数
适用于类和对象(一切皆对象,类本身也是一个对象)
3.1 hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性
3.2 getattr(object, name, default=None)
获取object中name字符串对应的方法或属性,如果该方法或属性不存在,指定了default的值则返回此值,未指定default的值则抛出异常
3.3 setattr(x, y, v)
设置x中y字符串对应的方法或属性的value,相当于x.y=v
3.4 delattr(x, y)
从x中删除 y字符串对应的方法或属性,相当于del x.y,不存在,则报错
4、反射例子
class BlackMedium:
feature='Ugly'
def __init__(self,name,addr):
self.name=name
self.addr=addr
def sell_house(self):
print('%s 黑中介卖房子' %self.name)
def rent_house(self):
print('%s 黑中介租房子' %self.name)
b1=BlackMedium('万成置地','回龙观天露园')
#检测是否含有某属性
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_house'))
#获取属性
n=getattr(b1,'name')
print(n)
func=getattr(b1,'rent_house')
func()
# getattr(b1,'aaaaaaaa') #报错
print(getattr(b1,'aaaaaaaa','不存在啊'))
#设置属性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.show_name(b1))
#删除属性
delattr(b1,'addr')
delattr(b1,'show_name')
delattr(b1,'show_name111')#不存在,则报错
print(b1.__dict__)
5、类也是对象
class Foo(object):
staticField = "old boy"
def __init__(self):
self.name = 'wupeiqi'
def func(self):
return 'func'
@staticmethod
def bar():
return 'bar'
print getattr(Foo, 'staticField')
print getattr(Foo, 'func')
print getattr(Foo, 'bar')
6、反射当前模块成员
import sys
def s1():
print 's1'
def s2():
print 's2'
x=111
# 获取当前模块
this_module = sys.modules[__name__]
current_module=__import__(__name__)
print(hasattr(current_module,"x"))
print(getattr(current_module,"x"))
hasattr(this_module, 's1')
getattr(this_module, 's2')
7、导入其他模块,利用反射查找该模块是否存在某个方法
"""
程序目录:
module_test.py
index.py
"""
# 导入模块:
def test():
print('from the test')
# 当前文件:
index.py
"""
import module_test as obj
#obj.test()
print(hasattr(obj,'test')) # True
getattr(obj,'test')() # from the test
8、反射的好处
- 实现可插拔机制:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即事先把主要的逻辑写好(只定义接口),后期再去实现接口的功能
# 还没有实现全部功能的接口 class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr # 不影响逻辑代码编写 #from module import FtpClient f1=FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->不存在此方法') print('处理其他的逻辑')
- 动态导入模块(基于反射当前模块成员)
9、__setattr__,__delattr__,__getattr__
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 #应该使用它
def __delattr__(self, item):
print('----> from delattr')
# del self.item #无限递归了
self.__dict__.pop(item)
#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)
#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx
(三)二次加工标准类型(包装)
1、包装
python提供了标准数据类型,以及丰富的内置方法,但在一些场景下需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
1.1 二次加工标准类型(基于继承实现)
class List(list): # 继承list所有的属性,也可以派生出自己新的,比如append和mid
def append(self, p_object):
' 派生自己的append:加上类型检查'
if not isinstance(p_object,int):
raise TypeError('must be int')
super().append(p_object)
@property
def mid(self):
'新增自己的属性'
index=len(self)//2
return self[index]
l=List([1,2,3,4])
print(l) # [1, 2, 3, 4]
l.append(5)
print(l) # [1, 2, 3, 4, 5]
# l.append('1111111') #报错,必须为int类型
print(l.mid) # 3
#其余的方法都继承list的
l.insert(0,-123)
print(l) # [-123, 1, 2, 3, 4, 5]
l.clear()
print(l) # []
1.2 练习(clear加权限限制)
class List(list):
def __init__(self,item,tag=False):
super().__init__(item)
self.tag=tag
def append(self, p_object):
if not isinstance(p_object,str):
raise TypeError
super().append(p_object)
def clear(self):
if not self.tag:
raise PermissionError
super().clear()
l=List([1,2,3],False)
print(l) # [1, 2, 3]
print(l.tag) # False
l.append('saf')
print(l) # [1, 2, 3, 'saf']
# l.clear() # 异常
l.tag=True
l.clear()
print(l) # []
2、授权
- 授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。
- 授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
- 实现授权的关键点就是覆盖__getattr__方法。
2.1 授权示范一
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 %T')
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()) # 2024-01-19 17:20:12 你好啊
f1.close()
2.2 授权示范二
#我们来加上b模式支持
import time
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
if 'b' in mode:
self.file=open(filename,mode)
else:
self.file=open(filename,mode,encoding=encoding)
self.filename=filename
self.mode=mode
self.encoding=encoding
def write(self,line):
if 'b' in self.mode:
if not isinstance(line,bytes):
raise TypeError('must be bytes')
self.file.write(line)
def __getattr__(self, item):
return getattr(self.file,item)
def __str__(self):
if 'b' in self.mode:
res="<_io.BufferedReader name='%s'>" %self.filename
else:
res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding)
return res
f1=FileHandle('b.txt','wb')
# f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气
f1.write('你好啊'.encode('utf-8'))
print(f1) # <_io.BufferedReader name='b.txt'>
f1.close()
2.3 练习题(授权)
#练习一
class List:
def __init__(self,seq):
self.seq=seq
def append(self, p_object):
' 派生自己的append加上类型检查,覆盖原有的append'
if not isinstance(p_object,int):
raise TypeError('must be int')
self.seq.append(p_object)
@property
def mid(self):
'新增自己的方法'
index=len(self.seq)//2
return self.seq[index]
def __getattr__(self, item):
return getattr(self.seq,item)
def __str__(self):
return str(self.seq)
l=List([1,2,3])
print(l) # [1, 2, 3]
l.append(4)
print(l) # [1, 2, 3, 4]
# l.append('3333333') #报错,必须为int类型
print(l.mid) # 3
#基于授权,获得insert方法
l.insert(0,-123)
print(l) # [-123, 1, 2, 3, 4]
#练习二
class List:
def __init__(self,seq,permission=False):
self.seq=seq
self.permission=permission
def clear(self):
if not self.permission:
raise PermissionError('not allow the operation')
self.seq.clear()
def __getattr__(self, item):
return getattr(self.seq,item)
def __str__(self):
return str(self.seq)
l=List([1,2,3])
# l.clear() # 此时没有权限,抛出异常
l.permission=True
print(l) # [1, 2, 3]
l.clear()
print(l) # []
#基于授权,获得insert方法
l.insert(0,-123)
print(l) # [-123]
(四)__getattribute__
对象调用属性时触发(不管存不存在都会触发)
- __getattr__(self,item):其实是在__getattribute__里抛出AttributeError异常时触发
- __getattribute__中要返回获取的属性值,否则用点调用属性并不会真正获取到属性值,而得到None
1、回顾__getattr__
class Foo:
def __init__(self,x):
self.x=x
def __getattr__(self, item):
print('执行的是我')
# return self.__dict__[item]
f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__
2、__getattribute__
class Foo:
def __init__(self,x):
self.x=x
def __getattribute__(self, item):
print('不管是否存在,我都会执行')
f1=Foo(10)
print(f1.x)
print(f1.xxxxxx)
# 输出结果:
不管是否存在,我都会执行
None
不管是否存在,我都会执行
None
3、二者同时出现
class Foo:
def __init__(self,x):
self.x=x
def __getattr__(self, item):
print('执行的是我')
# return self.__dict__[item]
def __getattribute__(self, item):
print('不管是否存在,我都会执行')
raise AttributeError('哈哈')
f1=Foo(10)
f1.x
f1.xxxxxx
# 当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
# 输出结果:
不管是否存在,我都会执行
执行的是我
不管是否存在,我都会执行
执行的是我
(五)描述符(__get__,__set__,__delete__)
1、描述符的概念
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
- __get__():调用一个属性时,触发
- __set__():为一个属性赋值时,触发
- __delete__():采用del删除属性时,触发
2、定义描述符
class Foo: # 在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
3、描述符的作用
用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中) ,描述符类产生的实例进行属性操作并不会触发三个方法的执行
class Foo:
def __get__(self, instance, owner):
print('触发get')
def __set__(self, instance, value):
print('触发set')
def __delete__(self, instance):
print('触发delete')
#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1=Foo()
f1.name='egon'
f1.name
del f1.name
#疑问:何时,何地,会触发这三个方法的执行
# 描述符应用之何时?何地?
#描述符Str
class Str:
def __get__(self, instance, owner):
print('Str调用')
def __set__(self, instance, value):
print('Str设置...')
def __delete__(self, instance):
print('Str删除...')
#描述符Int
class Int:
def __get__(self, instance, owner):
print('Int调用')
def __set__(self, instance, value):
print('Int设置...')
def __delete__(self, instance):
print('Int删除...')
class People:
name=Str()
age=Int()
def __init__(self,name,age): #name被Str类代理,age被Int类代理,
self.name=name
self.age=age
#何地?:定义成另外一个类的类属性
#何时?:且看下列演示
p1=People('alex',18) # Str设置... Int设置...
#描述符Str的使用
p1.name # Str调用
p1.name='egon' # Str设置...
del p1.name # Str删除...
#描述符Int的使用
p1.age # Int调用
p1.age=18 # Int设置...
del p1.age # Int删除...
#我们来瞅瞅到底发生了什么
print(p1.__dict__) # {}
print(People.__dict__) # {'__module__': '__main__', 'name': <__main__.Str object at 0x00000207FD9B3390>, 'age': <__main__.Int object at 0x00000207FD9DDDD8>, '__init__': <function People.__init__ at 0x00000207FF61D048>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
#补充
print(type(p1) == People) # True,type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__) # True
4、描述符分类
4.1 数据描述符
至少实现了__get__()和__set__()
class Foo: def __set__(self, instance, value): print(&