面向对象_类的特殊成员
成员名前如果有两个下划线,则表示该成员是私有成员,只能由类内部调用。
__doc__
表示类的描述信息
class Animal(): ''' 类的描述信息:动物类 ''' def eat(self): pass print(Animal.__doc__)
__module__和__class__
__module__表示当前操作对象所属的模块
__class__表示当前操作对象所属的类
class c(): def __init__(self): self.name = 'lary'
from module_test import c obj = c() print(obj.__module__) #module_test print(obj.__class__) #<class 'module_test.c'>
__init__
构造方法,通过类创建对象时,自动触发执行
class Animal(): def __init__(self,name): self.name = name self.feature = 'eat' animal = Animal('cat') #自动执行类中的__init__方法
__del__
析构方法,当对象在内存中被释放时,自动触发执行。此方法一般无需定义,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
__call__
对象后面加括号,触发执行。
class Animal(): def __init__(self): pass def __call__(self, *args, **kwargs): print('调用__call__') animal = Animal() #调用__init__ animal() #调用__call__
__dict__
类或对象中的所有成员
class Animal(): ''' 动物类 ''' feature = 'live' def __init__(self,name): self.name = name def __call__(self, *args, **kwargs): print('调用__call__') print(Animal.__dict__) animal = Animal('cat') print(animal.__dict__)
__getitem__,__setitem__,__delitem__
用于索引操作,如字典
class Foo(object): def __getitem__(self, key): print('__getitem__', key) def __setitem__(self, key, value): print('__setitem__', key, value) def __delitem__(self, key): print('__delitem__', key) obj = Foo() result = obj['k1'] # 自动触发执行 __getitem__ obj['k2'] = 'lary' # 自动触发执行 __setitem__ del obj['k1'] # 自动触发执行 __delitem__
__iter__和__next__
用于迭代器
class Foo(): def __init__(self,sq): self.sq = sq def __iter__(self): return iter(self.sq) obj = Foo([1,2,3,4,5]) for i in obj: 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)
__slots__
1.__slots__是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 3.字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 4.当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__的缺点就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 5.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如在程序中需要创建某个类的几百万个实例对象 。 6.关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
class Foo: __slots__ = 'x' f1 = Foo() f1.x = 1 f1.y = 2 # 报错 print(f1.__slots__) # f1不再有__dict__ class Bar: __slots__ = ['x', 'y'] n = Bar() n.x, n.y = 1, 2 #n.z = 3 # 报错 print(n.__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__管,节省内存
__enter__和__exit__
我们在操作文件对象的时候可以这么写
with open('a.txt') as f: f.read()
这叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
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) #------------------------------->不会执行
with语句中代码块出现异常,则with后的代码都无法执行,r如果__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) #------------------------------->会执行
class Open: def __init__(self,filepath,mode='r',encoding='utf-8'): self.filepath=filepath self.mode=mode self.encoding=encoding def __enter__(self): self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) return self.f def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() return True def __getattr__(self, item): return getattr(self.f,item) with Open('a.txt','w') as f: print(f) f.write('aaaaaa') f.wasdf #抛出异常,交给__exit__处理
使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动干预。
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制。
__getattribute__
访问属性时触发,不论该属性是否存在
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
class animal(object): def __getattribute__(self, item): return object.__getattribute__(self,item)() def eat(self): print('eating...') #print(animal.__dict__) cat = animal() #print(cat.__dict__) cat.eat #当获取属性时,直接return object.__getattribute__(self,*args,**kwargs) #如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可,如果不加的话,返回的是函数引用地址 #在__getattribute__方法里面,不能用self.xxx这种方式调用。因为调用类的属性每次都会强制调用__getattribute__,所以会导致递归调用