python类高级用法_十.python面向对象高级用法

1.反射

1.1 什么是反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

1.2 反射的四个方法

1.hasattr(object,name)

判断object中有没有一个name字符串对应的方法或属性

2.getattr(object, name, default=None)

获取属性,获取object中是否有name属性,如果有则获取,如果没有则报错,如果设置default则返回default的值,不会报错。

3.setattr(object, k, v)

设置属性,设置object的属性,k为设置属性的键,v为设置属性的值。

4.delattr(object, x)

删除属性,删除object的x属性。

方法示例:

ContractedBlock.gif

ExpandedBlockStart.gif

1 #!/usr/bin/env python

2 #-*- coding: utf-8 -*-

3

4 classHouse:5 def __init__(self,length,width):6 self.length =length7 self.width =width8

9 defArea(self):10 return "房子的面积为%s平方米" %(self.length *self.width)11

12

13 h1 = House(10,20)14 #打印h1的属性字典

15 print(h1.__dict__)16 #判断是否有length属性(返回bool值)

17 print(hasattr(h1,'length')) #切记length属性是字符串类型******

18 #获取length属性(返回获取到的值,default默认是不存在,获取不到返回"不存在")

19 print(getattr(h1,'lengtha','不存在'))20 #设置属性

21 print(setattr(h1,'height','30'))22 #设置完查看

23 print(h1.__dict__)24 #删除属性(删除不存在的会报错,AttributeError)

25 delattr(h1,'length')26 #删除完查看

27 print(h1.__dict__)

属性方法示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 {'length': 10, 'width': 20}2 True3 不存在4 None5 {'length': 10, 'width': 20, 'height': '30'}6 {'width': 20, 'height': '30'}

View Code

1.3 为什么用反射

可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。

实现可插拔机制

示例:

A程序员

1 #!/usr/bin/env python

2 #-*- coding: utf-8 -*-

3

4 #A程序员写文件的方法,有读写操作,但目前只写了读的方法。

5 classFile:6 defread(self):7 print("读操作")8

9 #def write(self):

10 #print("写操作")

B程序员(B程序员不用关心A程序员是否写write方法,利用反射判断属性并执行,效率高)

#!/usr/bin/env python#-*- coding: utf-8 -*-

from test importFile

f1=File()if hasattr(f1,'write'):

f1= hasattr(f1,'write')

f1()else:print("属性不存在继续写逻辑")

2.类的特殊方法

2.1 对象的属性操作

__setattr__ 添加/修改属性会触发它的执行

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__setattr__ 添加/修改属性会触发它的执行

2 classFoo:3 x=1

4 def __init__(self,y):5 self.y=y6

7 def __setattr__(self, key, value):8 print('----> from setattr')9 #以下模拟了给对象添加属性的原理,重写了对象添加属性。

10 #self.key=value #模拟添加属性,这样做会导致递归。

11 self.__dict__[key]=value #这样做不会递归

12

13 f1=Foo(10) #实例化等于添加self.y属性

14 print(f1.__dict__) #查看其实并未添加,因为当前的__setattr__覆盖了系统的,以上加了self.__dict__[key]=value,才进行了属性的添加

15 f1.x = 20 #为对象手动添加属性

16 print(f1.__dict__)

__setattr__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 ----> fromsetattr2 {'y': 10}3 ----> fromsetattr4 {'x': 20, 'y': 10}

View Code

__delattr__ 删除属性的时候会触发

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__delattr__ 删除属性的时候会触发

2

3 classFoo:4 x=1

5 def __init__(self,y):6 self.y=y7

8 def __delattr__(self, item):9 print('----> from delattr')10 print('----> %s' %item) #item为删除的值

11 #del self.item #这样做会无限递归

12 self.__dict__.pop(item) #操作属性字典删除

13

14 f1 = Foo(10)15 print(f1.__dict__)16 del f1.y #删除属性y

17 print(f1.__dict__)

__delattr__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 {'y': 10}2 ----> fromdelattr3 ---->y4 {}

View Code

