面向对象进阶
isinstance(obj,cls) 检查obj是否是cls的实例
issubclass(obj,cls) 检查obj是否是cls的继承类
反射
通过字符串的形式操作对象的相关的属性。
主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)
hasattr(obj,name) 检查obj下是否有叫name属性,name必须字符串
getattr(obj,name,default=None) 查询obj下的属性,有返回name对应的参数,没有返回default
setattr(obj,x,y) 给obj设置属性,x必须字符串
delattr(obj,x) 删除obj下的属性,x必须字符串
示例:
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__)
反射的好处
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,
但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,
等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,
这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
好处二:动态导入模块(基于反射当前模块成员)
在模块中导入自己:
this_modul=sys.modules[__name__]
使用字符串导入模块:
1、__import__("time")
2、import importlib
importlib.import_module("time")
推荐使用第二种
setattr,getattr,delattr 操作属性,在类中的用法和意义
setattr
设置属性的时候会触发。实质是操作类.__dict__
delattr
删除类的属性时会触发,实质是操作类.__dict__
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
setitem,getitem,__delitem__的使用方法
作用同上。只不过是调用的时候__setattr__那中方法是用 . 去调用。而这种是用["值"]的方式去调用。
class Foo:
x = 1
def __init__(self, y):
self.y = y
def __getitem__(self, item): #执行对象[属性],找不到属性的时候会执行这个代码。
print('----> from getattr:你找的属性不存在')
def __setitem__(self, key, value): #执行对象[属性]=值,的时候会执行这个代码
self.__dict__[key]=value
def __delitem__(self, item): #执行 del 对象[属性] 的时候会执行这个代码。
self.__dict__.pop(item)
f1 = Foo(10) #初始化触发init
print(f1.__dict__)
f1["z"]=3 #赋值触发setitem
print(f1.__dict__)
f1["a"] #查找不存在的属性,触发getitem
f1.__dict__['a'] = 3
del f1["a"] #删除触发delitem
print(f1.__dict__)
定制自己的数据类型:二次加工标准类型
基于类继承实现
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
def append(self, p_object):
if not isinstance(p_object,int): #' 派生自己的append:加上类型检查'
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)
l.append(5)
print(l)
# l.append('1111111') #报错,必须为int类型
print(l.mid)
#其余的方法都继承list的
l.insert(0,-123)
print(l)
l.clear()
print(l)
授权的方式
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())
f1.close()
#重写list
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)
def clear(self):
if not self.permission:
raise PermissionError('not allow the operation')
self.seq.clear()
@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)
l.append(4)
print(l)
# l.append('3333333') #报错,必须为int类型
print(l.mid)
#基于授权,获得insert方法
l.insert(0,-123)
print(l)
自定制格式化字符串__format__
date_dic={
'ymd':'{0.year}:{0.month}:{0.day}',
'dmy':'{0.day}/{0.month}/{0.year}',
'mdy':'{0.month}-{0.day}-{0.year}',
}
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
def __format__(self, format_spec):
if not format_spec or format_spec not in date_dic: 如果输入的为空或者输入的格式不在规定的字典中,赋予默认格式
format_spec='ymd'
fmt=date_dic[format_spec]
return fmt.format(self)
d1=Date(2016,12,29)
print(format(d1)) #默认输出模式
print('{:mdy1}'.format(d1)) #没有mdy1这个模式。所以默认ymd模式输出
print('{:mdy}'.format(d1)) #mdy输出模式
print(format(d1,"dmy")) #dmy输出模式
print("{:dmy}".format(d1)) #dmy输出模式,同上
__next__和__iter__实现迭代器协议
from collections import Iterable(可迭代对象),Iterator(迭代器)
class Foo:
def __init__(self,start=0,stop=0):
self.start=start
self.stop=stop
self.con=0
def __iter__(self):
return self
def __next__(self):
if self.con >= self.start and self.start >=self.stop :
raise StopIteration
if self.stop==0:
if self.con < self.start:
n = self.con
self.con += 1
return n
else:
self.con=self.stop
n=self.start
self.start+=1
return n
f=Foo(1,6)
print(isinstance(f,Iterator)) #判断f是否是迭代器
for i in Foo(1,6):
print(i)
for i in Foo(6):
print(i)
doc
class Foo:
'我是描述信息'
pass
print(Foo.__doc__)
输出:'我是描述信息'
这个属性无法被继承
enter__和__exit
class Open:
def __init__(self,name,mode="w"):
self.x=open(name,mode=mode)
self.name=name
self.mode=mode
def __enter__(self): #出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
return self
def __exit__(self, exc_type, exc_val, exc_tb): #with中代码块执行完毕后执行
print("exc_type",exc_type) #捕获异常。获取异常的各个信息。
print("exc_val",exc_val)
print("exc_tb",exc_tb)
return True
def write(self,value):
self.x.write(value)
def __getattr__(self, item):
return getattr(self.x,item)
# a=Open("a.txt","r")
# print(a.read())
# a.seek(0)
# print(a.read())
with Open("a.txt","r") as f:
print(f.read())
raise TypeError("error!")
print("-------------------------")
call
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
元类:
什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
创建类的两种方法
方式一:
1 class Foo:
2 def func(self):
3 print('from func')
方式二:
1 def func(self):
2 print('from func')
3 x=1
4 Foo=type('Foo',(object,),{'func':func,'x':1})
一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类
自定制元类
class B(type):
def __init__(self,name,bases=None,dict=None):
print("init B")
self.name=name
def __call__(self, *args, **kwargs):
print("call")
obj=self.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class A(object,metaclass=B):
def __init__(self,name):
print("init A")
self.name=name
def __new__(cls, *args, **kwargs):
print("new A")
return super().__new__(cls)
a=A("name1")
print(a.__dict__)
精简版
class Mytype(type):
def __init__(self,what,bases=None,dict=None):
print(what,bases,dict)
def __call__(self, *args, **kwargs):
print('--->')
obj=object.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class Room(metaclass=Mytype):
def __init__(self,name):
self.name=name
r1=Room('alex')
print(r1.__dict__)
总结
元类总结
class Mymeta(type):
def __init__(self,name,bases,dic):
print('===>Mymeta.__init__')
def __new__(cls, *args, **kwargs):
print('===>Mymeta.__new__')
return type.__new__(cls,*args,**kwargs)
def __call__(self, *args, **kwargs):
print('aaa')
obj=self.__new__(self)
self.__init__(self,*args,**kwargs)
return obj
class Foo(object,metaclass=Mymeta):
def __init__(self,name):
self.name=name
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
'''
需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__
而爹.__call__一般做两件事:
1.调用name.__new__方法并返回一个对象
2.进而调用name.__init__方法对儿子name进行初始化
'''
'''
class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行
Foo=Mymeta('foo',(...),{...})
因此我们可以看到,只定义class就会有如下执行效果
===>Mymeta.__new__
===>Mymeta.__init__
实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作,
遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法
于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化
'''
'''
obj=Foo('egon')
的原理同上
'''
'''
总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了
1.谁后面跟括号,就从谁的爹中找__call__方法执行
type->Mymeta->Foo->obj
Mymeta()触发type.__call__
Foo()触发Mymeta.__call__
obj()触发Foo.__call__
2.__call__内按先后顺序依次调用儿子的__new__和__init__方法
'''