Python基础之面向对象进阶一
一、isinstance(obj,cls)和issubclass(sub,super)
1、isinstance(obj,cls)检查obj是否是类 cls 的对象
class A: pass obj = A() #实例化对象obj isinstance(对象名,类名)#语法 print(isinstance(obj,A)) #isinstance函数返回的是布尔值,True,则obj,是A产生的对象 print(isinstance(object,A)) #同上,反之,为不是。 ---------------输出结果------------------- True False
2、issubclass(sub, super)检查sub类是否是 super 类的派生类或子类
class A: #定义父类 pass class B(A): #定义子类,继承A pass issubclass(子类名,父类名) #语法 print(issubclass(B,A)) #返回的也是布尔值,True为真,则B,是A的子类,反之,不是 -------------输出结果------------------ True
二、反射
1、什么是反射:
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自
省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp
和面向对象方面取得了成绩。
2、反射的简单含义:
1、通过类名获得类的实例对象
2、通过方法名得到方法,实现调用
3、python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用
反射)。
反射即想到4个内置函数分别为:hasattr、getattr、setattr、delattr 获取成员、检查成员、设置成员、删除成员下面
逐一介绍先看例子:
class motorcycle: #仅限二轮摩托 feature = "两个轱辘" #相对而言 def __init__(self,brand,price): self.brand = brand self.price = price def advance(self): #都有前进的技能 print("%s,出发了!"%self.brand) def stop(self): #都有减速,停车功能 print("%s,减速了!"%self.brand) m1 = motorcycle("春风",28800) #实例化对象m1
3.1、hasattr(obj,"name"):检测obj里是否含有"name"属性
#检测是否含有某属性 print(hasattr(m1,"feature")) #检测对象m1是否有类的数据属性 print(hasattr(m1,"brand")) #检测m1里是否有"brand"这个数据属性 print(hasattr(m1,"advance")) #检测m1里是否有"advance"这个绑定方法 print(hasattr(motorcycle,"feature")) #一切皆对象,加测类里面是否有"feature"这个数据属性 print(hasattr(motorcycle,"stop")) #一切皆对象,检测类里面是否有"stop"这个函数属性 print(hasattr(m1,"abcd")) #检测到没有这个"abcd"的时候,此时,会返回False ----------------------输出结果------------------- True True True True True False
3.2、getattr(obj,"name"):获取obj里的"name"的属性
#获取某属性 n = getattr(m1,"brand") #获取m1的品牌属性,并赋值给n print(n) #打印n n1 = getattr(motorcycle,"feature") #一切皆对象,获取类的数据属性,并赋值给n1 print(n1) #打印n1 n2 = getattr(m1,"feature") #获取对象m1的类的特征,并赋值给n2 print(n2) #打印n2 n3 = getattr(m1,"advance") #获取m1的绑定方法,并赋值给n3 n3() #现在n3就是m1的绑定方法,加括号就能调用 n4 = getattr(motorcycle,"stop") #获取类的函数属性,并赋值给n4 n4(m1) #因为n4是类的函数属性,so,调用的时候要传参 #getattr(m1,"abcd") #没有abcd"会报错 print(getattr(m1,"abcd","不存在啊!")) #没有"abcd"会打印后面的字符串 -----------------输出结果-------------------- 春风 两个轱辘 两个轱辘 春风,出发了! 春风,减速了! 不存在啊!
3.3、setattr(obj,"name",content):设置obj里的"name"的属性
#设置某属性 setattr(m1,"displacement",650) #新增m1的"displacement"数据属性 setattr(m1,"show_brand",lambda self: self.brand+"666") #新增m1的函数属性 setattr(m1,"price",26800) #春风做活动,回馈车友,减价2000,修改m1的价格 setattr(motorcycle,"show_brand",lambda self:self.brand+"666") #新增类的函数属性 setattr(motorcycle,"feature","两个轱辘,还有三个轱辘的") #修改类的特征 print(m1.show_brand(m1)) #注意,此时"show_brand"就是一个普通函数,需要传一个参数进去 print(motorcycle.show_brand(m1)) #类名.新增的函数名加括号调用,因为是函数属性,so,要传参 print(m1.__dict__) #打印对象m1的所有的属性 print(motorcycle.__dict__) #打印类motorcycle的所有的属性 -------------------输出结果-------------------------- 春风666 春风666 {'brand': '春风', 'price': 26800, 'displacement': 650, 'show_brand':\ <function <lambda> at 0x0000000002113E18>} {'__module__': '__main__', 'feature': '两个轱辘,还有三个轱辘的',\ '__init__': <function motorcycle.__init__ at 0x00000000025FA950>,\ 'advance': <function motorcycle.advance at 0x00000000025FA9D8>,\ 'stop': <function motorcycle.stop at 0x00000000025FAA60>, '__dict__': \ <attribute '__dict__' of 'motorcycle' objects>, '__weakref__': <attribute \ '__weakref__' of 'motorcycle' objects>, '__doc__': None, 'show_brand': \ <function <lambda> at 0x00000000025FAAE8>}
3.4、delattr(obj,"name"):删除obj里的"name"的属性
#删除某属性 delattr(m1,"displacement") #删除m1的"displacement"数据属性 delattr(m1,"show_brand") #删除m1的"show_brand"的函数属性 delattr(motorcycle,"show_brand") #删除类的函数属性 print(m1.__dict__) #打印对象m1的所有的属性 print(motorcycle.__dict__) #打印类motorcycle的所有的属性 --------------------输出结果------------------------- {'brand': '春风', 'price': 28800} {'__module__': '__main__', 'feature': '两个轱辘', '__init__': \ <function motorcycle.__init__ at 0x000000000291A950>,\ 'advance': <function motorcycle.advance at 0x000000000291A9D8>, \ 'stop': <function motorcycle.stop at 0x000000000291AA60>,\ '__dict__': <attribute '__dict__' of 'motorcycle' objects>,\ '__weakref__': <attribute '__weakref__' of 'motorcycle' objects>,\ '__doc__': None}
4、为什么用反射之反射的好处
好处一:实现可插拔机制
可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思
?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。
#类代码同上 if hasattr(m1,"advance"): #判断m1里是否含有"advance"属性,有就执行里面的代码 func = getattr(m1,"advance") #有就实现此行代码,这样就不会出现异常,将获取到的内存地址给func func() #调用次方法 print("继续其他逻辑") #就算没有也不会影响下面代码的执行,实现了可插拔机制 --------------------输出结果--------------------- 春风,出发了! 继续其他逻辑
好处二:动态导入模块(基于反射当前模块成员)
#模块(mokuai)的内容: x = 123 #变量x class A: #定义一个类 def aaa(self): #类A的函数属性aaa print("from aaa") def bbb(): #定义一个函数bbb print("from bbb")
动态导入模块:
import importlib this_module = importlib.import_module("mokuai") #语法就是this_module.属性名 print(this_module.x) #打印模块里变量x的值, a = this_module.A() #类A实例化对象a a.aaa() #对象a调用绑定方法 this_module.bbb() #调用函数bbb ----------------输出结果-------------------- 123 from aaa from bbb
三、__setattr__,__getattr__,__delattr__
class foo: def __init__(self,name): self.name = name def __setattr__(self, key, value): #添加/修改属性时,会触发它的执行 if not isinstance(value,str): raise TypeError("must be str") #self.key = value #注意:这样就无限递归了 self.__dict__[key] = value def __getattr__(self, item): #当属性不存在的时候,会触发它的执行 print("不存在----->%s"%item) def __delattr__(self, item): #删除属性时,会触发它的执行 # print("不能被删除!") #强制不让删除 #del self.item #注意:这样就无限递归了 self.__dict__.pop(item) #真正删除的方法 f1 = foo("michael") # 实例化对象f1 #调用__setattr__ print(f1.__dict__) #打印f1的属性字典 f1.age = "18" #"18"必须是str,赋值就会触发__setattr__的执行 print(f1.__dict__) #打印f1的属性字典 #调用__getattr__ print(f1.abcd) #此时abcd是不存在的,没有这个属性,会返回None #调用__delattr__ del f1.age #删除属性 print(f1.__dict__) #打印f1的属性字典 -------------------输出结果----------------------- {'name': 'michael'} {'name': 'michael', 'age': '18'} 不存在----->abcd None {'name': 'michael'}
四、二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型
来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的
方式进行二次加工)。
#基于继承的原理,来定制自己的数据类型(继承标准类型) class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和insert def append(self, p_object): #自己加工追加的方法 if not isinstance(p_object,int): #加上条件,不是int就抛出异常 raise TypeError("must be int") #返回提示输入类型 #self.append(p_object) #注意:这样就无限递归了 super().append(p_object) #此时才会完成追加 def insert(self, index, p_object): #自己加工指定位置插入的方法 if not isinstance(p_object,int): #加上条件,不是int就抛出异常 raise TypeError("must be int") #返回提示输入类型 super().insert(index,p_object) #此时才会完成插入 l1 = List([0,1,2]) #实例化对象l1 print(l1) #打印列表l1 l1.append(3) #对象l1追加元素3,注意必须是int print(l1) #打印列表l1 # l1.append("5") #此时会抛出异常,"5"不是int l1.insert(0,-1) #在下标0的位置,插入元素-1,注意元素的类型 l1.insert(5,4) #在下标5的位置,插入元素4,注意元素的类型 print(l1) #打印列表l1 print(l1.index(4)) #获取元素的下标,没有派生出自己的方法的时候,会继承父类的方法 --------------------输出结果------------------------ [0, 1, 2] [0, 1, 2, 3] [-1, 0, 1, 2, 3, 4] 5
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有
产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给
对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法。
import time class Open: #open是一个函数,不是类,不能用继承,so,用另一种方式:授权 def __init__(self,file_path,m="r",endoce="utf8"): self.file = open(file_path,mode=m,encoding=endoce) #获取文件句柄 self.file_path = file_path #文件路径 self.mode = m #文件打开模式 self.encoding = endoce #文件的编码方式 def write(self,line): #写入文件 t = time.strftime("%Y-%m-%d %X") #获取时间格式 self.file.write("%s ---> %s"%(t,line)) #前面加上时间,按格式写入 def __getattr__(self, item): #当方法不存在的时候,会触发它的执行 return getattr(self.file,item) #获取文件其他的方法,并返回给对象 f1 = Open("a.txt","w") #实例化对象f1,创建文件a.txt,以w模式 f1.write("1、你好,我摩旅去了!\n") #按自己加工的方法写入内容 f1.write("2、你好,我摩旅去了!\n") #按自己加工的方法写入内容 f1.write("3、你好,我摩旅去了!\n") #按自己加工的方法写入内容 f1.close() #关闭文件 f2 = Open("a.txt","r") #实例化对象f2,查看文件a.txt,以r模式 print(f2.read()) #打印输出a.txt的文件内容 f2.seek(0) #刚看完,光标走到最后位置了。要想再看,就得把光标调到最开始位置 print("-->:",f2.read()) #加上标识,再看一遍 f2.close() #关闭文件 -----------------输出结果------------------ 2017-04-24 18:30:57 ---> 1、你好,我摩旅去了! 2017-04-24 18:30:57 ---> 2、你好,我摩旅去了! 2017-04-24 18:30:57 ---> 3、你好,我摩旅去了! -->: 2017-04-24 18:30:57 ---> 1、你好,我摩旅去了! 2017-04-24 18:30:57 ---> 2、你好,我摩旅去了! 2017-04-24 18:30:57 ---> 3、你好,我摩旅去了!
授权小练习:
#要求: # 基于授权定制自己的列表类型,要求定制的自己的__init__方法, # 定制自己的append:只能向列表加入字符串类型的值 # 定制显示列表中间那个值的属性(提示:property) # 其余方法都使用list默认的(提示:__getattr__加反射) class list: #定义一个类list def __init__(self,li): self.li = li def append(self,value): #自己加工追加的方法 if not isinstance(value,str): #加上条件,不是str就抛出异常 raise TypeError("must be str") #返回提示输入类型 self.li.append(value) #追加到列表中 @property def pro(self): #加上装饰器的pro,获取属性 index = len(self.li)//2 #获取列表中间的索引 return self.li[index] #返回中间的元素 def __getattr__(self, item): #当方法不存在的时候,会触发它的执行 return getattr(self.li,item) #获取列表其他的方法,并返回给对象 l1 = list([1,2,3]) #实例化对象l1,新建列表 print(l1.li) #查看列表的内容 # l1.append(4) #此时会抛出异常,因为不是str类型 l1.append("4") #往l1列表追加内容 print(l1.li) #打印追加后的列表 print(l1.pro) #pro加property后,调用不用加括号了,跟获取数据属性一样 print(l1.index(2)) #自己没有定义的方法,会触发__getattr__,使用list其\ 他的方法,这里查看元素2的索引是多少 ---------------------输出结果--------------------- [1, 2, 3] [1, 2, 3, '4'] 3 1