带你了解Python面向对象(4)高级篇:类型判断、反射

目录

  • 前言:
    • isinstance
    • issubclass
  • 反射
    • hasattr
    • getattr
    • setattr
    • delattr
    • 反射模块
      • 方式一:`__import__`
      • 方式二:importlib


前言:

学习完面向对象进阶篇以后,已经掌握了大部分面向对象编程的使用,那么本章节主要学习的是更高级一点的使用,通过Python提供的内置方法,达到将字符串反射到对象的效果,可以间接操作对象和类且不需要修改源码

先来介绍两个常用的类型判断方法:isinstanceissubclass


isinstance

对象与类之间判断

用于判断对象所属类是否匹配,可以判断带有继承关系的类(推荐使用)

语法:

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

issubclass

类与类之间判断

判断类是否为另一个类的子类或重孙类,注意:判断的是类

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:删除对象或者类中的属性

hasattr

根据字符串判断这个对象里面是否存在该属性,存在返回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


getattr

根据属性名获取:对象 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(对象|类名,属性名,属性值)

# 设置方法的语法
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

删除对象或类里面的属性与方法

语法:

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

将模块作为对象使用

它的作用与上面相似,不同的是它导入路径时不需要添加额外参数

借用上序的目录结构,我们首先将importlib导入

语法:

import importlib

importlib.import_module(name,package)

实例:

import importlib

moduler = 'pack.test1'

res = importlib.import_module()

print(res.a)

执行结果:

10

推荐使用这种反射模块,将反射后的模块作为对象使用

你可能感兴趣的:(Python面向对象,python,面向对象编程,反射)