Python基础之面向对象进阶一

时间:2021-10-29 18:05:50

  一、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

Python基础之面向对象进阶一

  三、__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