一、__setitem__,__getitem,__delitem__
#把对象操作属性模拟成字典的格式 class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print('getitem===>') return self.__dict__[item] def __setitem__(self, key, value): print('setitem===>') self.__dict__[key]=value def __delitem__(self, key): print('delitem===>') self.__dict__.pop(key) f=Foo('luchuan') f['name']='luchuangao' print(f['name']) f['age']=18 print(f.__dict__) del f['age'] print(f.__dict__)
二、__slots__
__slots__的好处是限制设置规定得属性,剩内存。
1、__slots__是什么:是一个类变量,变量值可以是列表、元组、或可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2、引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例是独立的) 3、为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。在定义__slots__后,__slots__就会实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是不能给实例添加新的属性,只能使用__slots__中定义的那些属性名。 4、注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大多数情况下,应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为内存优化工具。
class People: __slots__=['x','y','z'] p=People() print(People.__dict__) p.x=1 p.y=2 p.z=3 # p.x=4 #报错 print(p.x,p.y,p.z) # print(p.__dict__) p不再有属性字典__dict__,统一归__slots__管,节省内存
三、__next__和__iter__实现迭代器协议
from collections import Iterable,Iterator class Foo: def __init__(self,start): self.start=start def __iter__(self): return self def __next__(self): return 'SB' f=Foo(0) # f.__iter__() # f.__next__() print(isinstance(f,Iterable)) print(isinstance(f,Iterator)) print(next(f)) #f.__next__() print(next(f)) #f.__next__() print(next(f)) #f.__next__() for i in f: # res=f.__iter__() #next(res),无限循环 print(i)
from collections import Iterable,Iterator class Foo: def __init__(self,start): self.start=start def __iter__(self): return self def __next__(self): if self.start > 10: raise StopIteration n=self.start self.start+=1 return n f=Foo(0) print(next(f)) print(next(f)) print(next(f)) for i in f: print('====>',i)
class Range: '123' def __init__(self,start,end): self.start=start self.end=end def __iter__(self): return self def __next__(self): if self.start == self.end: raise StopIteration n=self.start self.start+=1 return n for i in Range(0,3): print(i)
四、__doc__
class Foo: '我是描述信息' pass class Bar(Foo): pass print(Foo.__doc__) print(Bar.__doc__) #该属性无法继承给子类
注:__doc__属性无法继承给子类
五、__module__和__class__
__module__表示当前操作的对象在那个模块
__class__表示当前操作的对象的类是什么
class Foo: '我是描述信息' pass class Bar(Foo): pass # print(Bar.__doc__) #该属性无法继承给子类 b=Bar() print(b.__class__) print(b.__module__) print(Foo.__module__) print(Foo.__class__) # 输出结果: # <class '__main__.Bar'> #b由Bar类产生 # __main__ #表示当前模块 # __main__ # <class 'type'>
六、__del__
析构方法,当对象在内存中被释放时,自动触发执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
import time class Open: def __init__(self,filepath,mode='r',encode='utf-8'): self.f=open(filepath,mode=mode,encoding=encode) def write(self): pass def __getattr__(self, item): return getattr(self.f,item) def __del__(self): print('----->del') self.f.close() f=Open('a.txt','w') f1=f #引用计数为2 del f # time.sleep(1000000000) print('=========>') #测试f1=f注释后得效果,只有f 引用计数为1
七、__enter__和__exit__
一般在操作文件时这样写:
with open('a.txt') as f: '代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
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__中定制自动释放资源的机制,你无须再去关系这个文件,这将大有用处。
class Open: def __init__(self,filepath,mode,encode='utf-8'): self.f=open(filepath,mode=mode,encoding=encode) self.filepath=filepath self.mode=mode self.encoding=encode def write(self,line): print('write') self.f.write(line) def __getattr__(self, item): return getattr(self.f,item) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() return True with Open('a.txt','w') as write_file: #write_file=Open('a.txt','w') write_file.write('123123123123123 ') write_file.write('123123123123123 ') print(sssssssssssssss) write_file.write('123123123123123 ')
八、__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()
class People: def __init__(self,name): self.name=name def __call__(self, *args, **kwargs): print('call') p=People('egon') print(callable(People)) print(callable(p)) p()
九、metaclass
1、引子
class Foo: pass f1=Foo() #f1是通过Foo类实例化的对象
Python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
以上可以看出f1是由Foo类产生的对象,而Foo本身也是对象。
type函数可以查看类型,也可以用来查看对象的类,二者是一样的
print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print(type(Foo)) # 输出:<type 'type'>
2、什么是元类?
元类是类的类,可以控制类的行为,是类的模板,正如类是创建对象的模板。
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是type类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。
3、创建类的两种方式
方式一:
class Foo: def func(self): print('from func')
方式二:
def func(self): print('from func') x=1 Foo=type('Foo',(object,),{'func':func,'x':1})
4、一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type, 用户也可以通过继承type来自定义元类(顺便也可以了解下元类是如何控制类的创建,工作流程是什么)
其他内容待补充,生产环境很少修改,原理需要理解。