__getattr__ 只有在使用点调用属性且属性不存在的时候才会触发

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__getattr__ 只有在使用点调用属性且属性不存在的时候才会触发

2

3 classFoo:4 x=1

5 def __init__(self,y):6 self.y=y7

8 def __getattr__(self, item):9 print('----> from getattr:你找的属性不存在')10

11 f1 = Foo(10)12

13 print(f1.y) #属性存在的时候不会触发

14 print(f1.z) #属性不存在的时候会触发

__getattr__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 10

2 ----> fromgetattr:你找的属性不存在3 None

View Code

__getattribute__ 不管属性是否存在都会调用

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__getattribute__ 在使用点调用属性就会执行

2

3 classFoo:4 def __init__(self,x):5 self.x=x6

7 def __getattribute__(self, item):8 print('from------>__getattribute__')9

10 f1=Foo(10)11 #__getattribute__与__getattr__不同的是,不管属性是否存在都会调用。

12 f1.x13 f1.yyy

__getattrbute__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 from------>__getattribute__

2 from------>__getattribute__

View Code

__getattribute__ 与__getattr__ 同时出现

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__getattr__与__getattribute__共存

2

3 classFoo:4 def __init__(self,x):5 self.x=x6

7 def __getattr__(self, item):8 print('from------>__getattr__')9

10 def __getattribute__(self, item):11 print('from ------>__getattribute__')12 #raise AttributeError('没有这个属性')

13

14 f1=Foo(10)15 f1.x #共存调用有的x属性执行__getattribute__

16 f1.yyy #共存调用没有的yyy属性也执行__getattribute__

17

18 #总结:当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

__getattr__与__getattribute__共存

__getitem__:obj['属性'] 时触发

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__getitem__:obj['属性'] 时触发

2

3 classFoo:4 x=1

5 def __init__(self,y):6 self.y=y7

8 def __getitem__(self, item):9 print('from ----->__getitem__')10 print(self.__dict__[item])11

12 f1 = Foo(10)13

14 print(f1['y'])

__getitem__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 from ----->__getitem__

2 10

3 None

View Code

__setitem__:obj['属性']=属性的值 时触发

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__setitem__:obj['属性']=属性的值 时触发

2 classFoo:3 x=1

4 def __init__(self,y):5 self.y=y6

7 def __setitem__(self, key, value):8 print('from ------>__setitem__')9 self.__dict__[key] =value10

11 f1=Foo(10)12 print(f1.__dict__)13 f1['z'] = 30

14 print(f1.__dict__)

__setitem__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 {'y': 10}2 from ------>__setitem__

3 {'z': 30, 'y': 10}

View Code

__delitem__:del obj['属性'] 时触发

ContractedBlock.gif

ExpandedBlockStart.gif

1 #__delitem__:del obj['属性'] 时触发

2 classFoo:3 x=1

4 def __init__(self,y):5 self.y=y6

7 def __delitem__(self, key):8 print('from ------>__delitem__')9 self.__dict__.pop(key)10

11 f1=Foo(10)12 print(f1.__dict__)13 del f1['y']14 print(f1.__dict__)

__delitem__示例

结果:

ContractedBlock.gif

ExpandedBlockStart.gif

1 {'y': 10}

2 from ------>__delitem__

3 {}

View Code

2.2 检查对象和子类

isinstance语法:isinstance(obj,cls)

功能:检查对象obj是否是类cls的实例

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo(object):2 pass

3

4 obj =Foo()5 print(isinstance(obj, Foo)) #判断obj是否是Foo的实例

isinstance示例

issubclass语法:issubclass(sub,super)

功能:检查sub类是否是super类的派生类

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo(object):2 pass

3

4 classBar(Foo):5 pass

6

7 print(issubclass(Bar,Foo)) #判断Bar是否是Foo的子类

issubclass示例

2.3 __str__与__repr__

__str__与__repr__是控制输出的,__str__是控制print输出的,__repr__是控制控制台输出的

str函数或者print函数--->obj.__str__()

repr或者交互式解释器--->obj.__repr__()

注意:必须以return结尾,并且return的值只能是字符串

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5 #自定义str

