学习完面向对象进阶篇以后,已经掌握了大部分面向对象编程的使用,那么本章节主要学习的是更高级一点的使用,通过Python提供的内置方法,达到将字符串反射到对象的效果,可以间接操作对象和类且不需要修改源码。
先来介绍两个常用的类型判断方法:isinstance
、issubclass
对象与类之间判断
用于判断对象所属类是否匹配,可以判断带有继承关系的类(推荐使用)
语法:
isinstance(obj,class) # 对象、类名
实例:
l = [1,2,3,4,5]
res = isinstance(l,list)
>>> print(res)
> True
另外一种平常我们所写判断类型的方法:
l = [1,2,3,4,5]
# 可以判断对象的类是否匹配,只能判断对象是否属于这个类,不能判断带继承关系的类
>>> print(type(l) is list)
> True
这两种区别在于,一个可以判断带有继承关系的类型,另一种则不行
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
>>> print(isinstance(dog,Animal))
> True
>>> print(type(dog) is Animal)
> False
类与类之间判断
判断类是否为另一个类的子类或重孙类,注意:判断的是类
class Animal:
pass
class Dog(Animal):
pass
class People:
pass
class Tdog(Dog):
pass
>>> print(issubclass(Tdog,Animal))
> True
>>> print(issubclass(Tdog,People))
> False
这两种皆为我们日常做判断所用的内置方法,熟悉使用即可。
通过字符串来操作类或对象的属性和方法,Python提供了4个内置函数
1、hasattr:判断一个属性或方法,是否存在与这个类或对象中
2、getattr:根据字符串去获取对象里对应的属性、或方法的内存地址,加获取方法后加"()"括号即可执行
3、setattr:给对象或类设置属性和方法,或是更改已存在的属性和方法
4、delattr:删除对象或者类中的属性
根据字符串判断这个对象里面是否存在该属性,存在返回True,否则返回False
语法:
hasattr(对象|类名,属性名|方法名)
实例:
class People:
descr = 'this is People'
def __init__(self,name,age):
self.name = name
self.age = age
def test(self):
pass
p = People('jack',18)
# 判断对象是否存在该属性
hasattr(p,'name')
> True
>>> hasattr(p,'sex')
> False
>>> hasattr(People,'descr') # 判断类里面是否存在这个属性
> True
>>> hasattr(People,'test') # 只要类里面有该名称,则判断成功
> True
只要对象或类里面具备我们所指定的字符,那么就可以判断成功
还有一种类似的方式可以达到这个效果(了解即可)接着上面写
>>> print('jack' in p.__dict__)
> False
>>> print(p.__dict__)
> {
'name': 'jack', 'age': 18}
__dict__
里面代表对象的属性,以字典的形式存储的。推荐使用内置方法hasattr
根据属性名获取:对象 or 类里面的属性值、或方法的内存地址
语法:
getattr(对象|类名,属性名|方法名)
实例:
class People:
descr = 'this is People'
def __init__(self,name,age):
self.name = name
self.age = age
def test(self):
print('this test')
p = People('jack',18)
>>> print(getattr(p,'name'))
> 'jack'
>>> print(getattr(p,'sex'))
> 未找到sex属性,报错!!
我们尝试获取test方法进行调用
>>> print(getattr(People,'test'))
> <function People.test at 0x7f8cff60ec10>
>
>>> getattr(People,'test')
> TypeError: test() missing 1 required positional argument: 'self' 报错,未传递self参数
虽说我们通过类获取到它里面的方法内存地址,但只是获取了内存地址,调用时还是有几个参数传几个,上面我们调用时就少传递了一个self参数
虽说这样可以:getattr(People,'test')(p)
但未免有些麻烦,不如我们直接通过对象来调用
>>> getattr(p,'test')()
> 'this test'
已经可以看到效果,注意:如果未找到我们指定的字符,则会报错。我们使用getattr
是要获取属性的值或调用方法,如果要判断属性是否存在使用则hasattr
,不要大意了!
给类或对象设置属性和方法,或是更改已存在的属性
语法:
# 设置属性的语法
setattr(对象|类名,属性名,属性值)
# 设置方法的语法
setattr(对象|类名,方法名,函数名或其它对象的方法名)
实例:将设置的属性反射到对象里面
class People:
descr = 'this is People'
def __init__(self,name,age):
self.name = name
self.age = age
p = People('jack',18)
>>> setattr(p,'sex','male')
>>> print(p.sex)
> 'male'
# 还可以对已存在的属性进行更改
# 给类设置属性,步骤一致!对象名换成类名即可
设置方法,较为复杂一些,我们需要先在外部定义一个函数
def test():
print('this is test')
p = People('jack',18)
setattr(p,'test',test)
p.test()
> 'this is test'
# 给类设置方法,步骤一致!对象名换成类名即可
上面只是熟悉一下!我们定义的函数只是一个普通函数,而我们设置到对象或者类里面是要进行调用的,既然调用就要访问对象的属性,那么我们连对象都没有传递怎么访问呢。上面那种只是将方法反射到了对象里面,我们需要在上面的基础上进行优化。
如果设置一个方法反射到类里面去:需要这种写法:推荐使用这种反射到类里面,这样后续对象都可以访问到这个方法
def test(self):
print('这是通过外部设置进来的方法,打印效果就知道了:',self.age)
p = People('jack',18)
# 将方法反射到了类里面,默认在类里面设置方法都会携带self参数,这里设置的也不例外
setattr(People,'test',test)
p.test()
> '这是通过外部设置进来的方法,打印效果就知道了: 18'
可以看到,已经出现了效果!结尾还是要通过对象去调用,因为方法内有一个self参数,需要将对象传入
删除对象或类里面的属性与方法
语法:
delattr(对象|类名,属性名|方法名)
实例:
class People:
descr = 'this is People'
def __init__(self,name,age):
self.name = name
self.age = age
def test(self):
print('this is test')
p = People('jack',18)
delattr(p,'name') # 注意拼写:如果指定错误,也会因为找不到而报错
print(p.name)
> AttributeError: 'People' object has no attribute 'name'
# 成功删除!
我们来删除方法
p = People('jack',18)
>>> delattr(p,'test')
> AttributeError: test
# 这种写法是错误的哦!因为对象p里面没有test属性
纠正写法
delattr(People,'test')
> # 无报错信息,删除成功!
至此,反射的4种内置方法已经全部介绍完了,使用起来也没有很复杂。可以看到在使用反射后,并没有改变过源码,也就是没有动过类或对象里面的代码,我们就可以直接对其进行操作,最终实现了:增、删、改、查等操作
__import__
我们首先构建一个目录结构
目录结构:
test
pack(文件夹) > test1.py
test.py
正常导入
from pack import test1
>>> print(test1.a) # test1.py里面有一个a = 10
> 10
我们使用内置函数方法:__import__
帮助我们动态导入
inp_modular = 'pack.test1'
# 如果路径查找,则添加fromlist=True,我们这里是到了pack文件夹下面找到test1模块
res = __import__(inp_modular,fromlist=True)
>>> print(res.a)
> 10
但是我们只能导入模块,不能导入方法或者类,拿json模块举例
inp_modular = 'json.dump'
# 直接报错,因为dump并不是一个模块
res = __import__(inp_modular,fromlist=True)
直接导入模块是没有任何问题的
inp_modular = 'json'
res = __import__(inp_modular) # 因为并没有向后查找,所以不需要fromlist
>>> print(res.dump)
> <function dump at 0x7fd526f0ed30>
至于用处,可以根据需求结合,如果都是直接导入模块的话,可以写入到一个文件内,然后读取文件逐个来导入
将模块作为对象使用
它的作用与上面相似,不同的是它导入路径时不需要添加额外参数
借用上序的目录结构,我们首先将importlib
导入
语法:
import importlib
importlib.import_module(name,package)
实例:
import importlib
moduler = 'pack.test1'
res = importlib.import_module()
print(res.a)
执行结果:
10
推荐使用这种反射模块,将反射后的模块作为对象使用