egon说一切皆对象--------面向对象进阶紫禁之巅
一、检查isinstance(obj,cls)和issubclass(sub,super)
class Foo(object): pass obj = Foo() isinstance(obj, Foo) ##判断obj是否是Foo对象
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo) ##判断Bar是否是Foo的派生类
二、反射python面向对象中的反射:通过字符串的形式操作对象相关的属性
#hasattr(obj,str) #getattr(obj,str) #setattr(obj,key,value) #delattr(obj,str) 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__) 四个方法的使用演示
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') 类也是对象
#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2') 反射当前模块成员
导入其他模块
module_test
#!/usr/bin/env python # -*- coding:utf-8 -*- def test(): print('from the test')
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')()
反射的好处
可插拔机制:A出去游玩而代码的某些功能没有写完,我想加班把代码赶完,而且需要使用A没有写完的功能
B没有完成的代码
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('处理其他的逻辑')
好处二,实现动态导入模块
简单粗暴直接上代码
###
三、__setattr__、__getattr__、__delattr__
粗暴玩法
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 三者的用法演示
修改和赋值触发__setattr__、删除变量触发__delattr__、当不在__dict__中,触发__getattr__(使用obj.方式调用属性)
四、包装
包装:python提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
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) l.append(5) print(l) # l.append('1111111') #报错,必须为int类型 print(l.mid) #其余的方法都继承list的 l.insert(0,-123) print(l) l.clear() print(l) 二次加工标准类型(基于继承实现)
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
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()
#_*_coding:utf-8_*_ #我们来加上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) f1.close()
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) l.clear() print(l) #基于授权,获得insert方法 l.insert(0,-123) print(l)
五、property
把函数的返回值定制成变量形式
class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA
class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA
class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
#实现类型检测功能 #第一关: class People: def __init__(self,name): self.name=name @property def name(self): return self.name # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常 #第二关:修订版 class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里 print(p1.name) print(p1.name) print(p1.name) print(p1.__dict__) p1.name='egon' print(p1.__dict__) del p1.name print(p1.__dict__) #第三关:加上类型检查 class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') if not isinstance(value,str): raise TypeError('必须是字符串类型') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里 p1.name=1
六、 __setitem__,__getitem,__delitem__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
七、__slots__
######################__solts__节省内存,让类没有__dict__字典,严格定义对象属性 from collections import Iterable,Iterator class people: __slots__ = ['name','age','high'] b=people() b.name='egon' b.age=18 b.high=1.5
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
class Foo: __slots__=['name','age'] f1=Foo() f1.name='alex' f1.age=18 print(f1.__slots__) f2=Foo() f2.name='egon' f2.age=19 print(f2.__slots__) print(Foo.__dict__) #f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存 刨根问底
八、__next__和__iter__实现迭代器协议
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' class Foo: def __init__(self,x): self.x=x def __iter__(self): return self def __next__(self): n=self.x self.x+=1 return self.x f=Foo(3) for i in f: print(i)
class Foo: def __init__(self,start,stop): self.num=start self.stop=stop def __iter__(self): return self def __next__(self): if self.num >= self.stop: raise StopIteration n=self.num self.num+=1 return n f=Foo(1,5) from collections import Iterable,Iterator print(isinstance(f,Iterator)) for i in Foo(1,5): print(i)
class Fib: def __init__(self): self._a=0 self._b=1 def __iter__(self): return self def __next__(self): self._a,self._b=self._b,self._a + self._b return self._a f1=Fib() print(f1.__next__()) print(next(f1)) print(next(f1)) for i in f1: if i > 100: break print('%s ' %i,end='')
九、__doc__
class Foo: '我是描述信息' pass class Bar(Foo): pass print(Bar.__doc__) #该属性无法继承给子类
十、__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
###########################__del__当调用基数为0即对象被清除时候会被执行(析构函数) __doc__注释,不会被继承 class Open: def __init__(self,path,mode='a+',encoding='utf-8'): self.f=open(path,mode=mode,encoding=encoding) def __getattr__(self, item): return getattr(self.f,item) def __enter__(self): #使用with as 开始时执行(或with) print('已经开始') return self def __del__(self): print('清理') def __exit__(self, exc_type, exc_val, exc_tb): #使用with as 结束时执行 self.f.close() print("已经结束,正在清理") return True# 处理异常后返回真,可以使程序不推出异常 with Open('111') as f: f.write("\n我进来了")
管理上下文协议
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') with Open('a.txt') as f: print('=====>执行代码块') # print(f,f.name) 上下文管理协议
__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0'*100) #------------------------------->不会执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) return True with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0'*100) #------------------------------->会执行
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
十一、__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
十二、metaclass,元类
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
创建类的两种方式
一、
class Foo: def func(self): print('from func')
二、
def func(self): print('from func') x=1 Foo=type('Foo',(object,),{'func':func,'x':1})
一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
class Mytype(type): def __init__(self,class_name,bases=None,dict=None): print("Mytype init--->") print(class_name,type(class_name)) print(bases) print(dict) def __call__(self, *args, **kwargs): print('Mytype call---->',self,args,kwargs) obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Foo(object,metaclass=Mytype):#in python3 #__metaclass__ = MyType #in python2 x=1111111111 def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): return super().__new__(cls) # return object.__new__(cls) #同上 f1=Foo('name') print(f1.__dict__)
class Mytype(type): def __init__(self,what,bases=None,dict=None): print('mytype init') def __call__(self, *args, **kwargs): obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Foo(object,metaclass=Mytype): x=1111111111 def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): return super().__new__(cls) f1=Foo('egon') print(f1.__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__方法 '''