6 def __str__(self):7 return "name is %s,age is %s" %(self.name,self.age)8

9 f1 = Foo('zhangsan',23)10

11 print(f1) #str(f1)--->f1.__str__() #print调用顺序

12 #这里不定义__str__会显示<__main__.Foo object at 0x0000025B3895B668>

13 #定制str会显示

14 #name is zhangsan,age is 23

__str__示例

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5 #自定义repr

6 def __repr__(self):7 return "name is %s,age is %s" %(self.name,self.age)8

9 f1 = Foo('zhangsan',23)10

11 print(f1) #repr(f1)--->f1.__repr__() #调用顺序

12

13 ###############控制台示例#####################

14 >>>

15 >>> classFoo:16 ... def __init__(self,name,age):17 ... self.name =name18 ... self.age =age19 ... def __repr__(self):20 ... return "name is %s,age is %s" %(self.name,self.age)21 ...22 >>> f1 = Foo('zhangsan',23)23 >>>f124 name is zhangsan,age is 23

25 >>>

__repr__示例

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5

6 def __str__(self):7 return ("from---->str")8

9 def __repr__(self):10 return "name is %s,age is %s" %(self.name,self.age)11

12 f1 = Foo('zhangsan',23)13

14 print(f1) #共存调用顺序,str(f1)--->f1.__str__()--->f1.__repr__()

__str__与__repr__共存

2.4自定制format

ContractedBlock.gif

ExpandedBlockStart.gif

1 format_dic ={2 'ymd':'{0.year} {0.month} {0.day}',3 'y-m-d':'{0.year}-{0.month}-{0.day}',4 'm:d:y':'{0.month}:{0.day}:{0.year}',5 'd/m/y':'{0.day}/{0.month}/{0.year}',6 }7

8 classDate:9 def __init__(self,year,month,day):10 self.year =year11 self.month =month12 self.day =day13

14 def __format__(self, format_spec):15 if not format_spec or format_spec not informat_dic:16 format_spec = 'ymd'

17 fm =format_dic[format_spec]18 returnfm.format(self)19 #return format_dic[format_spec].format(self) #整合

20

21

22 d1 = Date('2017','07','07')23 x = format(d1,'ymd')24 y = format(d1,'y-m-d')25 z = format(d1,'d/m/y')26 print(x)27 print(y)28 print(z)29

30 ##############结果##############

31 2017 07 07

32 2017-07-07

33 07/07/2017

自定义时间格式(__format__)

2.5__slots__

什么是__slots__?

定义:__slots__是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

为什么使用__slots__?

前面学过不管是访问类的属性还是实例的属性其实都是对属性字典__dict__来做操作,但是类的属性字典是共享的,实例的属性字典是独立的;如果一个类属性很少,会占用一个字典,而这个类生成的多个实例会生成多个字典,字典一多,就会占用大量的内存,为了节省内存,可以使用__slots__来代替__dict__,这样所有实例的属性字典就会是一个__slots__,减少了内存的占用。

使用__slots__的优缺点。

优点:节省内存空间。

缺点:不能给实例添加新的属性,如果添加,只能在定义__slots__里面定义。

__slots__应该用在哪?

__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。

__slots__使用误区

关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 __slots__ = 'name' #定义单个属性,没有定义后面就不能添加。

3 #__slots__ = ['name','age'] #定义多个属性,没有定义后面就不能添加。

4

5 f1 =Foo()6 f1.name = 'zhangsan'

7 #print(f1.__dict__) #当使用__slots__的时候,就没有属性字典了,这里会报错,(AttributeError: 'Foo' object has no attribute '__dict__')

8 print(f1.__slots__) #name

9 #f1.age = 23 #如果__slots__没有这个属性,这里会报错(AttributeError: 'Foo' object has no attribute 'age')

10

11

12 f2 =Foo()13 f2.name = 'lisi'

14 print(f2.__dict__) #当使用__slots__的时候,就没有属性字典了,这里会报错,(AttributeError: 'Foo' object has no attribute '__dict__')

15 print(f2.__slots__) #name

16

17 #总结:f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

