Loading

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)

如果 classclassinfo 的 (直接、间接或 虚拟) 子类则返回 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
退出

posted @ 2020-12-29 15:54  吃了好多肉  阅读(102)  评论(0编辑  收藏  举报