Py修行路 python基础 (十九)面向对象进阶(下)
item系列 __slots__方法 __next__ 和 __iter__实现迭代器 析构函数 上下文管理协议 元类
一、item系列 把对象操作属性模拟成字典的格式。
例如:对象名['key'] = value
- class Foo:
- def __init__(self,name):
- self.name = name
- def __getitem__(self, item):
- return self.__dict__[item]
- def __setitem__(self, key, value):
- self.__dict__[key] = value
- def __delitem__(self, key):
- self.__dict__.pop(key)
- f = Foo('egon')
- f['age'] = 18
- print(f.__dict__)
- del f['age']
- print(f.__dict__)
- print(f['name'])
- #print(f['sex'])
- #执行结果:
- {'name': 'egon', 'age': 18}
- {'name': 'egon'}
- egon
二、__slots__方法:
1.__slots__是什么: 是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.使用点来访问属性本质,就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不再是为每个实例定义一个字典,这跟元组或列表很类似。
__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
5、好处:节省内存:类内部指定属性,对象就只能建立所对应的属性。不再有属性字典__dict__,统一归__slots__管。
关于__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)
- p1 = People()
- p1.x = 10
- p1.y = 20
- p1.z = 30
- print(p1.x,p1.y,p1.z)
- print(p1.__dict__) #会报错!
- #执行结果:
- {'__module__': '__main__', '__slots__': ['x', 'y', 'z'], 'x': <member 'x' of 'People' objects>, 'y': <member 'y' of 'People' objects>, 'z': <member 'z' of 'People' objects>, '__doc__': None}
- 1 2 3
- 10 20 30
- Traceback (most recent call last):
- File "F:/py_fullstack_s4/day32/__slots__的方法.py", line 18, in <module>
- print(p1.__dict__)
- AttributeError: 'People' object has no attribute '__dict__'
三、__next__ 和 __iter__实现迭代器
- 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(Foo,Iterable)) #判断是否是可迭代对象
- print(isinstance(Foo,Iterator)) #判断是否是迭代器
- print(isinstance(f,Iterable)) #判断是否是可迭代对象
- print(isinstance(f,Iterator)) #判断是否是迭代器
- for i in f :
- print(i)
- #执行结果:
- False
- False
- True
- True
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
定义类,实现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)
- #执行结果:
- 0
- 1
- 2
callable() 判断类是否可调用
四、 __doc__描述信息,该属性无法被继承,是对本函数或类的描述!没写打印None
__class__和__module__
__module__ 表示当前操作的对象在哪个模块
__class__ 表示当前操作的对象的类是什么
- class Foo:
- pass
- class A(Foo):
- pass
- a = A()
- print(a.__class__)
- print(a.__module__)
- #执行结果:
- <class '__main__.A'>
- __main__
五、__del__ 析构函数
删除对象或是对象执行完回收,引用基数为0,的时候,自动触发执行,将对象在内存中释放。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
在类中定义 __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','r+')
- #没有删除命令,也会自动执行!当没有代码要执行,引用基数为0的时候,就会触发。
- # f1 = f #定义其他的引用
- # del f #先执行删除命令,再打印下边的内容
- print('=-====>')
- #执行方式:
- # =-====>
- # =---->del
- #先执行删除命令,再打印下边的内容
- del f #先执行删除命令,再打印下边的内容
- print('=-====>') #没有删除命令,也会自动执行!当没有代码要执行,引用基数为0的时候,就会触发。
- #执行方式:
- # =---->del
- # =-====>
- #引用基数不为0的情况
- f1 = f #定义其他的引用
- del f #先执行删除命令,再打印下边的内容
- print('=-====>')
- #执行方式:
- # =-====>
- # =---->del
六、上下文管理协议 (with 方法)
__enter__ 和__exit__
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
- # __enter__和__exit__
- class Foo:
- def __enter__(self): #with 拿到一个对象,触发enter方法
- print('enter')
- return 11111
- def __exit__(self, exc_type, exc_val, exc_tb): #文件打开执行完毕后,执行exit方法
- print('exit')
- print('exc_type',exc_type) #异常的类型
- print('exc_val',exc_val) #异常的值
- print('exc_tb',exc_tb) #异常的内存地址
- return True #清空所有异常,抛异常之后,with后的内容正常执行。
- with Foo() as obj: #执行Foo().__enter__方法得到一个返回值,然后将这个值赋给obj。
- #出现with语句, 对象的__enter__被触发, 有返回值则赋值给as声明的变量
- print('with Foo 的子代码块',obj) #执行with模块字代码
- raise NameError('名字没有定义!') #只要是报异常,没有处理的话,就意味着with执行的字代码块运行完。触发exit方法,这之后的代码就不会再执行。
- print('##############')
- print('**************')
- #执行结果:
- enter
- with Foo 的子代码块 11111
- exit
- exc_type <class 'NameError'>
- exc_val 名字没有定义!
- exc_tb <traceback object at 0x00000000028EE948>
- **************
例子:实现上下文的管理协议:(with方法打开文件执行操作!)
- 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): #with 对象 就会触发对象下的该方法
- return self #将对象返回 write_file=Open('aaaaa.txt','w')
- #return self.f #这就是返回真实的open方法,字代码块的方法都可以使用,但是就不再是类Open的使用。
- def __exit__(self, exc_type, exc_val, exc_tb):#文件结束清理
- self.f.close()
- return True
- with Open('aaaaa.txt','w') as write_file: #变相的实例化 write_file=Open('aaaaa.txt','w')拿到产生的文件句柄
- write_file.write('123123123123123\n')
- write_file.write('123123123123123\n')
- write_file.write('123123123123123\n')
七、__call__方法
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
- class Foo:
- def __init__(self,name):
- self.name = name
- def __call__(self, *args, **kwargs):
- print('__call__')
- obj = Foo('egon') # 执行 __init__
- #obj() # 执行 __call__ 对象对去找有没有__call__的绑定方法,有加()就能运行!
- #查看是否为可调用对象(可调用对象:名字后加()就能运行)
- print(callable(Foo))
- print(callable(obj))
- #执行结果:
- True
- True
- #若是隐去__call__功能,查看结果
- class Foo:
- def __init__(self,name):
- self.name = name
- # def __call__(self, *args, **kwargs):
- # print('__call__')
- obj = Foo('egon') # 执行 __init__
- #obj() # 执行 __call__ 对象对去找有没有__call__的绑定方法,有加()就能运行!
- #查看是否为可调用对象(可调用对象:名字后加()就能运行)
- print(callable(Foo))
- print(callable(obj))
- #执行结果:
- True
- False
八、元类
1 引子
- 1 class Foo:
- 2 pass
- 3
- 4 f1=Foo() #f1是通过Foo类实例化的对象
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?
- 1 #type函数可以查看类型,也可以用来查看对象的类,二者是一样的
- 2 print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
- 3 print(type(Foo)) # 输出:<type 'type'>
2 什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3 创建类的两种方式
方法一:
- class Foo:
- x =1
- def run(self):
- pass
- print(Foo)
- print(type(Foo))
方法二:
- #type称为元类,是所有类的类,控制类的。利用type模拟class关键字的创建类的过程。
- def run(self):
- print('%s is running'%self.name)
- class_name = 'Bar' #类名
- bases=(object,) #继承方式
- class_dic = {'x':1,'run':run} #名称空间 属性方法
- #语法:变量名 = type(类名,继承关系(元组),属性方法(字典)) class 关键字创建类,其实质就是type()封装的方法!
- Bar = type(class_name,bases,class_dic) #自定义生成一个类
- print(Bar) #查看类型
- print(type(Bar)) #查看元类
4 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
(1)通过元类给类添加一些条件判断方法(查看工作流程)
- class Mymeta(type): #定义元类继承type
- def __init__(self,class_name,class_bases,class_dic):
- #实例化就会调用执行默认的方法,和用type直接创建一个类一致。(类名,继承关系,方法)
- print(self) #对比打印验证
- print(class_name) #对比打印验证
- print(class_bases) #对比打印验证
- print(class_dic) #对比打印验证
- #对创建类,不写__doc__方法的人进行提示
- for key in class_dic: #可在元类初始化这里加上对下边 子类 的判断验证方法
- if not callable(class_dic[key]):continue #判断类方法中有没有可调用方法,没有继续
- if not class_dic[key].__doc__: #判断方法类中__doc__对应的值是否为None
- raise TypeError('小子,你没写注释,赶紧去写')
- # type.__init__(self,class_name,class_bases,class_dic) #执行的上述方法实际上就是在调用type()的方法
- class Foo(metaclass=Mymeta):#将类foo看成元类的对象,加()运行就是在进行实例化
- x=1
- def run(self):
- 'run function'
- print('running')
- # Foo=Mymeta('Foo',(object,),{'x':1,'run':run})#上述利用class方法定义Foo类与用type()定义一致。
- print(Foo.__dict__)
- #执行结果:
- <class '__main__.Foo'>
- Foo
- ()
- {'__module__': '__main__', '__qualname__': 'Foo', 'x': 1, 'run': <function Foo.run at 0x00000000022BC950>}
- {'__module__': '__main__', 'x': 1, 'run': <function Foo.run at 0x00000000022BC950>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
(2)利用元类,定义产生一个类的完整过程:
- class Mymeta(type): #定义元类
- def __init__(self,class_name,class_bases,class_dic):#始终明确 self 就是Foo
- pass #不定义就走type()的内部方法
- def __call__(self, *args, **kwargs): #类实例化就能调用执行其实质上是执行了__call__方法
- # print(self)
- obj=self.__new__(self) #类Foo实例化f, Foo执行__new__方法,先产生一个空对象
- self.__init__(obj,*args,**kwargs) #obj.name='egon'
- #上一行代码是在 给产生的空对象传值,调用Foo下的__init__方法,也就是定义的类 Foo中的__init__方法
- return obj #空对象拿到值之后,产生一个新的对象obj,然后获取这个值,将这个值返回。
- class Foo(metaclass=Mymeta):
- x=1
- def __init__(self,name): #类内给实例化对象定义的初始属性
- self.name=name #obj.name='egon'
- def run(self):
- 'run function'
- print('running')
- # print(Foo.__dict__)
- f=Foo('egon') #类实例化
- print(f)
- print(f.name)
- #执行结果:
- <__main__.Foo object at 0x0000000002280128>
- egon
注意点:
- #元类总结
- 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__方法
- '''
Py修行路 python基础 (十九)面向对象进阶(下)的更多相关文章
- Py修行路 python基础 (九)作用域 函数嵌套 闭包
名称空间与作用域 变量,函数 分成三种 #内置名称空间 内置函数, 系统函数内部自定义的. python查看内置函数,命令: import builtins dir(builtins) #全局名称空 ...
- Py修行路 python基础 (十四)递归 及 面向对象初识及编程思想
一.递归 1.定义: 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. (1)递归就是在过程或函数里调用自身: (2)在使用递归策略时,必须有一个明确的递归结束条件 ...
- Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类
一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...
- Py修行路 python基础 (十六)面向对象编程的 继承 多态与多态性 封装
一.继承顺序: 多继承情况下,有两种方式:深度优先和广度优先 1.py3/py2 新式类的继承:在查找属性时遵循:广度优先 继承顺序是多条分支,按照从左往右的顺序,进行一步一步查找,一个分支走完会走另 ...
- Py修行路 python基础 (二十五)线程与进程
操作系统是用户和硬件沟通的桥梁 操作系统,位于底层硬件与应用软件之间的一层 工作方式:向下管理硬件,向上提供接口 操作系统进行切换操作: 把CPU的使用权切换给不同的进程. 1.出现IO操作 2.固定 ...
- Py修行路 python基础 (十八) 反射 内置attr 包装
一.isinstance 和 issubclass1.isinstance(obj,cls)检查是否obj是否是类 cls 的对象.2.issubclass(sub, super)检查sub类是否是 ...
- Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式
一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._next_() 取下一个值 优点: 1.提供了 ...
- Py修行路 python基础 (二十)模块 time模块,random模块,hashlib模块,OS及sys模块
一.前提介绍: 可以开辟作用域的只有类,函数,和模块 for循环 if,else: 不能开辟自己的作用域 避免程序复用和重复调用,将这些写到一个.py文件中,做成一个模块,进行调 ...
- Py修行路 python基础 (二十四)socket编程
socket编程 一.客户端/服务端架构 客户端/服务端架构 即C/S架构,包括:1.硬件C/S架构,2.软件C/S架构. 互联网中处处都是C/S架构,学习socket 就是为了完成C/S架构的开发. ...
随机推荐
- 【C#基本功 控件的用法】 Toolbar的用法
之前从事Labview编程,Labview是一门快速编程的语言,虽然快速,但作为一门语言他灵活性不够,有些方面也不是很给力,就比如 Toolbar labview就没有Toolbar的基础控件,虽然可 ...
- "下载"文件夹的desktop.ini
下载 [.ShellClassInfo] LocalizedResourceName=@%SystemRoot%\system32\shell32.dll,-21798 IconResource=%S ...
- 牛客比赛-Wannafly9-A/B/C
A-链接:https://www.nowcoder.com/acm/contest/71/A来源:牛客网 给定n个正整数,请找出其中有多少个数x满足:在这n个数中存在数y=kx,其中k为大于1的整数 ...
- POJ 1062 限制点
http://poj.org/problem?id=1062 昂贵的聘礼 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 50 ...
- IOS-Alcatraz(插件管理工具)
一.简单说明 Alcatraz 是一款 Xcode的插件管理工具,可以用来管理XCode的 插件.模版以及颜色配置的工具. 二.如何安装 1.github地址:https://github.com/a ...
- CC攻击工具list
从论文里抠出来的工具列表如下,后面有黑产的工具以及网络上摘录的工具: 分类:(1)有僵尸网络(是否代理服务器)&没有的==>(2)单一url&混合url(多线程,压测为主,dem ...
- Oracle的导出和导入
(摘自:http://www.cnblogs.com/mchina/archive/2012/09/12/2678093.html) 数据库的备份操作是在整个项目运行中最重要的工作之一. 一.数据的导 ...
- L131
Fake, Low Quality Drugs Come at High CostAbout one in eight essential medicines in low- and middle-i ...
- C++友元函数实现
友元函数是一种特殊的函数,它必须要在类中进行声明,但其本身并不是类的成员函数,但友元函数可以访问类的私有成员变量. 友元函数的好处: 1.实现类之间的数据共享 2.提高程序运行效率,方便编程 友元函数 ...
- ng 双向数据绑定 实现 注册协议效果
效果: 代码: <!DOCTYPE html> <html ng-app="myApp"> <head lang="en"> ...