__slots__使用示例

2.6__doc__

__doc__就是文档描述符,用来描述类或方法的作用,可以任意定义,自己能看懂就好。

特点:无法继承

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 '我是描述信息'

3 pass

4

5 classBar(Foo):6 pass

7 print(Bar.__doc__) #该属性无法继承给子类

__doc__示例

2.7__module__和__class__

__module__ 表示当前操作的对象在那个模块

__class__ 表示当前操作的对象的类是什么

创建test/test.py

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,name):3 self.name = name

test/test.py

导入test.py模块

ContractedBlock.gif

ExpandedBlockStart.gif

1 from test.test importFoo2

3 obj =Foo()4 print obj.__module__ #输出 test.test,即:输出模块

5 print obj.__class__ #输出 test.test.Foo,即:输出类

test.py导入

2.8__del__ 析构方法

定义:析构方法,当对象在内存中被释放时,自动触发执行。

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,name):3 self.name =name4

5 def __del__(self):6 print("from ------>__del__")7

8 f1 = Foo('zhangsan')9 print('--------------程序执行完------------>')10

11

12 #结果

13 --------------程序执行完------------>

14 from ------>__del__

15

16 #结果可以看出程序执行完毕,触发析构函数。

示例一

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,name):3 self.name =name4

5 def __del__(self):6 print("from ------>__del__")7

8 f1 = Foo('zhangsan')9 delf110 print('--------------程序执行完------------>')11

12 #结果

13 from ------>__del__

14 --------------程序执行完------------>

15

16 #以上可以看出,删除f1实例,触发析构函数,然后程序执行完毕。

示例二

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

2.9 __call__

对象后面加括号,触发执行。

ContractedBlock.gif

ExpandedBlockStart.gif

1 #!/usr/bin/env python

2 #-*- coding: utf-8 -*-

3

4 classFoo:5 def __init__(self,name):6 self.name =name7

8 def __call__(self, *args, **kwargs):9 print('from------>__call__')10

11 f1 = Foo('zhangsan')12

13

14 f1() #执行Foo下的__call__方法

15 Foo('zhangsan')() #等同于f1(),执行Foo下的__call__方法

16 Foo('zhangsan') #例如Foo()触发执行的是abc下的__call__方法。

17

18 结果#19 from------>__call__

20 from------>__call__

__call__示例

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

2.10 __next__和__iter__实现迭代器协议

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self,x):3 self.x=x4

5 def __iter__(self):6 returnself7

8 def __next__(self):9 n=self.x10 self.x+=1

11 returnself.x12

13 f=Foo(3)14 for i inf:15 print(i)16

17 #1.首先要有__iter__,生成迭代器对象的时候会调用

18 #2.要有__next__方法,因为可以被next()或__next__()函数调用并不断返回下一个值(直到没有数据时抛出StopIteration错误)的对象称为迭代器

19 #3.强大的for循环遇到StopIteration会自动停止,并且不会报错。

迭代器实现的基本过程

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __init__(self):3 self.x = 1

4 self.y = 1

5

6 def __iter__(self):7 returnself8

9 def __next__(self):10 self.x,self.y = self.y,self.x +self.y11 if self.x > 20:12 raise StopIteration('异常了。。。')13 returnself.x14

15 f1 =Foo()16

17 #print(f1.__next__())

18 #print(f1.__next__())

19 #print(f1.__next__())

20 #print(f1.__next__())

21 #print(f1.__next__())

22 #print(f1.__next__())

23 #print(f1.__next__())

24 #print(f1.__next__())

25

26 for i inf1:27 print(i)

迭代器实现斐波那契

3.描述符

1.描述符是什么?

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议;python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。如果__get__(),__set__(),__delete__()被定义在一个对象中,这个对象就是一个描述符。

定义描述符

ContractedBlock.gif

ExpandedBlockStart.gif

1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符(实现任意一个都被称作描述符)

2 def __get__(self, instance, owner):3 pass

4 def __set__(self, instance, value):5 pass

6 def __delete__(self, instance):7 pass

描述符的定义

2.描述符是做什么的?

