Python中的面向对象(进阶)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python中的面向对象(进阶)相关的知识,希望对你有一定的参考价值。
参考技术A一、实例方法,类方法,静态方法
我们首先写一个类,里面包含这三种方法。
可以看到,我们用到了两个装饰器。
我们用类和实例分别调用下类方法
我们用类和实例分别调用下静态方法
静态方法其实就是把一个普通的函数写在类里,与直接在外层写一个函数是一样的,本质上是一个函数。
为了方便理解,我们分别打印下这些方法的类型
通过type()查看对象是方法还是函数
此外,还可以通过inspect模块判断某个对象是否是某种类型,返回布尔值。
用法
小Tips:概念理解
直接def定义的,我们叫做函数
把函数放到类里,我们叫做方法
方法可以通过装饰器staticmethod转为(放在方法里的)函数
继承
一个类继承另一个类时,会自动获得另一个类的所有属性和方法,被继承的类称之为父类,新类称为子类。子类拥有父类所有的属性和方法,并且可以定义自己的属性和方法
我们以上边的Rectangle类为父类来试一下
1)完全继承
可以看到,子类完全继承父类后,可以直接调用父类的所有方法。
2)部分继承
部分继承:继承父类后,修改父类的同名方法
我们试一下,Square继承Rectangle后,修改__init__()方法
3)拓展父类的方法
在保留父类中某个方法的代码同时,对方法进行拓展
可以在方法中加入"super().方法名"来实现
4)@property
python-----面向对象进阶
一、反射
概念:主要是指程序可以访问、检测和修改它本身状态或者行为的一种能力(自省)
python面向对象中的反射:通过字符串的形式操作对象的相关属性。python中的一切事物皆对象(都可以使用反射)
一、四个可以实现自省的函数:
hasattr(obj,‘属性名‘) 检测对象是否含有某属性
getattr(obj,‘属性名’,100)通过字符串类型获取对象的某个属性,不存在则会报错,可定义第三个参数的默认值,不存在则会显示第三个参数,不会报错。
setattr(obj,‘属性名‘,属性)设置属性,若属性不存在则会新添新的属性,存在则会修改为新的属性
delattr(obj,‘属性名’)删除对象的某个属性,不存在则会报错
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/1 13:09‘ 4 # class BlackMedium: 5 # feture=‘ugly‘ 6 # def __init__(self,name,addr): 7 # self.name=name 8 # self.addr=addr 9 # def rent_hourse(self): 10 # print(‘[%s]出租房子,不要去租!‘%self.name) 11 # def sell_hourse(self): 12 # print(‘[%s]卖房子,不要去买!‘%self.name) 13 # b=BlackMedium(‘万成置地‘,‘天露园‘) 14 # print(hasattr(b,‘name‘)) #有没有name属性 找的是对应的key 15 # print(hasattr(b,‘addr‘)) 16 17 # print(getattr(b,‘name‘)) #万成置地 得到这个对象的属性值===相当于b.name 18 # getattr(b,‘sell_hourse‘)() #同样也可以 19 # print(getattr(b,‘age‘)) #没有这个属性就会报错,不过可以传一个默认值 20 # print(getattr(b,‘age‘,23)) #没有这个属性值会显示默认的值,不会报错 23 21 22 # setattr(b,‘name‘,‘小黑‘) 23 # print(b.__dict__) #name的值就从万成置地变为小黑 修改了一个属性的值 24 # setattr(b,‘age‘,5) 25 # # print(b.__dict__) #{‘name‘: ‘万成置地‘, ‘addr‘: ‘天露园‘, ‘age‘: 5} 添加了一个属性 26 # # 27 # # delattr(b,‘age‘) 28 # # print(b.__dict__) #删除了一个属性/2反射的优点:
二、反射的优点
1、通过反射,可以实现即插即用,定义接口的功能(可以事先把逻辑写好(只定义接口),然后后期再去实现接口功能)
‘alex’应该写的代码
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/1 14:16‘ 4 class FtpClient: 5 ‘ftp客户端,但没有实现功能‘ 6 def __init__(self,addr): 7 self.addr=addr 8 def load(self): 9 print(‘上传照片‘)
‘listen’应该写的代码
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/1 14:16‘ 4 #1通过反射,可以实现即插即用,定义接口的功能(可以事先把主要逻辑写好(只定义接口),后期再去实现接口功能。) 5 # from alex_module import FtpClient 6 # from alex_module import * 7 # f1=FtpClient(‘172.16.1.3‘) 8 # if hasattr(f1,‘load‘): 9 # f1.load() #‘FtpClient‘ object has no attribute ‘load‘ 报错,但不影响,两个人可以相互独立工作,等第一个人写好了,直接调用了 10 # else: 11 # print(‘没有load这个方法‘)
这样实现代码互不影响。
2、动态导入模块(基于反射当前的模块成员)
1 #2 动态导入模块 底层基于反射 包中__init__文件 2 # from hell.test import * #一导入模块直接运行模块中的内容 3 # from hell.test import * #内容中有私有的属性 _test1() 4 # from hell.test import test,_test1,test2 #这样就可以运行了 *对于私有的导不进去,只能一个一个把函数导进去 5 # test() #这个可以运行 6 # _test1() #这个运行不了 7 # test2() 8 #运行结果: 9 # ----------》hello 10 # --->test1 11 # --->test2 12 # --->test3 13 14 #模块是一个字符换‘hell.test‘ 怎么导入字符串模块 15 # module=__import__(‘hell.test‘) 16 # print(module) #只能识别到hell模块中不能再识别下一级模块 17 18 19 # import importlib 20 # m=importlib.import_module(‘hell.test‘) #可以识别到hell模块的下一级test 21 # # print(m)
三、__setattr__、__getattr__、__delattr__内置的方法
__getattr__在使用的时候,没有实例的这个属性的时候才会被触发,有实例的属性不会触发这个方法。------>>>最常用的方法
1 class Foo: 2 country=‘china‘ 3 def __init__(self,name): 4 self.name=name 5 # def __getattr__(self, item): #item就是不存在的属性 6 # print(‘执行__getattr__‘) 7 f=Foo(‘alex‘) #设置一遍执行一遍 8 # # print(f.name) 9 # # print(f.age) #如果没有这个属性 会触发__getattr__ 有这个属性则不会触发
__setattr__:在设置值得时候就会触发 -------------->设置属性的过程,可以里面自定义属性
1 class Foo: 2 country=‘china‘ 3 def __init__(self,name): 4 self.name=name 5 # 6 # def __setattr__(self, key, value): 7 # print(‘执行__setattr__‘)
#直到这没有意义不会添加属性到属性字典中,内置的__setattr__会添加属性字典中
8 # # self.key=value 错误用法 9 # self.__dict__[key]=value #在底层字典设置,本来就在底层操作字典 10 11 f=Foo(‘alex‘) #设置一遍执行一遍 12 # f.age=18 13 # f.sex=‘male‘ 14 # print(f.__dict__)
__delattr__删除属性的时候会触发
1 class Foo: 2 country=‘china‘ 3 def __init__(self,name): 4 self.name=name 5 6 # 7 def __delattr__(self, item): 8 print(‘执行__delattr__‘) 9 self.__dict__.pop(item) 10 f=Foo(‘alex‘) #设置一遍执行一遍 11 print(f.__dict__) 12 del f.name #只要删除就会触发 13 print(f.__dict__)
四、包装和授权
包装:python为我们提供了标准数据类型,以及丰富的内置方法,其实很多场景下,我们都需要基于标准的数据类型来定制我们自己的数据类型,新增或改写方法,这就需要继承和派生了(其他标准类型均可以通过下面的方式进行二次加工)
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/2 8:51‘ 4 #包装 5 #继承、派生、改写父类的方法 6 class List(list): 7 #改写父类list的方法 8 def append(self, object): 9 if type(object) is str: 10 # return list.append(self,object) #调用父类的方法 11 return super().append(object) #不用传self 12 else: 13 return ‘不是字符串类型,不能添加成功!‘ 14 #取中间值 派生 15 def get_middle(self): 16 mid_num=int(len(self)/2) 17 return self[mid_num] 18 19 l=List(‘1234567‘) 20 # l.append(‘b‘) 21 print(l.get_middle())
授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有的功能,其他的保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象默认属性。
实现授权的关键就是覆盖__getattr__方法
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/2 9:23‘ 4 import time 5 #授权 6 class FileHandle: 7 def __init__(self,filename,mode,encoding=‘utf-8‘): 8 self.file=open(filename,mode,encoding=encoding) #类似于组合 9 self.mode=mode 10 self.encoding=encoding 11 12 #实现重写功能 13 # def write(self,line): 14 # print(‘不允许写入‘,line) #自己有这个方法先执行自己的方法 15 #实现加上时间的操作 16 def write(self,line): 17 t=time.strftime(‘%Y-%m-%d %X‘) 18 self.file.write(‘%s%s‘%(t,line)) 19 20 #实现了继承功能 21 def __getattr__(self,item): 22 # print(‘----->‘,item) 23 # print(type(item)) #str类型变为属性 getattr()可以做到 24 return getattr(self.file,item) 25 26 f=FileHandle(‘a.txt‘,‘r+‘) 27 f.write(‘负载过大 ‘) 28 f.write(‘内存不足 ‘) 29 f.write(‘硬盘内存不足‘) 30 # f.seek(0) 31 # print(f.read())
五、__getattrabute__用法
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/2 12:56‘ 4 class Foo: 5 def __init__(self,name): 6 self.name=name 7 def __getattr__(self, item): #小弟 8 print(‘----->from getattr‘) 9 def __getattribute__(self, item): #大哥 10 print(‘----------->from getattribute‘) 11 raise AttributeError(‘抛出异常了!‘) #attribute和attr的一个通信 12 f=Foo(‘alex‘) 13 # f.name #有这个属性执行getattribute 14 f.age #没有这个属性也执行 getattribute 没执行getattr 但是加上AttributeError之后,大哥和小弟之间就可以通话了,然后再去找attr,然后就执行attr,当没定义getattrate,系统就会调用自己的getattrate,然后看的是只执行了attr(其实内部已经执行了getattrabute)
六、__setitem__、__getitem__、__delitem__
字典设置属性
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/2 13:35‘ 4 # . 形式的操作是会触发__setattr__ __getattr__ __delattr__ 字典的形式是操作__setitem__ __getitem__ __delitem__ 5 #都是以字典的形式找、删、改 6 class Foo: 7 def __init__(self,name): 8 self.name=name 9 def __getitem__(self, item): 10 print(‘--------->from getitem‘) 11 return self.__dict__[item] 12 13 def __setitem__(self, key, value): 14 print(‘---->from setitem‘) 15 self.__dict__[key]=value 16 17 def __delitem__(self, key): 18 print(‘----->from delitem‘) 19 self.__dict__.pop(key) 20 21 f=Foo(‘alex‘) 22 f[‘age‘]=17 #触发setitem 添加一个新的属性age 23 print(f[‘name‘]) #触发getitem 寻找属性的时候 24 del f[‘age‘] #会触发delitem 删除属性 25 print(f.__dict__)
七、__str__、__repr__、__format__ ====str() repr() format()
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/2 13:49‘ 4 #__str__方法 5 # class Foo: 6 # def __init__(self,name,age): 7 # self.name=name 8 # self.age=age 9 # def __str__(self): 10 # return ‘名字是%s,年龄是%s‘%(self.name,self.age) 11 # 12 # f=Foo(‘listen‘,18) 13 # print(f) #<__main__.Foo object at 0x000001A72D16BCC0>内存地址 不添加__str__方法 14 # print(f) #添加了__str__方法 --》名字是listen,年龄是18 str(obj)===obj.__str__() 和len(l)===l.__len__() print打印 其实是调用__str__()方法 15 16 #__repr__方法 17 class Foo: 18 def __init__(self,name,age): 19 self.name=name 20 self.age=age 21 def __repr__(self): 22 return ‘名字n是%s,年龄a是%s‘%(self.name,self.age) 23 24 f=Foo(‘listen‘,18) 25 print(f) #名字n是listen,年龄a是18 print一打印,先找__str__方法,没有定义的__str__方法如果有__repr__,再去用__repr__代替
__format__ 格式化字符串
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/2 14:40‘ 4 format_dic={ 5 ‘ymd‘:‘{0.year}{0.month}{0.day}‘, 6 ‘y:m:d‘:‘{0.year}:{0.month}:{0.day}‘, 7 ‘y-m-d‘:‘{0.year}-{0.month}-{0.day}‘ 8 } 9 10 class Date: 11 def __init__(self,year,month,day): 12 self.year=year 13 self.month=month 14 self.day=day 15 def __format__(self, format_spec): 16 # print(‘我开始执行了‘) 17 if not format_spec or format_spec not in format_dic: 18 n=format_dic[‘y:m:d‘] 19 return n.format(self) 20 m=format_dic[format_spec] 21 return m.format(self) 22 23 24 d=Date(2018,12,2) 25 # print(format(d,‘2‘)) #得到的是一个format_spec 26 # ymd=‘{0.year}{0.month}{0.day}‘.format(d) #format(d)===d.__format__() 27 # ydm=‘{0.year}:{0.month}:{0.day}‘.format(d) 28 # dym=‘{0.year}-{0.month}-{0.day}‘.format(d) 29 # print(ydm) 30 print(format(d,‘y-m-d‘)) #2018-12-2 触发内部的__format__方法 31 print(format(d,‘ymd‘)) #2018122 32 print(format(d,‘asd‘)) 33 print(format(d)) #2018:12:2
八、isinstance(obj,cls)和issubclass(sub,super)
isinstance(p,A) 判断p是否是类A的对象,返回布尔值
issubclass(B,A) 判断类B是否继承类A,即B是否是A的派生类返回布尔值。
1 class A: 2 pass 3 class B(A): 4 pass 5 p=A() 6 print(isinstance(p,A)) #True 7 print(issubclass(B,A)) #True
九、__slots__
1、__slots__是什么:是一个类变量,变量值可以为列表、元组、或者可迭代对象,也可以为一个字符串(意味着所有实例只有一个数据属性)
2、引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的而实例的属性是独立的)
3、使用__slots__优点:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更紧凑的内部表示。实例通过一个很凶啊的固定大小的数组来构建,而不是为每个实例定义一个。
字典,这跟列表和元组很类似,在__slots__中列出的属性名在内部被映射到这个数组的小标上。使用__slots__一个不好的地方就是我们不能在给实例添加新的属性,只能使用在__slots__定义的属性。
4、注意事项:__slots__的很多特性依赖于普通字典的实现。另外,定义了__slots__后的类不再支持一些普通的特性,比如多继承,大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如在程序中需要创建某个类的成千上百个实例对象。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管可以达到这样的目的,但并不是它的初衷。 更多的是用来作为一个内存优化的用具。
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/3 21:03‘ 4 # #类的属性很少 但创建了好多实例 为了防止内存利用率低,可这样节省内存 取消实例字典的功能 但类还有字典的功能 5 # class Foo: 6 # # __slots__ = ‘name‘ #可以为字符串也可以为字典 7 # __slots__ = [‘name‘,‘age‘] 8 # # f1=Foo() 9 # f2=Foo() 10 # f3=Foo() 11 # # f1.name=‘alex‘ 12 # # print(f1.name) 13 # # f1.age=18 14 # # print(f1.age) #会报错 限制了实例的属性 节省内存 实例没有自己的属性只能根据类定义的__slots__属性添加自己的属性,把底层字典的功能取消掉 15 # f2.name=‘listen‘ 16 # f2.age=18 17 # f3.age=20 18 # 19 # print(f3.age) 20 # 21 # print(f2.name,f2.age) 22 # print(f2.__slots__)
十、__doc__
1 #__doc__属性不被继承 2 # class A: 3 # "这是文档描述信息" 4 # pass 5 # class B(A): 6 # pass 7 # print(A.__dict__) 8 # print(A.__doc__) #这是文档信息 9 # print(B.__doc__) #None 10 # print(B.__dict__) #{‘__module__‘: ‘__main__‘, ‘__doc__‘: None}
十一、析构方法 __del__
__del__:析构方法,当对象在内存中被释放时,自动触发执行。此方法无需定义,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/3 21:42‘ 4 # class Foo: 5 # def __init__(self,name): 6 # self.name=name 7 # 8 # def __del__(self): 9 # print(‘我执行了!‘) 10 # f=Foo(‘alex‘) 11 # del f 12 # print(‘---------------‘) 13 #result: 14 # 我执行了! 15 # --------------- 16 17 class Foo: 18 def __init__(self,name): 19 self.name=name 20 21 def __del__(self): 22 print(‘我执行了!‘) 23 f=Foo(‘alex‘) 24 del f.name 25 print(‘---------------‘) 26 # result: 27 # --------------- 28 # 我执行了! 删掉实例的属性时不会触发析构函数,删掉对象才会触发 但是当程序执行完毕后 垃圾回收 所以会触发释放内存
十二、__call__
对象后面加括号,触发执行 。
注:构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象加()触发的,即对象() 类名()() 一切皆对象,其实类也是对象
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/3 21:53‘ 4 class Foo: 5 def __call__(self, *args, **kwargs): 6 print(‘我执行啦‘) 7 f=Foo() 8 f() #会触发实例的类的__call__方法 我执行啦 9 Foo() #一切皆对象 Foo加()也会触发它下面的object类的__call方法
十三、__module__和__class__用法
__module__:表示当前操作的对象在那个模块下(import的模块直接用实例)
__class__:表示当前操作的对象的类是什么
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/3 21:26‘ 4 class Foo: 5 def __init__(self,name,age): 6 self.name=name 7 self.age=age
引用上面的模块去创建对象
2 # _*_ encoding:utf-8 _*_ 3 __author__ = ‘listen‘ 4 __date__ = ‘2018/12/3 21:27‘ 5 from module_class.module import Foo 6 f=Foo(‘alex‘,18) 7 print(f.name) 8 print(f.__module__) #module_class.module 9 print(f.__class__) #<class ‘module_class.module.Foo‘>
十四、__iter__和__next__实现迭代器协议
1 # _*_ ecoding:utf-8 _*_ 2 class A: 3 def __init__(self,num): 4 self.num=num 5 def __iter__(self): 6 return self 7 def __next__(self): 8 if self.num == 5: 9 raise StopIteration(‘异常终止‘) 10 self.num+=1 11 return self.num 12 a=A(2) 13 # print(a.__next__()) 14 # print(a.__next__()) 15 # print(a.__next__()) 16 # print(a.__next__()) 17 for i in a: 18 print(i) #for 先调用__iter__方法,变为可迭代对象之后再内部执行__next__方法,但是内部可以到不满足条件终止,循环打印,捕捉异常退出
十五、描述符(__get__、__set__、__delete__)
描述符本质上就是一个新式类,在这个新式类中,至少实现了__get__()、__set__()、__delete__()方法中的一个,这也被称为描述符协议。
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除一个属性时,触发
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/4 20:45‘ 4 #这个类是一个描述符 5 class Foo: 6 def __get__(self, instance, owner): 7 print(‘get method‘) 8 def __set__(self, instance, value): 9 print(‘set method‘,instance,value) 10 # instance.__dict__[‘x‘]=value 11 12 def __delete__(self, instance): 13 print(‘delete method‘) 14 #描述符是用来代理另外一个类的属性(必须把描述符定义到另一个类的类属性中,不能定义到构造函数中),描述符只能用来定义描述其他的类,自己的类不行 15 #只要代理的属性统统归代理管,没有代理的属性统统和代理无关 16 class Bar: 17 x=Foo() #在何地 18 # def __init__(self,x): 19 # self.x=x ===b.x=10 20 b=Bar() 21 b.x=10 #调用类属性并赋值 set method {‘x‘: 10} 22 print(b.__dict__) #{} 空的 x被Foo代理了,所以为空 因为__set__方法中什么都没有做,没有做底层字典的操作 23 # b.x #get method 调用类属性 24 # del b.x #delete method 删除类属性
描述符是干什么用的?
描述符的作用是用来定义另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中),描述符只能用来代理除描述符以外的其他类
Bar这个类被Foo类代理----一般从实例对象中可以看出来
描述符分为两种:1、数据描述符:至少实现__get__和__set__
2、非数据描述符:没有实现__set__
注意事项:一、描述符本身应该定义为新式类,被代理的类也应该是新式类
二、必须把描述符定义成另一个类的类属性,不能定义到构造函数中
三、要严格遵守该优先级,优先级从高到低是:
类属性
数据描述符
实例属性
非数据描述符
找不到的属性触发__getattr__()
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/4 21:31‘ 4 class Foo: 5 def __get__(self, instance, owner): 6 print(‘get method‘) 7 # def __set__(self, instance, value): 8 # print(‘set method‘,instance,value) 9 # # instance.__dict__[‘x‘]=value 10 # 11 # def __delete__(self, instance): 12 # print(‘delete method‘) 13 class Bar: 14 x=Foo() #在何地 15 16 b=Bar() 17 # print(Bar.x) #get method 18 # Bar.x=1 19 # print(Bar.x) #1 20 # print(Bar.__dict__) #里面有x=1属性 说明类属性>数据描述符(有get和set方法) 21 22 # b.x #触发get method 23 # b.x=10 #触发 set method <__main__.Bar object at 0x00000235855FBE48> 10 并不是给实例重点添加一个属性 24 # print(b.__dict__) #{} 数据描述符> 实例 25 26 # b.x #触发 get method 27 # b.x=10 28 # print(b.__dict__) #{‘x‘: 10} 实例>非数据描述符 29 30 b.x #get method 31 print(b.yyyyy) #触发__getattr__ 非数据>找不到
描述符的使用:
1、由于python是弱类型语言,所以不用定义属性的数据类型就可以直接使用,描述符可以实现类型的限制。
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/6 20:38‘ 4 # class Typed: 5 # def __get__(self, instance, owner): 6 # print(‘这是get方法‘) 7 # print(‘这是instance%s‘%instance) 8 # print(‘这是owner%s‘%owner) 9 # def __set__(self, instance, value): 10 # print(‘这是set方法‘) 11 # print(‘这是instance%s‘%instance) 12 # print(‘这是owner%s‘%value) 13 # def __delete__(self, instance): 14 # print(‘这是delete方法‘) 15 # print(‘这是instance%s‘%instance) 16 # 17 # 18 # class People: 19 # name=Typed() 20 # def __init__(self,name,age,salary): 21 # self.name=name 22 # self.age=age 23 # self.salary=salary 24 # p=People(‘alex‘,18,1000.6) # 这是set方法 这是instance<__main__.People object at 0x000001FD8B301550> 这是owneralex 赋值的所有者是附的值 25 # print(p) #<__main__.People object at 0x000001FD8B301550> 26 # p.name #这是get方法 这是instance<__main__.People object at 0x000002C3D23A1550> 这是owner<class ‘__main__.People‘> 调用(对象的所有者是类) 27 # p.name=‘listen‘ #这是set方法 这是instance<__main__.People object at 0x0000024E7C251588> 这是ownerlisten 28 # print(p.__dict__) #{‘age‘: 18, ‘salary‘: 1000.6}
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/6 20:38‘ 4 5 #对传入属性的值类型限制 name只能传入字符串 6 # class Typed: 7 # def __init__(self,key): 8 # self.key=key 9 # def __get__(self, instance, owner): 10 # print(‘这是get方法‘) 11 # return instance.__dict__[self.key] 12 # 13 # def __set__(self, instance, value): 14 # print(‘这是set方法‘) 15 # if not isinstance(value, str): 16 # raise TypeError(‘类型不是字符串‘) 17 # instance.__dict__[self.key]=value 18 # def __delete__(self, instance): 19 # print(‘这是delete方法‘) 20 # instance.__dict__.pop(self.key) 21 # 22 # class People: 23 # name=Typed(‘name‘) 24 # # age = Typed(‘age‘) 25 # def __init__(self,name,age,salary): 26 # self.name=name 27 # self.age=age 28 # self.salary=salary 29 # p=People(‘alex‘,18,1000.6) 30 # print(p.__dict__) #{‘name‘: ‘alex‘, ‘age‘: 18, ‘salary‘: 1000.6} 31 # p=People(22,18,1000.6) 32 # print(p.__dict__) #TypeError: 类型不是字符串 肯定加不到字典中 33 # p.name=‘listen‘ 34 # print(p.__dict__) #{‘name‘: ‘listen‘, ‘age‘: 18, ‘salary‘: 1000.6} 添加进去了
1 #再加一个限制age的类型 2 class Typed: 3 def __init__(self,key,except_key): 4 self.key=key 5 self.except_key=except_key 6 def __get__(self, instance, owner): 7 print(‘这是get方法‘) 8 return instance.__dict__[self.key] 9 10 def __set__(self, instance, value): 11 print(‘这是set方法‘) 12 if not isinstance(value,self.except_key): 13 raise TypeError(‘类型不是%s‘%self.except_key) 14 instance.__dict__[self.key]=value 15 def __delete__(self, instance): 16 print(‘这是delete方法‘) 17 instance.__dict__.pop(self.key) 18 19 class People: 20 name=Typed(‘name‘,str) 21 age = Typed(‘age‘,int) 22 def __init__(self,name,age,salary): 23 self.name=name 24 self.age=age 25 self.salary=salary 26 # p=People(‘alex‘,18,1000.6) #这是set方法 这是set方法 27 # p=People(10,18,1000.6) #TypeError: 类型不是<class ‘str‘> 28 p=People(‘alex‘,‘j‘,1000.6) #TypeError: 类型不是<class ‘int‘>
2、实现装饰器功能
基本的装饰器:
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/6 22:18‘ 4 # def deco(obj): 5 # obj.x=1 6 # obj.y=2 7 # obj.z=3 8 # return obj 9 # @deco 10 # class Foo: 11 # pass 12 # print(Foo.__dict__) #属性字典中有 x y z 13 #传的参数是活的 14 def Type(**kwargs): #在外面嵌套一个函数 实现加参数的功能 15 def deco(obj): 16 for key,val in kwargs.items(): 17 setattr(obj,key,val) 18 return obj 19 return deco 20 @Type(age=17,name=‘alex‘) 21 class Foo: 22 pass 23 print(Foo.age,Foo.name) #17 alex
装饰器的补充(和描述符没关系,只是单纯的装饰器的补充):
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/8 10:13‘ 4 # class Dog: 5 # def __init__(self,name): 6 # self.name=name 7 # @property 8 # def like_wang(self): 9 # print( ‘get汪汪汪‘) 10 # @like_wang.setter 11 # def like_wang(self,value): 12 # print(‘set汪汪汪‘,value) 13 # @like_wang.deleter 14 # def like_wang(self): 15 # print(‘del汪汪汪‘) 16 # 17 # d=Dog(‘豆豆‘) 18 # # print(d.like_wang) 19 # # d.like_wang=‘wolf‘ #AttributeError: can‘t set attribute 20 # del d.like_wang 21 22 #商品的价格变动 23 # class Goods: 24 # def __init__(self,orign_price,zhekou): 25 # self.orign_price=orign_price 26 # self.zhekou=zhekou 27 # @property 28 # def price(self): 29 # return self.orign_price*self.zhekou 30 # @price.setter 31 # def price(self,val): 32 # self.orign_price=val 33 # 34 # @price.deleter 35 # def price(self): 36 # del self.orign_price 37 # 38 # 39 # g=Goods(100,0.5) 40 # # print(g.price) #50 41 # # g.price=200 42 # # print(g.price) #100 43 # del g.price 44 # print(g.price) 45 46 47 #加上类型限制 48 class Goods: 49 def __init__(self,orign_price,zhekou): 50 self.orign_price=orign_price 51 self.zhekou=zhekou 52 @property 53 def price(self): 54 return self.orign_price*self.zhekou 55 @price.setter 56 def price(self,val): 57 if val is not int: 58 raise TypeError(‘此类型应该为int‘) 59 self.orign_price=val 60 61 @price.deleter 62 def price(self): 63 del self.orign_price 64 # a=property(get_price,set_price,del_price) 第2种使用方法 65 66 g=Goods(100,0.5) 67 g.price=‘lll‘ 68 print(g.price) # 此类型应该为int 69 # print(g.price) #50 70 # g.price=200 71 # print(g.price) #100 72 # print(g.a) #50
十六、__enter__和__exit__
文件的操作方式:
1 #此方法不需要自己关掉文件 2 with open(‘文件名‘,‘打开方式‘,‘编码方式‘) as f: 3 ‘代码块‘ #文件操作
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/5 21:58‘ 4 class Open: 5 def __init__(self,name): 6 self.name=name 7 def __enter__(self): 8 print(‘---->enter‘) 9 return self 10 def __exit__(self, exc_type, exc_val, exc_tb): 11 print(‘---------->exit‘) 12 with Open(‘a‘) as f : #__enter__返回的值会给f 这一步是执行enter f=self 对象的实例化 13 print(f) #打印对象的地址 14 print(f.name) #a 15 print(‘----->‘) 16 print(‘----->‘) 17 print(‘----->‘) #中间都在执行文件的操作 18 print(‘----->‘) 19 print(‘----->‘) #关闭文件 执行完内部后会触发__exit__ 20 print(‘==========>>‘)
在with的代码块中,打印未定义的变量,触发exit方法,待exit函数执行结束,代表with代码块也执行结束,报错信息打印,整个程序结束;但是在exit方法中加上return True,就会继续执行with代码块外的语句,让外部看不出里面有什么报错,隐藏报错信息。
1 class Open: 2 def __init__(self,name): 3 self.name=name 4 def __enter__(self): 5 print(‘---->enter‘) 6 return self 7 def __exit__(self, exc_type, exc_val, exc_tb): #exc_type 异常的类 exc_val 异常的值 exc_tb 异常的追踪信息 8 print(‘---------->exit‘) 9 print(exc_type) #<class ‘NameError‘> 10 print(exc_val) # NameError: name ‘adghh‘ is not defined name ‘adghh‘ is not defined 11 print(exc_tb) #<traceback object at 0x000001E7A7FB9048> 12 return True 13 with Open(‘a‘) as f : #__enter__返回的值会给f 这一步是执行enter f=self 14 print(f) #打印对象的地址 15 print(f.name) #a 16 print(adghh) #打印未定义的变量 触发__exit__程序会退出 打印错误信息 17 print(‘----->‘) #关闭文件 执行完内部后会触发__exit__ 18 19 print(‘==========>>‘) #这将会被执行 把错误吞掉 除了内部知道有问题,外部看不出来,不影响外部程序的运行
小结:
1、with obj---->代表触发obj.__enter__(),拿到返回值
2、as f----->f=返回值
3、with obj as f ===f=obj.__enter__()
4、执行代码块
①没有异常的情况下,整个代码块运行完毕后去触发__exit__.
②有异常的情况下,从异常出现的位置触发__exit__
a:如果__exit__返回值是True 代表吞掉了异常
b:如果__exit__返回值不为True,代表吐出了异常
__exit__的运行完毕就代表整个with语句的执行完毕
用途或者好处:
1、使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动干预。
2、在需要管理一些资源比如文件、网络连接和锁的编程环境中,可以在__exit__中制定自动释放资源的机制,你无须再关注这个问题,有很大好处。
十七、元类
python中一切皆对象,类本身也是对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象是类而非类的实例)
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例是对象(f对象是Foo类的实例,Foo类是type类的一个实例)
创建类的方式有两种:
# _*_ encoding:utf-8 _*_ __author__ = ‘listen‘ __date__ = ‘2018/12/8 11:19‘ #python中一切皆对象 类也是对象 #对象的类是类 类类是元类 type
#第一种 class Foo: def __init__(self,name,age): self.name=name self.age=age f=Foo(‘alex‘,18) # print(Foo) #<class ‘__main__.Foo‘> 这个类的类名 print(f.__class__) #<class ‘__main__.Foo‘> print(Foo.__class__) #<class ‘type‘> # #实例化 # def __init__(self,name,age): # self.name=name # self.age=age # def test(self): # print(‘=====‘) # 第二种 FFoo=type(‘FFoo‘,(object,),{‘x‘:1,‘__init__‘:__init__,‘test‘:test}) #字典中放数据属性 # f1=FFoo(‘listen‘,23) # print(f1.name,f1.age) #listen 23 实现元类的实例化调用 # f1.test() #=====
自定义元类,实现类的自定义
1 # _*_ encoding:utf-8 _*_ 2 __author__ = ‘listen‘ 3 __date__ = ‘2018/12/8 12:31‘ 4 #实现自定义类的模板 5 class MyType(type): 6 def __init__(self,a,b,c): 7 print(‘元类的构造函数‘) 8 def __call__(self, *args, **kwargs): 9 obj=object.__new__(self) #得到f对象 10 self.__init__(obj,*args,**kwargs) #实现实例字典属性的添加 11 return obj #如果这个没有返回值 则返回none 没有dict方法 12 13 14 class Foo(metaclass=MyType): #Foo=type(‘Foo‘,(object,),{}) 15 def __init__(self,name): 16 self.name=name 17 f=Foo(‘alex‘) #会运行call函数 类也是对象加() 触发call 18 print(f.__dict__)
以上是关于Python中的面向对象(进阶)的主要内容,如果未能解决你的问题,请参考以下文章