面向对象进阶
一 isinstance(obj,cls) 和 issubclass(sub,super)
1.isinstance(obj,cls) 检查obj是否是 cls的对象
class Foo(object): pass obj = Foo() print(isinstance(obj, Foo))
2.issubclass(sub,super)检查sub类 是否是 super类的 派生类
class Foo(object): pass class Bar(Foo): pass print(issubclass(Bar, Foo))
二 attr系列:__setattr__,__getattr__,__delattr__
class Foo(object): def __init__(self, x): self.name = x def __setattr__(self, key, value): self.__dict__[key] = value def __delattr__(self, item): print('delattr:--->{}'.format(item)) print(type(item)) self.__dict__.pop(item) f = Foo('Tom') # f.name = 'Tom' f.age = 18 # print(f.__dict__) del f.age print(f.__dict__)
class Foo(object): def __init__(self, x): self.name = x # 属性不存在的情况下才会触发 def __getattr__(self, item): print('getattr-->{}'.format(item)) f = Foo('Tom') # print(f.name) print(f.xxx) # f.xxx的结果: getattr -->xxx None
三 item系列:__setiem__,__getitem__,__delitem__
把对象操作属性 模拟成字典的格式
# 把对象操作属性 模拟成字典的格式 class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): print('getitem', item) return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): self.__dict__.pop(key) ##测试 del方法-----> del f.name # def __delattr__(self,item): # print('del obj.key时,我执行') # self.__dict__.pop(item) f = Foo('xxx') f['name'] = 'Jerry' f['age'] = 18 del f['age'] print(f.__dict__)
四 __slots__
1. 对象不再产生新的名称空间,它们共用类的名称空间。节省内存
2. 不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名
''' 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。 ''' class Foo: __slots__ = 'x' f1 = Foo() f1.x = 1 # f1.y = 2 #报错 print(f1.__slots__) class Bar: __slots__ = ['x','y'] n = Bar() n.x,n.y = 1,2 # n.z = 3 #会报错,原本没 z的存在 print(n.__slots__) __slots__使用
class People: __slots__ = ['x', 'y', 'z'] p = People() print(People.__dict__) p.x = 1 p.y = 2 p.z = 3 print(p.x, p.y, p.z) # print(p.__dict__) #报错 p1 = People() p1.x = 10 p1.y = 20 p1.z = 30 print(p1.x, p1.y, p1.z) # print(p1.__dict__) 报错 例二
class Foo(object): __slots__ = ['name', 'age'] f = Foo() f.name = 'Tom' f.age = 18 print(f.__slots__) f2 = Foo() f2.name = 'Jerry' f2.age = 20 print(f2.__slots__) print(Foo.__dict__) # f1 与f2 都没有属性字典__dict__了,统一归__slots__管,节省内存
五 __call__
对象后面加括号,触发执行。
类对象都是可被调用对象,类的实例对象是否可调用对象,取决于类是否定义了__call__方法。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行__init__ obj() # 执行__call__ ##输出结果 __call__
class People: def __init__(self, name): self.name = name def __call__(self, *args, **kwargs): print('打印---call') p = People('Tom') print(callable(People)) # 类People是可调用对象 print(callable(p)) p() # 结果: ''' True True 打印---call '''
六 __next__和__iter__实现迭代器协议
1.for(无限循环)
from collections import Iterable, Iterator class Foo: def __init__(self, start): self.start = start def __iter__(self): return self def __next__(self): return 'aSB' f = Foo(0) # f.__iter__() # f.__next__() # # print(isinstance(f,Iterable)) # print(isinstance(f,Iterator)) #是否是 迭代器 # # # print(next(f)) #相当于print(f.__next__()) # print(next(f)) # print(next(f)) for i in f: print(i)
2.for(有限)
rom 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) for i in f: print('--->', i)
3.Range
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, 5): print(i) # print(Range.__doc__)
七 __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中代码块执行完毕时执行我啊') # self.f.close() 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声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print('exc_type:', exc_type) print('exc_val:', exc_val) print('exc_tb:', exc_tb) with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('出错了,出错了') print('*' * 10) # ---------------->不会执行
如果__exit__()返回值为True,那么异常会被清空,就好像神马都没发生一样,with后的语句正常执行
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中代码块执行完毕时执行我啊') print('exc_type:', exc_type) print('exc_val:', exc_val) print('exc_tb:', exc_tb) return True # 添加此行 with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('出错了,出错了') print('*' * 10) # ---------------->会执行
模拟open:
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.write('123123123\n') write_file.write('123123123\n') print(sss) # 抛出异常,交给__exit__处理 write_file.write('123123123\n')
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
七 __doc__, 加 __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__)
八 __del__ (析构函数)
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
class Foo: def __del__(self): print('执行我了') f1 = Foo() # del f1 print('----->') ##输出结果 -----> 执行我了
class Foo: def __del__(self): print('执行我了') f1 = Foo() del f1 # 此步的 存在!!! print('----->') ##执行结果 执行我了 ----->
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') print('======>>>') ##输出结果 ##======>>> ##----->del 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') del f # 此步的存在 !!!!!! print('=======>>>') # 输出结果 ''' ======>>> ----->del ----->del =======>>> '''

浙公网安备 33010602011771号