描述符的作用是用来代理另外一个类的属性的(注意:必须把描述符定义成这个类的类属性,不能定义到构造函数中)

为什么说是代理属性呢?

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __get__(self, instance, owner):3 print('触发get')4 def __set__(self, instance, value):5 print('触发set')6 def __delete__(self, instance):7 print('触发delete')8

9 #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法

10 f1=Foo()11 f1.name='zhangsan'

12 f1.name13 del f1.name

示例:先看描述符类产生的实例进行属性操作

ContractedBlock.gif

ExpandedBlockStart.gif

1 #描述符Str

2 classStr:3 def __get__(self, instance, owner):4 print('Str调用...')5 def __set__(self, instance, value):6 print('Str设置...')7 def __delete__(self, instance):8 print('Str删除...')9

10 #描述符Int

11 classInt:12 def __get__(self, instance, owner):13 print('Int调用...')14 def __set__(self, instance, value):15 print('Int设置...')16 def __delete__(self, instance):17 print('Int删除...')18

19 classPeople:20 name=Str() #Str描述符用在这里

21 age=Int() #Int描述符用在这里

22 def __init__(self,name,age): #name被Str类代理,age被Int类代理

23 self.name=name24 self.age=age25

26 #实例化的时候会调用,zhangsan--->name(因为name被Str代理)--->Str--->触发Str下的__set__方法(因为实例化本身就是把zhangsan赋值给name,是设置操作)

27 #age和name相同只不过是被Int代理。

28 p1=People('zhangsan',23)29 #被代理的属性(name和age,在示例本身属性字典里查不到)

30 print(p1.__dict__)31 #实例name属性被Str代理,调用/赋值/删除会触发Str描述符的方法

32 print('------>描述符Str的使用')33 p1.name34 p1.name='zhangsan'

35 #因为name被代理,所以代理里面没有操作,属性字典为空

36 print(p1.__dict__)37 delp1.name38 #实例age属性被Int代理,调用/赋值/删除会触发Int描述符的方法

39 print('------>描述符Int的使用')40 p1.age41 p1.age=23

42 #因为name被代理,所以代理里面没有操作,属性字典为空

43 print(p1.__dict__)44 delp1.age45 print(type(p1))

示例:再来看描述符代理类的属性操作

ContractedBlock.gif

ExpandedBlockStart.gif

1 Str设置...2 Int设置...3 {}4 ------>描述符Str的使用5 Str调用...6 Str设置...7 {}8 Str删除...9 ------>描述符Int的使用10 Int调用...11 Int设置...12 {}13 Int删除...

View Code

总结:描述符就是来代理其他的类的属性操作的。

3.描述符的种类

描述符分为两种

1.数据描述符:至少实现了__get__()和__set__()

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __set__(self, instance, value):3 print('set')4 def __get__(self, instance, owner):5 print('get')

数据描述符示例

2.非数据描述符:没有实现__set__()

ContractedBlock.gif

ExpandedBlockStart.gif

1 classFoo:2 def __get__(self, instance, owner):3 print('get')

非数据描述符示例

4.使用描述符要注意的事项*****

1.描述符本身应该定义成新式类,被代理的类也应该是新式类***

2.必须把描述符定义成这个类的类属性,不能为定义到构造函数中(__init__())***

3. 要严格遵循该优先级,优先级由高到底分别是

1) 类属性

2) 数据描述符

3) 实例属性

4) 非数据描述符

5) 找不到的属性触发__getattr__()

ContractedBlock.gif

ExpandedBlockStart.gif

1 #描述符Str,因为至少包含了__get__和__set__,所以具体可以称为数据描述符。

2 classStr:3 def __get__(self, instance, owner):4 print('Str调用...')5 def __set__(self, instance, value):6 print('Str设置...')7 def __delete__(self, instance):8 print('Str删除...')9

10 classPeople:11 name=Str()12 def __init__(self,name,age): #name被Str类代理,age被Int类代理

13 self.name=name14 self.age=age15

16 #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典

17 #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,这里做下实验。

