python基础12-多态/封装/反射/继承包装/getattr授权
-
多态
- 定义:由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
- 面向对象的类叫工厂,来生产实例的。所以工厂函数str就是生成字符串对象的函数
- len(str1)本质上就是str.__len__()。即不同类,可以调用同一个方法len
- 多态是动态时的绑定状态,调用不同对象的方法时。多态反应在执行时候
- 面向对象,就是不要强迫自己去做事情,定义类
- 多态是继承的一种体现。继承的目的是实现代码的重用,多态让这种重用目标的以体现
-
封装
- 是特性,是种思想,不是具体的技术
- 封装提供接口,封是隐藏的意思
-
_author_='xiansuo ' class People: _star='earth'#单下划线开头,就是隐藏,不应该在外部使用该属性 __star='earth'#python给重命名了,_People__star def get_star(self):#访问函数,或者接口函数,为外部提供访问隐藏属性的接口 print(self._star)
- 封装三层意思
- 类就是麻袋,这本身就是一种封装
- 类中定义私有的,只在类的内部使用,外部无法访问。但这也只是约定,python并没有实际限制访问
- 明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用
- 面向对象的优点
- 通过封装明确了内外,上帝造物的逻辑你无需知道,上帝想让你知道的你才能知道,明确了划分等级
- 继承+多态在语言层面支持了归一化设计,即一切皆对象,按照统一的格式去调用即可,归一化
- 泛化:所有子类和父类有一样的特点。特化:描述子类与父类的不同
-
反射/自省
-
class BlackMedium: feture='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('[%s]正在卖房子,傻逼才买呢'%self.name) def rent_hourse(self): print('[%s]正在租房子,傻逼才租呢'%self.name) b1=BlackMedium('万成置地','天露园')#生成实例 print(hasattr(b1,'name'))#检测实例b1能否调用name的数据属性 print(getattr(b1,'name'))#获取实例b1中的name属性的value func=getattr(b1,'rent_hourse')#获取实例b1中的函数的地址,等价于b1.rent_hourse func()#执行上述函数 print(getattr(b1,'rent_hoursesdfsdaf','没有这个属性'))#匹配不到,则返回后边的值 setattr(b1,'sb',True)#给实例b1的字典加个值,key和value print(b1.__dict__) setattr(b1,'name','SB') setattr(b1,'func',lambda x:x+1)#可以实例加函数属性 print(b1.__dict__) print(b1.func(10)) delattr(b1,'sb') print(b1.__dict__)
- 反射就是自己找自己,应用场景:适用于团队合作,彼此分工,一人休假,不影响其他人的代码工作。可插拔程序。
-
动态导入模块
- 动态导入的底层就是基于反射做的
- 一切皆对象,模块也是对象
-
m=__import__('dic')#以字符串的形式导入模块。无论dic.套多少层,m只拿到dic print(m) m.t.test()#执行m下边t模块的test函数 #m1.t中_test2()函数,在此种*的情况下,不能被导入。 from m1.t import * from m1.t import test1,_test2#如此方可导入 test1() test2() #import importlib m=importlib.import_module('m1.t') print(m) m.test1()#也是以字符串的形式导入上述内容
-
类的内置attr属性
- 类本质上也是一个对象,上述的反射方法同样适用于类
- 双下划线开头的attr方法
-
class Foo: x=1 def __init__(self,y): self.y=y #这三个方法是给实例用的,类不能调用 #以下三个方法系统都有内置,如果用户不定义,则在适当条件下触发系统内置的 def __getattr__(self, item): print('执行_getattr_') #__getattr__是python给类的内置方法,如果用户不定义,则执行系统默认的,就是报错。 #如果用户定义,则不报错,执行自定义的函数 #以下两种操作很多余,系统默认__delattr和__setattr_就是在执行这种操作 def __delattr__(self, item): print('删除操作_delattr_') #del self.item#无限递归了 self.__dict__pop(item):#操作底层字典进行删除 def __setattr__(self, key, value):#可以加上if判断,实现符合某种条件再设置 print('_setatter_执行') # self.key=value#这种操作属于设置操作,就会触发_setattr_操作,陷入递归 self.__dict__[key]=value#操作底层字典进行设置 f1=Foo(10) print(f1.y) print(getattr(f1,'y')) f1.ssssss#调用f1没有的属性,则执行getattr函数 del f1.y#删除操作时,会触发__delattr_操作
-
二次加工标准类型的两种方式
-
继承+派生
- str,list,tuple都是类
- class List(list),List继承list类
-
class List(list): def append(self, p_object): if type(p_object) is str: # self.append(p_object) #如此调用,又陷入递归陷阱 # list.append(self,p_object) #调用父类append本身的,建议用super的方法 super().append(p_object) else: print('只能添加字符串类型') def show_midlle(self): # 继承列表的类,并进一步加工,取得字符串得中值 mid_index = int(len(self) / 2) return self[mid_index] l1 = List('helloworld') # print(l1.show_midlle()) l1.append('SB') print(l1) l1.append(1212)
-
组合的方式完成授权,效果等价于继承加工
- 授权:授权是包装的一个特性,包装一个类型通常是对已经存在的类型的一些定制,这种做法可以新建,修改或者删除原有产品的功能。其他的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。实现授权的关键点就是覆盖__getattr__方法
- 授权的步骤
- 基于标准类型来定制自己的类型
- 跟继承没关系,通过getattr实现
-
class FileHandle:#在系统内置的文件上,新增方法,其余的授权给系统内置的文件对象即可,用getattr实现了继承的操作 def __init__(self,filename,mode='r',encoding='utf-8'): # self.filename=filename self.file=open(filename,mode,encoding=encoding)#系统提供的 self.mode=mode self.encoding=encoding def __getattr__(self, item): # self.file.read return getattr(self.file,item)#返回系统的item方法 f1=FileHandle('a.txt','w+') print(f1.__dict__) print('==>',f1.read)#触发__getattr__。看似执行自己的read方法,实则是执行open打开文件的read方法