python面向对象之反射

发布时间 2024-01-06 20:03:51作者: Xiao0101

image

众所周知,每一个实例化对象都是有实例化属性或者方法的,之前我们对实例化对象进行属性方法查询、删除或者添加都是直接去通过对象名.属性 去操作的,所以今天我来讲其他方法去操作实例化对象的属性方法,看正文:

什么是反射

反射指的是在程序运行过程中可以“动态(不见棺材不落泪)”的获取对象的信息(属性), 在Python面对对象中,通过字符串的形式去操作对象的属性方法就称之为反射(在Python中一切事物都是可以为对象)

为什么要用反射

实现可插拔机制

  • 反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,意味着可以在程序运行过程中动态地绑定接口的实现。
  • 这种灵活性使得程序更容易扩展和维护。
class PluginInterface:
    def execute(self):
        pass


class PluginA(PluginInterface):
    def execute(self):
        print("插件A被启动")


class PluginB(PluginInterface):
    def execute(self):
        print("插件B被启动")


def run_plugin(plugin):
    plugin.execute()


# 使用反射调用插件
plugin_name = input("请输入插件名字(PluginA or PluginB)  :>>>> ")
# 从全局名称空间中获取到 插件名字对应的类
plugin_class = globals().get(plugin_name)

# 判断一下当前类是否存在 并且 判断当前类是否 有 PluginInterface 接口
if plugin_class and issubclass(plugin_class, PluginInterface):
    # 如果都成立会触发相应的方法
    run_plugin(plugin_class())
else:
    # 不存在则抛出异常
    print("Invalid plugin name")

动态导入模块(基于反射当前模块成员)

  • 动态导入模块是指在程序运行时根据字符串的形式导入模块。
  • 通过反射,可以动态导入模块的成员,实现更灵活的代码组织和管理。
import importlib

module_name = input("请输入模块名 :>>>> ")
method_name = input("请输入方法名 :>>>> ")

try:
    # 动态导入模块
    module = importlib.import_module(module_name)
    # 反射是否存在当前方法
    method = getattr(module, method_name)
    # 如果存在则执行当前方法
    method()
except ImportError:
    print("Module not found")
except AttributeError:
    print("Method not found")

如何使用反射

简介

Python提供了一些内置函数和内置属性,用于实现反射操作,其中常用的有以下几个:

  1. hasattr(obj, name): 检查对象是否有指定的属性或方法。其中,obj是要检查的对象,name是要检查的属性或方法的名称。如果存在该属性或方法,返回True,否则返回False。

  2. getattr(obj, name[, default]): 获取对象的指定属性或方法。其中,obj是要获取的对象,name是要获取的属性或方法的名称。如果对象有指定的属性或方法,返回对应的值;如果不存在该属性或方法,如果提供了default参数,则返回default值,否则会抛出AttributeError异常。

  3. setattr(obj, name, value): 给对象设置指定的属性或方法。其中,obj是要设置的对象,name是要设置的属性或方法的名称,value是要设置的属性或方法的值。

  4. delattr(obj, name): 删除对象的指定属性或方法。其中,obj是要删除的对象,name是要删除的属性或方法的名称。

除了上述函数外,还可以使用内置属性__dict__来直接访问对象的属性字典,通过操作这个字典,可以实现对对象属性的增删改查。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("人可以跑步")


person = Person('xaio', 18)

# 直接通过对象获取属性对应的值
print(person.name)
# xiao

# 可以查看类的属性,不能查看方法
print(person.__dict__)
# {'name': 'xiao', 'age': 18}

# 根据字典取值:拿到字典中对应的值
print(person.__dict__['name'])
# xiao

# 查看对象中的所有方法
print(person.__dir__())

# 查看 对象中 的所有方法, 列表中查看到的属性全为字符串
print(dir(person))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'run']

在python中实现反射非常简单,在程序运行过程中,如果我们获取一个不知道存有何种属性的对象,若想操作其内部属性,可以先通过内置函数dir来获取任意一个类或者对象的属性列表,列表中全为字符串格式,接下来就是想办法通过字符串来操作对象的属性了,这就涉及到内置函数hasattr、getattr、setattr、delattr的使用了(Python中一切皆对象,类和对象都可以被这四个函数操作,用法一样)。

常用的反射方法使用

1. hasattr() 函数

格式:
变量 = hasattr(对象, "对象属性")

作用:
判断对象是否有这个属性以及方法 ,返回布尔类型

示例:
class A(object):
    def __init__(self,name):
        self.name=name
    def fun(self):
        print(f'我是{self.name}')
user=A('Jack')

print(hasattr(user,'age')) #False
print(hasattr(user,'name')) #True
print(hasattr(user,'fun')) #True

2. getattr() 函数

格式:
变量名 = getattr(对象, 对象属性, 默认值)

作用:
获取指定对象的属性以及方法,然后返回这个属性或者方法

示例:
class A(object):
    def __init__(self,name):
        self.name=name
    def fun(self):
        print(f'我是{self.name}')
user=A('Jack')
print(user.name) #输出 Jack
 
b=getattr(user,'name')
a=getattr(user,'age',18) #注意注意!!!
print(user.age)#报错的
c=getattr(user,'fun')
print(b,a)
c()
print(user.__dict__)
#输出结果:
# Jack 18
# 我是Jack
# {'name': 'Jack'}
注意看,这里我a=getattr(user,'age',18),其中age不是这个实例化对象的属性,只是我通过这个函数把18这个数字赋值给了c,实际上user和age并没有参与这个赋值,因为user与age一定关系都没有。下面通过__dict__就很明显看出来.

3. setattr() 函数

格式:
setattr(对象, "对象属性", 值)

作用:
添加设置对象属性

示例:
#给对象添加属性
class A(object):
    def __init__(self,name):
        self.name=name
    def fun(self):
        print(f'我是{self.name}')
user=A('Jack')
#之前的做法:直接添加
user.age=18
print(user.age)
#通过字符串操作
setattr(user,'num','137***55***')
print(user.name,user.num)
print(user.__dict__)
#输出结果:
# 18
# Jack 137***55***
# {'name': 'Jack', 'age': 18, 'num': '137***55***'}

4. delattr() 函数

格式:
delattr(对象, 对象属性)  

作用:
删除对象属性或者方法

示例:
class A(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def fun(self):
        print(f'我是{self.name}')
        
user=A('Jack',18)#{'name': 'Jack', 'age': 18}
print(user.__dict__)
delattr(user,'age') 
#等效于 del user.age
print(user.__dict__)#{'name': 'Jack'}
可以看出,实例化对象user中的age属性被删除了.

当然,这四个函数不单单可以在实例化对象使用,Python一切皆对象,所以一个模块,一个函数都是可以去使用的。

通过反射,我们可以在运行时动态地操作对象的属性和方法,这在某些情况下非常有用,例如在配置文件中动态指定需要调用的方法,或者在运行时根据用户的输入来决定调用哪个方法等。但是需要注意的是,反射的使用应该谨慎,因为它破坏了封装性,可能导致代码的可读性和可维护性降低。