18 People.name #测试通过,可以调用。但是本质就是在调用描述符Str,触发了__get__(),但并不能证明类(属性>数据描述符)

19 #再来看赋值和删除

20 People.name = 'zhangsan' #没有触发__set__方法

21 del People.name #没有触发__delete__方法

22

23 '''

24 结论:从类对属性的赋值和删除的时候,并没有触发__set__和__delete__方法,说明类对属性的操作要高于描述符。25 '''

26

27 类属性>数据描述符

类属性>数据描述符

ContractedBlock.gif

ExpandedBlockStart.gif

1 #描述符Str,因为至少包含了__get__和__set__,所以具体可以称为数据描述符。

2 classStr:3 def __get__(self, instance, owner):4 print('Str调用...')5 def __set__(self, instance, value):6 print('Str设置...')7 def __delete__(self, instance):8 print('Str删除...')9

10 classPeople:11 name=Str()12 def __init__(self,name,age): #name被Str类代理

13 self.name=name14 self.age=age15

16 #这里定义一个实例

17 p1=People('zhangsan',23)18

19 #这里用实例对属性的调用/赋值/删除来判断实例属性和数据描述符的优先级

20 print("开始测试......")21 p1.name22 p1.name='lisi'

23 delp1.name24 '''

25 结果:26 Str设置...27 开始测试......28 Str调用...29 Str设置...30 Str删除...31

32 结论:数据描述符>实例属性33 '''

数据描述符>实例属性

ContractedBlock.gif

ExpandedBlockStart.gif

1 #描述符Str,因没有__set__方法,所以具体可以称为非数据描述符。

2 classStr:3 def __get__(self, instance, owner):4 print('Str调用...')5

6 classPeople:7 name=Str()8 def __init__(self,name,age): #name被Str类代理

9 self.name=name10 self.age=age11

12 #这里定义一个实例

13 p1=People('zhangsan',23)14

15 #这里用调用示例属性的方式来判断实例属性和非数据描述符的优先级

16 p1.name #并没有触发Str的__get__方法。

17 print(p1.name) #可以找到,说明调用的是实例的属性字典。

18

19 '''

20 结果:zhangsan21 结论:实例属性>非数据描述符22 '''

实例属性>非数据描述符

5.描述符的使用

python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

ContractedBlock.gif

ExpandedBlockStart.gif

1 classTyped:2 def __init__(self,name,expected_type):3 self.name=name4 self.expected_type=expected_type5

6 def __get__(self, instance, owner):7 if instance isNone:8 returnself9 return instance.__dict__[self.name]10

11 def __set__(self, instance, value):12 if notisinstance(value,self.expected_type):13 raise TypeError('当前类型是:%s 需要改为:%s' %(type(value),str(self.expected_type)))14 instance.__dict__[self.name]=value15

16 def __delete__(self, instance):17 instance.__dict__.pop(self.name)18

19 classPeople:20 name=Typed('name',str)21 age=Typed('age',int)22 salary=Typed('name',float)23 def __init__(self,name,age,salary):24 self.name=name25 self.age=age26 self.salary=salary27

28 #p1=People(123,18,3333.3)

29 #p1=People('zhangsan','18',3333.3)

30 #p1=People('zhangsan',18,3333)

实例化类型限制

以上Typed属于调用重复的代码,这里可以用类的装饰器实现

类的装饰器分类:

ContractedBlock.gif

ExpandedBlockStart.gif

1 defdecorate(cls):2 print('类的装饰器开始运行啦------>')3 returncls4

5 @decorate #无参:People=decorate(People)

6 classPeople:7 def __init__(self,name,age,salary):8 self.name=name9 self.age=age10 self.salary=salary11

12 p1=People('zhangsan',23,3333.3)

类的装饰器:无参

ContractedBlock.gif

ExpandedBlockStart.gif

1 def typeassert(**kwargs):2 defdecorate(cls):3 print('类的装饰器开始运行啦------>',kwargs)4 returncls5 returndecorate6 @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)

7 classPeople:8 def __init__(self,name,age,salary):9 self.name=name10 self.age=age11 self.salary=salary12

