python基础----面向对象进阶,isinstance,issubclass,反射,内置attr,定制自己的数据类型
一,isinstance用法,issbuclass用法:
1,用法:isinstance(对象,类) ,ininstance(object,class) 检测对象是不是class的对象,是返回结果True,不是返回结果False
1 #isinstance,检查对象是不是class对象,是的话返回True,不是返回False
2 class Foo:
3 def __init__(self,name):
4 self.name = name
5 def s1(self):
6 print("hello python")
7 a = Foo("egon")
8 print(isinstance(a,Foo)) #True
9
10 # a.s1 :a是对象,s1是对象的绑定方法,下面检测的是(对象的绑定方法)是不是class的对象,而不是对象,所以返回False
11 print(isinstance(a.s1,Foo)) #False
2,issubclass(sub,super) 检查sub这个类是否是super的派生类,也就是新建类是否继承了父类(超类),
继承了父类(超类)返回True,没有继承 返回False
用法 :issubclass(类,父类) , issubclass(sub,super)
1 # issubclass(sub,super) 检查sub这个类是否是super的派生类,也就是新建类是否继承了父类(超类),
2 # 继承了父类(超类)返回True,没有继承 返回False
3 class Foo(object):
4 pass
5 class Msg(Foo):
6 pass
7 class Go:
8 pass
9 print(issubclass(Msg,Foo)) #True Msg类,继承了Foo父类 返回True
10 print(issubclass(Go,Foo)) #False Go类, 没有继承Foo父类 返回False
二,反射:
1,python面向对象中的反射:
通过字符串的形式操作对象相关的属性。# python中的一切事物都是对象(都可以使用反射)
反射:getattr,setattr,delattr,hasattr
1.1 hasattr(object,"name") 判断对象中有没有一个字符串“name”对应的属性,有的话 返回 True, 没有的话返回False
1 class People:
2 country = "China"
3 def __init__(self,name):
4 self.name = name
5
6 p = People("egon")
7 8 print(hasattr(p,"name")) #等同于这句,效果是一样的 print("name" in p.__dict__)
9 print(hasattr(p,"country")) #True 先去p的名称空间找,没有,又去类里面找,找到了,返回True
10 print(hasattr(p,"walk")) # False p的名称空间没有walk这个属性,又去类里面找,类里面也没有,所以返回False
11 print(p.__dict__) #查看p的名称空间,以字典的形式返回结果
12 # 返回结果 :{'name': 'egon'}
1.2,修改对象名称空间内字典的字符串key对应的value,找到字符串key才修改,对象名称空间的字典内没有的话
新建一个字符串的key和value
用法 : setattr(object,"key",value)
1 class People:
2 country = "China"
3 def __init__(self,name,age):
4 self.name = name
5 p = People("egon",20)
6 print(p.__dict__) #原始p的名称空间是:{'name': 'egon'}
7 p.sex = "male" # 新添加一个 "sex" = "male"
8
9 print(p.__dict__) # 现在:{'name': 'egon', 'sex': 'male'}
10 setattr(p,"name","alex") #把“name”对象的“egon” 修改成了"alex"
11 print(p.__dict__) # {'name': 'alex', 'sex': 'male'}
12 setattr(p,"ca",18) # 新添加了一个字符串属性“ca”:18
13 print(p.__dict__) # 变成这样:{'name': 'alex', 'sex': 'male', 'ca': 18}
1.3 getattr(object,"name") , 获取对象的属性,有这个属性,就执行,没有就报错
getattr(object,"sex","提示信息") 加上“提示信息” 就不会报错了,没有“sex”这个属性,就会打印“提示信息”,也可以换成别的
1 class People:
2 country = "China"
3 def __init__(self,name,age):
4 self.name = name
5 def walk(self):
6 print("%s is walking...."%(self.name))
7 p = People("egon",20)
8 print(getattr(p,"name")) # egon
9 print(getattr(p,"sex","这个属性不存在")) # 结果:这个属性不存在, 因为__init__方法里没有sex属性,找不到,所以打印’这个属性不存在‘
10 print(getattr(p,"xxxx","这个属性不存在")) #同上
11 f = getattr(p,"walk") #把结果让变量f接收
12 f() #执行 egon is walking....
13
14 # 通常 hasattr 和 getattr 是连用的
15 if hasattr(p,"walk"): #查看“walk”在p名称空间里找,不在在去类里面找,找到了,判断成立,执行下面
16 func= getattr(p,"walk") #获取 字典里“walk”这个key ,func接收
17 func() #执行 egon is walking....
18 print(p.__dict__)
19 print(People.__dict__)
1.4 delattr(object,"name") 删除属性 跟del 差不多,只是转换成字符串形式的删除
#4 deleteattr(object,"name") 删除属性
class People:
country = "China"
def __init__(self,name,age):
self.name = name
self.age = age
def walk(self):
print("%s is walking...."%(self.name))
p = People("egon",20)
print(p.__dict__) # {'name': 'egon', 'age': 20}
delattr(p,"name") #跟这个是一样的 del p.name
print(p.__dict__) # {'age': 20}
2,反射模块,通过字符串方式导入模块
1 #反射当前模块的属性
2 import sys # 先导入sys
3
4 class Foo:
5 pass
6 def s1():
7 print("111111")
8
9 def s2():
10 print("222222")
11
12 this1 = sys.modules[__name__] #语法
13 print(this1)
14 print(hasattr(this1,"s1"))
15 print(hasattr(this1,"s3"))
1 #用输入字符串的的方式导入time模块
2 m = input(">>>>")
3 m1 = __import__(m)
4 print(m1.time())
5
6
7 #推荐使用方法
8 import importlib
9 t = importlib.import_module("time")
10 print(t.time())
3,反射的好处
1,实现可插拔机制
2,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,
什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
1 class FtpClient:
2 '''ftp服务端,没有实现具体的功能'''
3 def __init__(self,addr):
4
5 print("正在连接服务器[%s]"%addr)
6 self.addr = addr
7 def get(self):
8 pass
9
1 from client import FtpClient #导入client 模块 下的FtpClient类, 上面只定义,没有具体功能实现的
2
3 f = FtpClient(" 192.168.1.1 ") # 在这里 实例化
4
5 if hasattr(f,"get"): # 需要用到他的get功能,有的话就执行, 没有,就走下面的逻辑,
6 func = getattr(f,"get")
7 func()
8
9 #好处体现出来了 因为他没有写get功能,我就能开始写我后面的逻辑
10 print("执行下一个逻辑")#走下面的代码
11 ....
12 ....
三,内置attr:
1,__getattr__ : 只有属性不存在的情况,才会触发__getattr__,执行下面的代码,存在的属性,不会触发
1 class Foo:
2 def __init__(self,name):
3 self.name = name
4 #属性不存在的情况下才会触发
5 def __getattr__(self, item):
6 print("----->")
7 f = Foo("egon")
8 print(f.name)
9 print(f.age) #没有age这个属性,触发__getattr__
10 print(f.ort) #没有ort这个属性,才触发__getattr__
2,__setattr__ : 添加属性和修改属性会触发__setattr__运行
1 class Foo:
2 def __init__(self,name):
3 self.name = name # 等于 self.name = "egon" 自动触发下面的__setattr__
4 def __setattr__(self, key, value): #把 上面的值接收过来,一一对应,self=self,key=name,value=name
5 if not isinstance(value,str):
6 raise TypeError("must be str")
7 print("key:%s , value:%s"%(key,value)) #key:"name",value:"egon"
8 self.__dict__[key] = value # 把获取过来的key和value 加入类的名称空间的字典里
9
10 f= Foo("egon") #实例化
11 f.name = "alex" #添加和修改会触发__setattr__运行 ,这是修改
12 f.sex = "male" #添加和修改会触发__setattr__运行 , 这是添加
13 print(f.__dict__) # {'name': 'alex', 'sex': 'male'}
3,__delattr__ :只有del删除的时候,才会触发__delattr__运行,执行__delattr__下面的删除操作,
当然你也可以不让删除,加上限制就可以了
1 class Foo:
2 def __init__(self,name):
3 self.name = name
4 def __delattr__(self, item):
5 # del self.item #无限递归了 ,错误的
6 self.__dict__.pop(item) #正确的 去类的名称空间内字典中删除key
7
8 f= Foo("egon") #实例化
9 f.name = "alex" #修改属性
10 f.sex = "male" # 添加属性
11 print(f.__dict__) #{'name': 'alex', 'sex': 'male'}
12 del f.sex #只有del的时候才会触发__delattr__的运行 ,
13 print(f.__dict__) # {'name': 'alex'}
14 #已经把'sex': 'male' 删除了
15 del f.name
16 print(f.__dict__) # 变成空字典了{}
四:定制自己的数据类型:
1,包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
1.1继承方式实现,定制自己的数据类型
1 #使基于继承的原理定制自己的数据类型,继承标准类型
2 #只实现append,还有一个insert,其他的还需要自己设置
3 class List(list): #继承内置list
4 def append(self, object):
5 if not isinstance(object,int): #不是int类型抛出错误
6 raise TypeError("muth be int")
7 super().append(object) #继承父类的append追加方法
8
9 def insert(self, index: int, object: int): #提示index是int型,object也是int型的
10 if not isinstance(object,int): # 加上不是int数据类型抛出错误的限制
11 raise TypeError("muth be int")
12 super().insert(index,object) # 继承父类的安索引值,添加元素的insert方法
13 l = List([1,2,3,4])
14 l.append(100)
15 print(l) #现在列表内的是这样:[1, 2, 3, 4, 100]
16 l.insert(0,12)
17 print(l) #现在列表内是这样:[12, 1, 2, 3, 4, 100]
18
19 l.append("1") #添加字符串出错了:TypeError: muth be int
2, 授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
2.1,授权的方式实现,定制自己的数据类型:
1 #不能用继承的方式没来实现open函数的功能
2 #用授权的方式实现,定制自己的数据类型
3 import time
4
5 class Open:
6 def __init__(self,filepath,m="r",endcode="utf8"):
7 self.x = open(filepath,mode=m,encoding = endcode)
8
9 self.filepath = filepath
10 self.m = m
11 self.encoding = endcode
12
13 def write(self,line):
14 t = time.strftime("%Y-%m-%d %X") #年月日格式,当前时间
15 self.x.write("%s %s "%(t,line)) #把字符串和时间 写入self.x中
16
17 def __getattr__(self, item): #
18 return getattr(self.x,item)
19
20 #写
21 f = Open("a.txt","w")
22 f.write("11111111\n")
23 f.write("22222222\n")
24 #读
25 f1 = Open("a.txt","r")
26 #基于授权,获得read()方法
27 print(f.read())
五,作业:
1,基于授权定制自己的列表类型,要求定制的自己的__init__方法,
2,定制自己的append:只能向列表加入字符串类型的值
3,定制显示列表中间那个值的属性(提示:property)
4,其余方法都使用list默认的(提示:__getattr__加反射)
1 # 作业:
2 # 基于授权定制自己的列表类型,要求定制的自己的__init__方法,
3 # 定制自己的append:只能向列表加入字符串类型的值
4 # 定制显示列表中间那个值的属性(提示:property)
5 # 其余方法都使用list默认的(提示:__getattr__加反射)
6 class List:
7 def __init__(self,msg):
8 self.msg = msg #初始化msg,定制自己的__init__方法
9
10 def append(self,object): # 定制append方法
11 '''派生出自己的append方法,覆盖list的append方法'''
12 if not isinstance(object,str): #追加的不是str类型的,抛出错误
13 raise TypeError("math be str")
14 self.msg.append(object) # 把srt类型的值追加进自己的self.msg中
15
16 @property #
17 def call_index(self): #定义查看属性
18 l = len(self.msg)//2 #取列表长度,在取列表中间值
19 return l
20 def __getattr__(self, item):
21 return getattr(self.msg,item)
22
23 def __str__(self):
24 return str(self.msg)
25
26 f = List([1,2,3])
27 print(f) # [1, 2, 3]
28 f.append("hello")
29 print(f) # [1, 2, 3, 'hello']
30 f.insert(2,'python') #基于授权,获得insert方法
31 print(f) # [1, 2, 'python', 3, 'hello']
32 print(f.call_index) #2