一、继承
''' 1、什么是继承 继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类、父类、超类 继承描述的是一种“遗传”的关系:子类可以重用父类的属性 在python中的继承注意两点: 1. 在python中支持一个子类同时继承多个父类, 2. python中类分为两种: 新式类:但凡继承object的类,以及该类的子类。。。都是新式类 在python3中一个类如果没有继承人类类,默认继承object类,即python3中所有的类都是新式类 经典类: 没有继承object的类,以及该类的子类。。。都是经典类 在python2中才区分新式类与经典类 2、为何要用继承 减少代码冗余
3、如何用继承 class Parent1(object): pass class Parent2: pass class Subclass1(Parent1,Parent2): pass print(Subclass1.__bases__) # 2、在继承的背景下,属性查找的优先级 #当类是经典类时,多继承的情况下,在要查找的属性不存在时,会按照深度优先的方式查找下去 #当类是新式类时,多继承的情况下,在要查找的属性不存在时,会按照广度优先的方式查找下去
多继承背景下属性查找的顺序:对象-》对象的类-》按照从左往右的顺序一个一个的分支找下去
# 一旦出现菱形继承问题,新式类与经典类在属性查找上的区别是
# 新式类:广度优先查找,在最后一个分支查找顶级类
# 经典类:深度优先查找,在第一个分支就查找顶级类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class OldboyPeople: school='Oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class OldboyStudent(OldboyPeople): def __init__(self,name,age,sex,num=0): # OldboyPeople.__init__(self,name,age,sex) #OldboyPeople.__init__(stu1,李特丹',18,'female') super(OldboyStudent,self).__init__(name,age,sex) self.score=num def choose_course(self): print('%s is choosing course' %self.name) class OldboyTeacher(OldboyPeople): def __init__(self,name,age,sex,level): super().__init__(name,age,sex) self.level=level def score(self,stu,num): stu.score=num #例子 class A: def test(self): print('A.test()') super().test() class B: def test(self): print('from B') class C(A,B): pass obj=C() print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] obj.test() ''' A.test() from B '''
类的继承有两层意义:
1.改变 2.扩展
二、多态
多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样
python中的标准类型就是多态概念的一个很好的示范
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def speak(self): pass @abc.abstractmethod def run(self): pass # 抽象基类:是用来指定规范,但凡继承该类的子都必须实现speak和run,而名字必须叫speak和run # 注意:不能实例化抽象基类 Animal() class People(Animal): def speak(self): print('say hello') def run(self): pass class Dog(Animal): def speak(self): print('汪汪汪') def run(self): pass class Pig(Animal): def speak(self): print('哼哼哼哼哼') def run(self): pass obj1=People() obj2=Dog() obj3=Pig() # obj1,obj2,obj3都是动物 obj1.speak() obj2.speak() obj3.speak() def speak(animal): animal.speak() speak(obj1) speak(obj2) speak(obj3) obj1=[1,2,3] obj2='hello' obj3={'x':1} print(obj1.__len__()) print(obj2.__len__()) print(obj3.__len__()) print(len(obj1)) print(len(obj2)) print(len(obj3))
三、封装
第一个层面的封装:类就是麻袋,这本身就是一种封装
第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问
第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)
class People: __country='China' #_People__country='China' __n=111 #_People__n=111 def __init__(self,name): self.__name=name #self._People__name=name def run(self): print('%s is running' %self.__name) #self._People__name print(People.__country) obj=People('egon') print(obj.__name) print(obj.run) obj.run() print(People.__dict__) print(People._People__country) print(obj.__dict__) print(obj._People__name)
四、类方法与装饰器
类中定义的函数有两大类(3小种)用途,一类是绑定方法,另外一类是非绑定方法
# 1. 绑定方法:
# 特点:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
# 1.1 绑定给对象的:类中定义的函数默认就是绑定对象的
# 1.2 绑定给类的:在类中定义的函数上加一个装饰器classmethod
# 2. 非绑定方法
# 特点: 既不与类绑定也不与对象绑定,意味着对象或者类都可以调用,但无论谁来调用都是一个普通函数,根本没有自动传值一说
class Foo: def func1(self): print('绑定给对象的方法',self) @classmethod def func2(cls): print('绑定给类的方法: ',cls) @staticmethod def func3(): print('普通函数') obj=Foo() obj.func1() print(obj) Foo.func2() # 绑定方法 print(obj.func1) print(Foo.func2) # 非绑定方法 print(obj.func3) print(Foo.func3)
一 什么是装饰器?
器即函数,装饰即修饰,意指为其他函数添加新功能
装饰器定义:本质就是函数,功能是为其他函数添加新功能
property装饰器就是将一个函数属性伪装成一个数据属性
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_user={'username':None,'login':False} def auth(auth_type='file'): def auth_deco(func): def wrapper(*args,**kwargs): if auth_type == 'file': if current_user['username'] and current_user['login']: res=func(*args,**kwargs) return res username=input('用户名: ').strip() passwd=input('密码: ').strip() for index,user_dic in enumerate(user_list): if username == user_dic['name'] and passwd == user_dic['passwd']: current_user['username']=username current_user['login']=True res=func(*args,**kwargs) return res else: print('用户名或者密码错误,重新登录') elif auth_type == 'ldap': print('巴拉巴拉小魔仙') res=func(*args,**kwargs) return res return wrapper return auth_deco #auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file') #就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数 @auth(auth_type='ldap') def index(): print('欢迎来到主页面') @auth(auth_type='ldap') def home(): print('这里是你家') def shopping_car(): print('查看购物车啊亲') def order(): print('查看订单啊亲') # print(user_list) index() # print(user_list) home() # 带参装饰器
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
def deco(func): print('---') func.x=1 func.y=2 func.z=3 return func #一切皆对象 @deco #test=deco(test) def test(): print('test_func_run') test.x=1 print(test.__dict__) @deco #Foo=deco(Foo) class Foo: pass f1=Foo() print(f1.__dict__) print(Foo.__dict__) print(f1.x)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
def Typed(**kwargs): def deco(obj): # print('+++++',kwargs) # print('obj_name',obj) for key,val in kwargs.items(): # obj.__dict__[key]=[val] # obj.key=val setattr(obj,key,val) return obj # print('---',kwargs) return deco @Typed(x=1,y=2,z=3) #Typed(x=1,y=2,x=3)---> @deco ---->Foo=deco(Foo) class Foo: pass print(Foo.__dict__) # @deco @Typed(name='egon') class Bar: pass print(Bar.name)
装饰器=高阶函数+函数嵌套+闭包
@timer #@timer就等同于cal=timer(cal) def cal(array): res=0 for i in array: res+=i return res cal(range(10))
''' 闭包:在一个作用域里放入定义变量,相当于打了一个包 ''' def father(name): def son(): # name='alex' print('我爸爸是 [%s]' %name) def grandson(): # name='wupeiqi' print('我爷爷是 [%s]' %name) grandson() son() father('XXX')
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Room: tag="mmp" def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property def cal_area(self): # print('%s 住的 %s 总面积是%s' %(self.owner,self.name,self.width*self.length)) return self.width*self.length @property def cal_tiji(self): return self.length*self.width*self.heigh def test(cls): print('from test',self.name) @classmethod # 专门供类使用的方法 def tell_info(cls): print(cls) print('----->',cls.tag) r1=Room('厕所','av',100,100,10000) r2=Room('公共厕所','abc',10,10,10) Room.tell_info() #有特殊意义,类在调用自己函数属性是跟实例绑定在一起 print(Room.tag) # print('%s 住的 %s 总面积是%s' %(r1.owner,r1.name,r1.width*r1.length)) print(r1.cal_area) print(r2.cal_area) print(r1.cal_tiji) print(r1.name) print(r2.name)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Room: tag="mmp" def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property #属性方法 def cal_area(self): # print('%s 住的 %s 总面积是%s' %(self.owner,self.name,self.width*self.length)) return self.width*self.length @property def cal_tiji(self): return self.length*self.width*self.heigh @classmethod # 类方法,专门供类使用的方法 def tell_info(cls,x): print(cls) print('----->',cls.tag,x) # def test(x,y): # print(x,y) @staticmethod #静态方法,类的从工具包 def wash_body(a,b,c): print('%s %s %s 洗澡',(a,b,c)) Room.wash_body('alex','yuanhao','wupenqi') r1=Room('厕所','alex',100,100,10000)
五、属性方法
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class List(list): def append(self, object): if type(object) is str: # self.append(object) # list.append(self,object) super().append(object) else: print('只能添加字符串') def show_midlle(self): mid_index=int(len(self)/2) return self[mid_index] l1=List('helloworld') l1.append('SB') print(l1)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Chinese: country='China' def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 打 %s' %(self.name,ball)) p1=Chinese('alex') print(p1.__dict__) #查看 print(p1.name) p1.play_ball('篮球') #增加 p1.age=18 print(p1.__dict__) print(p1.age) def test(self): print('实例函数属性') p1.test=test print(p1.__dict__) p1.test(p1) # #不要修改底层的属性字典 # p1.__dict__['sex']='male' # print(p1.__dict__) # print(p1.sex) #修改 p1.age=19 print(p1.__dict__) print(p1.age) #删除 del p1.age print(p1.__dict__)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Chinese: contry='China' dang='gongchandang' def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s'%(self,name)) #查看 print(Chinese.contry) #修改 Chinese.contry='japan' print(Chinese.contry) p1=Chinese('alex') print(p1.__dict__) print(p1.contry) #删除 del Chinese.dang del Chinese.contry print(Chinese.__dict__) #增加 def eat_food(self,food): Chinese.eat=eat_food() print(Chinese.__dict__) p1.eat('aaa')
内置方法:
__str__:在对象被打印时自动触发,然后将该绑定方法的返回值(必须是字符串类型)当做本次打印的结果 class People: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '<name:%s age:%s>' %(self.name,self.age) obj1=People('egon',18) obj2=People('lxx',38) print(obj1) #print(obj1.__str__()) print(obj2) #print(obj2.__str__()) __del__:在对象被删除前自动触发, 在该方法内应该执行与该对象有关的系统资源的回收操作 class Foo: def __init__(self,filename,encoding='utf-8'): self.f=open(filename,'r',encoding=encoding) def __del__(self): # print('run.....') self.f.close() obj=Foo() del obj #obj.__del__() #obj.__del__()
六、高阶函数、迭代器、生成器
高阶函数定义
1、函数接收的参数是一个函数名
2、函数的返回值是一个函数名
3、满足上述条件任意一个,都可称为高阶函数
import time def Foo(): print('------') def test(func): print(func) start_time=time.time() func() stop_time=time.time() print('func_runtime %s' %(stop_time-start_time)) Foo() test(Foo) #修改函数调用方式
def run(): print('from_run') run() def test(): yield 1 #暂停,入口 print('from_run') yield 2 print('from_run') t=test() 产生生成器对象 #唤醒迭代器 t.__next__() next(t) # yield10 t.send('123')
age=10 res=True if age>10 else False # if 形式三元表达式 l=['a' for i in range(10)] #列表解析 g_l=('a' for i in range(10)) #生成器表达式 print(l) def test(): for i in range(4): yield 1 t=test()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Foo: def __init__(self,n): self.n=n def __iter__(self): return self def __next__(self): if self.n>=100: raise StopIteration self.n+=1 return self.n # l = list('hello') # for i in l: # print(i) f1 = Foo(10) for i in f1: #f1.__iter__()----------->iter(f1) print(i)
七、反射
反射是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
issubclass(sub, super)检查sub类是否是 super 类的派生类
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
hasattr(object,name) 判断object中有没有一个name字符串对应的方法或属性 getattr(object,name,default=None) 从对象中获取命名属性;getattr(x, 'y')等价于x.y。 当给出一个默认参数时,当属性没有给出时,返回该参数 不存在,在这种情况下就会引发异常。 setattr(x,y,v) 将给定对象上的命名属性设置为指定值。 setattr(x, 'y', v)等于' x.y = v delattr(x,y) 从给定对象中删除命名属性。 delattr(x, 'y')等价于' del x.y'
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Foo: pass class Bar(Foo): pass obj=Bar() print(isinstance(obj,Bar)) print(isinstance([],list)) print(issubclass(Bar,Foo)) # 反射:指的是通过字符串来操作属性 class Foo: def __init__(self,name): self.name=name obj=Foo('eg') # hasattr() # print(hasattr(obj,'name')) #'name' in obj.__dict__ # getattr() # print(getattr(obj,'name')) #obj.__dict__['name'] # print(getattr(obj,'age')) #obj.__dict__['age'] # print(getattr(obj,'age',None)) #obj.__dict__['age'] # setattr() # setattr(obj,'age',18) #obj.age=18 # setattr(obj,'name','EG') #obj.name='EG' # print(obj.__dict__) # delattr() # delattr(obj,'name')# del obj.name # print(obj.__dict__)
导入其他模块,利用反射查找该模块是否存在某个方法
反射的好处
好处一:
实现可插拔机制,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
好处二:
动态导入模块(基于反射当前模块成员)
module_abc=__import__('dic.a.b.c')#从字符串中提取文件名 # module_abc 拿到最顶层dic模块 module_abc.a.b.c
八、__setattr__,__delattr__,__getattr__
三者的用法演示:
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用调用属性且属性不存在的时候才会触发 f1.xxxxxx
授权:
授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) self.mode=mode self.encoding=encoding def write(self,line): print('---------',line) t=time.strftime('%Y-%m-%d %X') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): # print(item,type(item)) return getattr(self.file,item) # def read(self): # pass f1=FileHandle('a.txt','r+') print(f1.__dict__) print('——————>',f1.read) # 触发 getattr # print(f1.write) f1.write('11111111211111\n') # f1.seek(0) # sys_f=open('b.txt','w+') #授权的方式主要在于__getattr__将没有定义的方法映射到真正的内建函数file里面
class List: def __init__(self,seq,permission=False): self.seq=seq self.permission=permission def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) # l.clear() #此时没有权限,抛出异常 l.permission=True print(l) l.clear() print(l) #基于授权,获得insert方法 l.insert(0,-123) print(l)
九、__getattribute__
回顾__getattr__
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('执行的是我') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的属性访问,触发__getattr__
class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('不管是否存在,我都会执行') f1=Foo(10) f1.x f1.xxxxxx __getattribute__
当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
二者同时出现
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
十、描述符(__get__,__set__,__delete__)
1、 描述符是什么:
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符 def __get__(self, instance, owner): pass def __set__(self, instance, value): pass def __delete__(self, instance): pass
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
class Foo: def __get__(self, instance, owner): print('触发get') def __set__(self, instance, value): print('触发set') def __delete__(self, instance): print('触发delete') #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法 f1=Foo() f1.name='egon' f1.name del f1.name #疑问:何时,何地,会触发这三个方法的执行
class Foo: def __get__(self, instance, owner): print('---get方法') def __set__(self, instance, value): print('---set方法') instance.__dict__['x']=value def __delete__(self, instance): print('---delete方法') class Bar: x=Foo()#定义成另外一个类的类属性 def __init__(self,n): self.x=n b1=Bar(10) print(b1.__dict__)
一 数据描述符:至少实现了__get__()和__set__()
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get')
二 非数据描述符:没有实现__set__()
class Foo: def __get__(self, instance, owner): print('get')
注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class People: name=Str() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错 People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__() People.name='egon' #那赋值呢,我去,并没有触发__set__() del People.name #赶紧试试del,我去,也没有触发__delete__() #结论:描述符对类没有作用-------->傻逼到家的结论 ''' 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__() del People.name #同上 ''' 类属性>数据描述符
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class People: name=Str() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age p1=People('egon',18) #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性 p1.name='egonnnnnn' p1.name print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了 del p1.name 数据描述符>实例属性
描述符应用
python是弱类型语言,即参数的赋值没有类型限制,可以通过描述符机制来实现类型限制功能
class Type: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self, instance, owner): print('get_methods') # print('getinstance参数[%s]'%instance) # print('owner参数[%s]'%owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set_methods') # print('setinstance参数[%s]' % instance) # print('value参数[%s]' % value) if not isinstance(value,self.expected_type) : # print('你传入的类型不是字符串,错误') # return raise TypeError('你传入的不是字符串') instance.__dict__[self.key]=value def __delete__(self, instance): print('delete_methods') # print('instance_arg[%s]'%instance) instance.__dict__.pop(self.key) class People: name=Type('name',str) age=Type(18,int) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('egon',18,33.3) p1.name='alex' print(p1.__dict__) p1.age
总结:
描述符是可以实现大部分python类特性中的底层功能,包括@classmethod,@staticmethd,@property甚至是__slots__属性
描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class ClassMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, def feedback(): print('在这里可以加功能啊...') return self.func(owner) return feedback class People: name='linhaifeng' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls): print('你好啊,帅哥 %s' %cls.name) People.say_hi() p1=People() p1.say_hi() #疑问,类方法如果有参数呢,好说,好说 class ClassMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, def feedback(*args,**kwargs): print('在这里可以加功能啊...') return self.func(owner,*args,**kwargs) return feedback class People: name='linhaifeng' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls,msg): print('你好啊,帅哥 %s %s' %(cls.name,msg)) People.say_hi('你是那偷心的贼') p1=People() p1.say_hi('你是那偷心的贼') 自己做一个@classmethod
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class StaticMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, def feedback(*args,**kwargs): print('在这里可以加功能啊...') return self.func(*args,**kwargs) return feedback class People: @StaticMethod# say_hi=StaticMethod(say_hi) def say_hi(x,y,z): print('------>',x,y,z) People.say_hi(1,2,3) p1=People() p1.say_hi(4,5,6)
十一、item系列方法
class Foo: def __getitem__(self, item): print('getitem') return self.__dict__[item] def __setitem__(self, key, value): print('setitem') self.__dict__[key]=value def __delitem__(self, key): print('delitem') self.__dict__.pop(key) #字典操作触发 item #. 触发 attr f1= Foo() print(f1.__dict__) f1['name']='egon' f1['22']='2222' print(f1.__dict__)
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): 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['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alx' print(f1.__dict__)
十二、property
一个静态属性property本质就是实现了get,set,delete三种方法
#实现类型检测功能 #第一关: class People: def __init__(self,name): self.name=name @property def name(self): return self.name # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常 #第二关:修订版 class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里 print(p1.name) print(p1.name) print(p1.name) print(p1.__dict__) p1.name='egon' print(p1.__dict__) del p1.name print(p1.__dict__) #第三关:加上类型检查 class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') if not isinstance(value,str): raise TypeError('必须是字符串类型') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里 p1.name=1
十三、__str__,__repr__,__format__,__slots__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __repr__(self): return 'School(%s,%s)' %(self.name,self.addr) def __str__(self): return '(%s,%s)' %(self.name,self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: format_spec='nat' fmt=format_dict[format_spec] return fmt.format(obj=self) s1=School('oldboy1','北京','私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) ''' str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))
1.__slots__是什么:
class Foo: __slots__='x' f1=Foo() f1.x=1 f1.y=2#报错 print(f1.__slots__) #f1不再有__dict__ class Bar: __slots__=['x','y'] n=Bar() n.x,n.y=1,2 n.z=3#报错
class Foo: __slots__=['name','age'] f1=Foo() f1.name='alex' f1.age=18 print(f1.__slots__) f2=Foo() f2.name='egon' f2.age=19 print(f2.__slots__) print(Foo.__dict__) #f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存
十四、__next__、__iter__、__doc__
斐波那契数列
class Fib: def __init__(self): self._a=0 self._b=1 def __iter__(self): return self def __next__(self): self._a,self._b=self._b,self._a + self._b return self._a f1=Fib() print(f1.__next__()) print(next(f1)) print(next(f1)) for i in f1: if i > 100: break print('%s ' %i,end='')
class Foo: '我是描述信息' pass class Bar(Foo): pass print(Bar.__doc__) #该属性无法继承给子类
十五、__module__、__class__、__del__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
典型的应用场景:
创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中
当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源
f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件 del f #只回收用户空间的f,操作系统的文件还处于打开状态 #所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是 f=open('a.txt') 读写... f.close() 很多情况下大家都容易忽略f.close,这就用到了with上下文管理
十六、__enter__、__exit__、__call__
在操作文件对象的时候
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中代码块执行完毕时执行我啊') with Open('a.txt') as f: print('=====>执行代码块') # print(f,f.name)
__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open: def __init__(self,filepath,mode='r',encoding='utf-8'): self.filepath=filepath self.mode=mode self.encoding=encoding def __enter__(self): # print('enter') self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) return self.f def __exit__(self, exc_type, exc_val, exc_tb): # print('exit') self.f.close() return True def __getattr__(self, item): return getattr(self.f,item) with Open('a.txt','w') as f: print(f) f.write('aaaaaa')#授权 f.wasdf #抛出异常,交给__exit__处理
用途:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
对象后面加括号,触发执行 __call__
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
十七、元类 metaclass
python中一切皆为对象。
所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)
类的产生过程其实就是元类的调用过程
class关键字在帮我们创建类时,调用了元类type(...),那调用type时传入类的关键组成部分,一个类有三大组成部分,分别是
1、类名class_name='xxxx'
2、基类们class_bases=(object,)
3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的
调用type时会依次传入以上三个参数
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类,用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看)