13 p1=People('zhangsan',23,3333.3)

类的装饰器:有参

ContractedBlock.gif

ExpandedBlockStart.gif

1 classTyped:2 def __init__(self,name,expected_type):3 self.name=name4 self.expected_type=expected_type5 def __get__(self, instance, owner):6 print('get--->',instance,owner)7 if instance isNone:8 returnself9 return instance.__dict__[self.name]10

11 def __set__(self, instance, value):12 print('set--->',instance,value)13 if notisinstance(value,self.expected_type):14 raise TypeError('Expected %s' %str(self.expected_type))15 instance.__dict__[self.name]=value16 def __delete__(self, instance):17 print('delete--->',instance)18 instance.__dict__.pop(self.name)19

20 def typeassert(**kwargs):21 defdecorate(cls):22 print('类的装饰器开始运行啦------>',kwargs)23 for name,expected_type inkwargs.items():24 setattr(cls,name,Typed(name,expected_type))25 returncls26 returndecorate27 @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)

28 classPeople:29 def __init__(self,name,age,salary):30 self.name=name31 self.age=age32 self.salary=salary33

34 print(People.__dict__)35 p1=People('zhangsan',23,3333.3)

实例化类型限制(装饰器实现)

6.描述符实战

6.1 自制@property

ContractedBlock.gif

ExpandedBlockStart.gif

1 classLazyproperty:2 def __init__(self,func):3 self.func=func4 def __get__(self, instance, owner):5 print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')6 if instance isNone:7 returnself8 return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情

9

10 classRoom:11 def __init__(self,name,width,length):12 self.name=name13 self.width=width14 self.length=length15

16 @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符

17 defarea(self):18 return self.width *self.length19

20 r1=Room('alex',1,1)21 print(r1.area)

自制@property

ContractedBlock.gif

ExpandedBlockStart.gif

1 classLazyproperty:2 def __init__(self,func):3 self.func=func4 def __get__(self, instance, owner):5 print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')6 if instance isNone:7 returnself8 else:9 print('--->')10 value=self.func(instance)11 setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中

12 returnvalue13

14 classRoom:15 def __init__(self,name,width,length):16 self.name=name17 self.width=width18 self.length=length19

20 @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'

21 defarea(self):22 return self.width *self.length23

24 r1=Room('alex',1,1)25 print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法

26 print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算

实现延迟计算功能

6.2 自制@classmethod

ContractedBlock.gif

ExpandedBlockStart.gif

1 classClassMethod:2 def __init__(self,func):3 self.func=func4

5 def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,

6 deffeedback():7 print('在这里添加功能...')8 returnself.func(owner)9 returnfeedback10

11 classPeople:12 name='lisi'

13 @ClassMethod #say_hi=ClassMethod(say_hi)

14 defsay_hi(cls):15 print('你好 %s' %cls.name)16

17 People.say_hi()18

19 p1=People()20 p1.say_hi()21 #疑问,类方法如果有参数呢

22

23 classClassMethod:24 def __init__(self,func):25 self.func=func26

27 def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,

28 def feedback(*args,**kwargs):29 print('在这里添加功能...')30 return self.func(owner,*args,**kwargs)31 returnfeedback32

33 classPeople:34 name='lisi'

35 @ClassMethod #say_hi=ClassMethod(say_hi)

36 defsay_hi(cls,msg):37 print('你好 %s %s' %(cls.name,msg))38

39 People.say_hi('见到你很高兴')40

41 p1=People()42 p1.say_hi('见到你很高兴')

自制@classmethod

6.3 自制@staticmethod

ContractedBlock.gif

ExpandedBlockStart.gif

1 classStaticMethod:2 def __init__(self,func):3 self.func=func4

5 def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,

6 def feedback(*args,**kwargs):7 print('在这里可以加功能啊...')8 return self.func(*args,**kwargs)9 returnfeedback10

11 classPeople:12 @StaticMethod#say_hi=StaticMethod(say_hi)

13 defsay_hi(x,y,z):14 print('------>',x,y,z)15

16 People.say_hi(1,2,3)17

