面向对象进阶
Posted samyoung
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象进阶相关的知识,希望对你有一定的参考价值。
setattr、__getattr__和__delattr__可以拦截对对象属性的访问
__setattr__函数是用来设置对象的属性,通过object中的__setattr__函数来设置属性:
Posted samyoung
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象进阶相关的知识,希望对你有一定的参考价值。
setattr、__getattr__和__delattr__可以拦截对对象属性的访问
__setattr__函数是用来设置对象的属性,通过object中的__setattr__函数来设置属性:
在c对象被实例化的时候,触发了__serattr__函数。在c.age赋值操作的时候,也触发了__setattr__函数,说明每次只要给属性设置的时候,都会触发这个函数但是在打印c.name的时候却被告知没有name这个属性。是因为我们在这里只是打印了值,并没有去修改或者保存值。这里不能用self.key=value,因为key是一个字符串,对象.‘属性名’的调用方式是行不通的,必须是对象.属性名。也不能setattr(self,key,value),setattr就是在设置对象的属性的值,一旦设置属性的值就回触发__setattr__函数,所以会无限递归下去,应该使用self.__dict__[key]=value。在__setattr__中我们还可以添加对传入的值的类型控制等满足程序自定义的操作
__delattr__用来删除对象的属性:
当在删除对象的属性的时候,会触发__delattr__函数,这里跟__setattr__函数的触发机制是一样的,在这里要做删除对象的属性操作,注意,不能写del self.item,也不能使用delattr()函数,回形成无限递归。正确姿势应该是self.__dict__.pop(item)。同理,我们也可以在__delattr__中添加一些特殊的控制
__getattr__当在对象.__dict__找不到的属性时候,就会触发这个函数
class A: def __init__(self,name): self.name=name def __getattr__(self,item): print("--getattr---%s"%item) a=A("abc") a.name#到着一步也没有__getattr__函数 a.age#打印 --getattr---age,属性不存在的时候触发了__getattr__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setattr__(self, key, value): print(‘setattr===>‘) def __setitem__(self, key, value): print(‘setitem--->‘) 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.name=‘sbli‘ f1[‘age‘]=18 f1[‘age1‘]=19 del f1.age1 del f1[‘age‘] f1[‘name‘]=‘alex‘ print(f1.__dict__) setattr===> setattr===> setitem---> setitem---> del obj.key时,我执行 del obj[key]时,我执行 setitem---> {‘name‘: ‘alex‘}
__getattribute__
class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print(‘不管是否存在,我都会执行‘) f1=Foo(10) f1.x 不管是否存在,我都会执行
#_*_coding:utf-8_*_ __author__ = ‘abc‘ 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
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__) print(Foo.__dict__) #在f1.__dict__里并没加进去它的属性 [‘name‘, ‘age‘] {‘age‘: <member ‘age‘ of ‘Foo‘ objects>, ‘__module__‘: ‘__main__‘, ‘__slots__‘: [‘name‘, ‘age‘], ‘name‘: <member ‘name‘ of ‘Foo‘ objects>, ‘__doc__‘: None}
可迭代对象:有__iter__方法
特点:
对象不会再去建命名空间,节省内存
属性只能设置成规定的
迭代器特点:
既有__next__也有__iter__
不依赖于索引取值
省内存,同一时间在内存只有一个值
缺点:不知道总共多少值,只能next()
只能往后不断读值
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(isinstance(f,Iterable)) #判断是否是可迭代的 print(isinstance(f,Iterator)) #判断是否是迭代器 print(next(f)) #f.__next__() print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) #最多到10 for i in f: print(‘====>‘,i)
实现range
class Range: 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)
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
#!/usr/bin/env python # -*- coding:utf-8 -*- class C: def __init__(self): self.name = ‘SB‘ from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print(‘执行我啦‘) f1=Foo() #del f1 print(‘------->‘) -------> 执行我啦
class Foo: def __del__(self): print(‘执行我啦‘) f1=Foo() del f1 print(‘------->‘) 执行我啦 ------->
上述叫做上下文管理协议,即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) #------------------------------->会执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print(‘enter‘) return 111 def __exit__(self, exc_type, exc_val, exc_tb): print(‘exit‘) print(‘exc_type‘,exc_type) print(‘exc_val‘,exc_val) print(‘exc_tb‘,exc_tb) return True with Open(‘a.txt‘) as f: print(‘===>open‘,f) #res=Open().__enter__() #f=res # raise AttributeError(‘aaaaa‘) enter ===>open 111 exit exc_type None exc_val None exc_tb None
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): pass with Open(‘a.txt‘,‘w‘) as write_file: #write_file=open(‘a.txt‘,‘w‘) write_file.write(‘123\n‘) print(write_file) write <__main__.Open object at 0x0000000000D319E8>
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print(‘__call__‘) obj = Foo() # 执行 __init__ obj() # 执行 __call__ 得到的对象变成可调度对象
callable 检查是否是可调用对象
print(callable(People))
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
type成为元类,是所有类的类,利用type模拟class关键字的创建类的过程
#模拟创建类 都被封装成class类了
def run(self):
print(‘%s is runing‘ %self.name)
class_name=‘Bar‘
bases=(object,)
class_dic={
‘x‘:1,
‘run‘:run
}
Bar=type(class_name,bases,class_dic) #类的名字,类的继承,类的字典
print(Bar)
print(type(Bar))
<class ‘__main__.Bar‘>
<class ‘type‘>
#自定义元类 class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): print(self) print(class_name) print(class_bases) print(class_dic) type.__init__(self,class_name,class_bases,class_dic) #进行实例化 class Foo(metaclass=Mymeta): x=1 def run(self): print(‘running‘) #type(‘Foo‘,(object,),{‘x‘:1},‘run‘:run) f=Foo() print(f)
class Mymeta(type): def __init__(self,*args,**kwargs): pass def __call__(self, *args, **kwargs): print(self) obj=self.__new__(self) self.__init__(obj,*args,**kwargs) #obj.name=‘egon‘ 产生空对象进行初始化赋值 return obj class Foo(metaclass=Mymeta): x=1 def __init__(self,name): #返回的call的值 self.name=name def run(self): print(‘running‘) #type(‘Foo‘,(object,),{‘x‘:1},‘run‘:run) f=Foo(‘egon‘) print(f) print(f.name)
<class ‘__main__.Foo‘>
<__main__.Foo object at 0x00000000007B1B00>
egon
以上是关于面向对象进阶的主要内容,如果未能解决你的问题,请参考以下文章