isinstance、issubclass以及反射
isinstance(object,class)
判断对象object是否为class的实例或者是class直接、间接或虚拟子类,是则返回True。如果 object 不是给定类型的对象,函数将总是返回 False
。isinstance与type类似,但type只能判断对象所属的类,不能判断基类。
>>> x = 10
>>> isinstance(x,int)
True
>>> isinstance(x,object)
True
>>> isinstance(x,str)
False
>>> class Myint(int):
... pass
...
>>> isinstance(Myint,object)
True
>>> isinstance(Myint,int) # 不是给定类型的对象
False
如果 classinfo 是类型对象元组(或由其他此类元组递归组成的元组),那么如果 object 是其中任何一个类型的实例就返回 True
。
>>> isinstance(x,(int,str))
True
>>> isinstance(x,((int,str),list))
True
>>> isinstance(x,((dict,str),list))
False
如果 classinfo 既不是类型,也不是类型元组或类型元组的元组,则将引发 TypeError
异常。
>>> isinstance(x,x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types
issubclass(class,classinfo)
如果 class 是 classinfo 的 (直接、间接或 虚拟) 子类则返回 True
。 类会被视作其自身的子类。 classinfo 也以是类对象的元组,在此情况下 classinfo 中的每个条目都将被检查。 在任何其他情况下,都将引发 TypeError
异常。
class A:
pass
class B(A):
pass
class C(B):
pass
x = C()
print(issubclass(C,A)) # True
print(issubclass(x,A)) # TypeError: issubclass() arg 1 must be a class
反射
反射指的是程序可以访问、检查和修改它本身的状态和行为的一种能力(自省)。
Python中反射指的是:通过字符串的形式操作对象相关的属性。Python中的一切事物都是一对象,都可以使用反射。
内置有四个函数:
hasattr(obj,str)
判断一个对象是否有某个属性或方法。
class Coder:
head = 1
def work(self):
print('打工技能')
f1 = 'head'
f2 = 'tail'
# 判断类中是否有此属性
print(hasattr(Coder,f1)) # True
print(hasattr(Coder,f2)) # Fasle
# 判断对象中是否有此属性.
c1 = Coder()
c1.tail = '哈哈哈'
print(hasattr(c1,f2)) # True
通过判断一个字符串是否在__dict__
字典中,也能判断该对象是否有该属性或方法。
class Coder:
head = 1
def work(self):
print('打工技能')
print('head' in Coder.__dict__)
True
getattr(obj,str, [None])
通过字符串获取对象中的属性或方法,可以指定属性不存在时返回None或其他值。
class Coder:
head = 1
def work(self):
print('打工技能')
d = getattr(Coder,'work')
print(d) # 函数内存地址 <function Coder.work at 0x00000000033BD430>
# 等同于下面,通过点获取,正常的调用方式,区别在于getattr可以用字符串。
f = Coder.work
d(1) # 此时d就是work方法,加括号调用,但要给self传个参数。
打工技能
d2 = getattr(Coder,'tail') # 没有给定属性则报错,所以可以先用hasattr判断一下。
AttributeError: type object 'Coder' has no attribute 'tail'
class A:
pass
print(getattr(A,'x','abc')) # 没有该属性则返回指定的值。
abc
setattr(obj,str_name,str_value)
1、通过字符串给对象赋值数据属性。
class Coder:
head = 1
def work(self):
print('打工技能')
setattr(Coder, 'tail', 0)
print(Coder.tail)
0
c1 = Coder()
setattr(c1,'name','张三')
print(c1.name)
张三
2、通过字符串给类赋值函数方法。
class Coder:
head = 1
def work(self):
print('打工技能')
def run(self):
print('跑步技能')
c1 = Coder()
setattr(Coder,'run',run) # 给类赋值
c1.run() # 调用对象的绑定方法会将对象传进去。
setattr(c1,'run',run) # 给对象赋值,那么仅为普通函数,不会自动将对象传进去,需要手动出入。
c1.run(c1)
delattr(obj,str)
通过字符串删除对象的属性或方法。
class Coder:
head = 1
def work(self):
print('打工技能')
delattr(Coder,'work')
print(Coder.work)
AttributeError: type object 'Coder' has no attribute 'work'
反射隐藏属性
hasattr:
class Coder:
__head = 1
def __work(self):
print('打工技能')
print(hasattr(Coder, '_Coder__head'))
True
getattr:
class Coder:
__head = 1
def __work(self):
print('打工技能')
h = getattr(Coder,'_Coder__work')
h(None)
打工技能
setattr:
class Coder:
head = 1
def work(self):
print('打工技能')
def run():
print('跑步技能')
setattr(Coder,'_Coder__run',run)
Coder._Coder__run() # 调用时同样需要变形。
跑步技能
反射模块
使用hasattr判断模块是否有某个成员。
import time
print(hasattr(time,'time'))
True
getattr获取方法。
my_time = getattr(time,'time')
print(my_time())
1609226695.2641723
__import__('字符串'):会导入给定字符串的模块。
ti = __import__('time')
print(ti.time())
1609155137.1639736
输入路径导入:
# d目录内的test模块内容
def a():
print('这是aaaaaaa')
module_path = input('输入导入的模块: ').strip()
module = __import__(module_path, fromlist=True ) # 需要加个参数
module.a()
会将字符串当成模块名,所以不要加from和import。
输入导入的模块: d.test
这是aaaaaaa
Python中更推荐使用的反射模块方式是使用importlib模块。
import importlib
m = importlib.import_module('d.test')
m.a()
这是aaaaaaa
不用加fromlist参数,模块名和模块路径都能接收,程序健壮性更高。
反射的应用
应用一:实现可插拔机制。
例如,多肉和张三协同开发一个项目,张三在写程序的时候需要用到多肉所写的类,但多肉旅游去了,还没有完成他的类,张三想到了反射,使用反射机制张三可以继续完成他的代码,等多肉旅游回来后在继续完成类的定义,并实现张三想要的功能。
也就是说,可以事先定好接口,接口只有在被完成后才会真正执行,这实现了即插即用,其实是一种 “ 后期绑定 ” ,可以事先将主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。
# 多肉还没有实现的功能。
class FtpClient:
'''ftp客户端,但还未实现具体的功能'''
def __init__(self,ip):
print('正在连接服务器{}'.format(ip))
self.ip = ip
# 张三代码可正常编写
from module import FtpClient
f1 = FtpClient(192.168.10.10)
if hasattr(f1, 'get'): # 判断get方法是否存在。
func_get = getattr(f1, 'get')
func_get()
else:
print('方法不存在')
应用二:
能在程序的运行过程中,接收用户的输入,以此来执行指定的功能,从而达到一个动态修改、执行属性或方法的作用。
class Ftp:
def __init__(self, ip, port):
self.ip = ip
self.port = port
def get(self):
print('get功能')
def put(self):
print('put功能')
def run(self):
while 1:
choice = input('>>>(q退出):').strip()
if choice.lower() == 'q':
print('退出')
break
if hasattr(self, choice):
method = getattr(self, choice)
method()
else:
print('输入的指令不存在。')
ftp = Ftp("192.168.10.10/24", 80)
ftp.run()
执行:
>>>(q退出):get
get功能
>>>(q退出):put
put功能
>>>(q退出):aaa
输入的指令不存在。
>>>(q退出):q
退出