18 p1=People()19 p1.say_hi(4,5,6)

自制@staticmethod

7.描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

4.上下文管理器

4.1 什么是上下文管理器?

在使用Python编程中,可以会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作;当语句块执行完成后,需要继续执行一些收尾动作。这就是上下文管理器。

4.2 为什么要使用?用在哪?

示例1:

当需要操作文件或数据库的时候,首先需要获取文件句柄或者数据库连接对象,当执行完相应的操作后,需要执行释放文件句柄或者关闭数据库连接的动作。

示例2:

当多线程程序需要访问临界资源的时候,线程首先需要获取互斥锁,当执行完成并准备退出临界区的时候,需要释放互斥锁。

对于这些情况,Python中提供了上下文管理器(Context Manager)的概念,可以通过上下文管理器来定义/控制代码块执行前的准备动作,以及执行后的收尾动作。

4.3 上下文管理协议又是什么?

当我们需要创建一个上下文管理器类型的时候

必须要实现:

__enter__方法(上文)

__exit__方法(下文)

这对方法就称为上下文管理协议(Context Manager Protocol),定义了一种运行时上下文环境。

4.4 如何使用上下文管理?

这里利用文件操作with open 来理解上下文管理的用法。

我们在操作文件对象的时候可以这可写,如下

1 with open('a.txt') as f:2   '代码块'

这里的with open('a.txt')代表的意思是打开一个文件句柄,as f的意思是赋值给f,这个过程就是上下文管理协议。

这里来模拟with open的实现,来理解如何使用。

ContractedBlock.gif

ExpandedBlockStart.gif

1 '''

2 with obj as f 做的是f = obj.__enter__(),with obj 是触发obj.__enter__拿到返回值,as f是赋值给f3 执行代码块:4 1.没有异常的情况,整个代码块执行完成后触发__exit__方法,它的三个参数都为None5 2.有异常的情况,会从异常出现的位置直接触发__exit__的运行、6 a:如果__exit__的返回值为True,代表吞掉异常7 b:如果__exit__的返回值不为True,代表吐出异常8 c:__exit__的运行完毕就代表了整个with语句的执行完毕。9

10 '''

11 #没有异常

12 classOpen1:13

14 def __init__(self,name):15 self.name =name16

17 def __enter__(self):18 print("执行enter")19 returnself20

21 def __exit__(self, exc_type, exc_val, exc_tb):22 print("执行exit")23 print("参数开始------------------>")24 print(exc_type)25 print(exc_val)26 print(exc_tb)27 print("参数结束------------------>")28

29 with Open1('a.txt') as f:30 pass

31

32 print('='*50)33

34 #有异常

35 classOpen:36

37 def __init__(self,name):38 self.name =name39

40 def __enter__(self):41 print("执行enter")42 returnself43

44 def __exit__(self, exc_type, exc_val, exc_tb):45 print("执行exit")46 print("参数开始------------------>")47 print(exc_type)48 print(exc_val)49 print(exc_tb)50 print("参数结束------------------>")51 #return True #这里代表吞掉异常。开启就会吞掉异常

52

53 with Open('a.txt') as f:54 raise AttributeError("出错了。。。")55 print('执行结束。。。。。。。。。。。。。') #如果吞掉异常,这里会执行,如果吐出异常这里不会执行

56

57 模拟with open的实现过程

模拟with open的实现过程

ContractedBlock.gif

ExpandedBlockStart.gif

1 classOpen:2 def __init__(self,filepath,mode='r',encoding='utf-8'):3 self.filepath=filepath4 self.mode=mode5 self.encoding=encoding6

7 def __enter__(self):8 #print('enter')

9 self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)10 returnself.f11

12 def __exit__(self, exc_type, exc_val, exc_tb):13 #print('exit')

14 self.f.close()15 returnTrue16 def __getattr__(self, item):17 returngetattr(self.f,item)18

19 with Open('a.txt','w') as f:20 print(f)21 f.write('aaaaaa')22 f.asdfsdf #抛出异常,交给__exit__处理

实现一个wtih Open

5.元类(metaclass)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值