python:反射

Posted 韩非囚秦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python:反射相关的知识,希望对你有一定的参考价值。

  反射机制是通过python3内置的hasattr、getattr、setattr来实现的。即根据变量名的字符串形式来获取变量名的属性或方法。

一、通过反射查看已知对象的属性和方法

  getattr(object, name[, default]) -> value

  Get a named attribute from an object; getattr(x, ‘y‘) is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn‘t exist; without it, an exception is raised in that case.

  getattr接收三个参数:对象,对象的属性或方法,以及未获取到时的默认返回值。

class MyClass(object):
    country = "China"
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self._gender = None
    def hello(self):
        print("I‘m {}, {}.".format(self.name, self.age))
    def gender(self, gender):
        self._gender = gender
        print(self._gender)

print(getattr(MyClass, "country"))  # 获取对象的静态属性
my = MyClass("Li", 24)
print(getattr(my, "name"))          # 获取实例的变量(属性)
getattr(my, "hello")()              # 获取并执行实例的方法
getattr(my, "gender")("female")     # 传入参数

二、通过反射获取模块的方法

  1.访问当前模块的对象。  

def func():
    func.name = "Li"
    func.age = 24
    print("I‘m {}, {}.".format(func.name, func.age))


if __name__ == __main__:
    import sys  
    # 需要引入sys模块,用sys.modelus["__main__"]访问当前模块的内存地址
    getattr(sys.modules["__main__"], "func")()  # 从模块中获取属性
    print(getattr(func, "name"))

  2.访问其它模块的对象。

  将上面的代码保存到test1.py文件中,在test2.py中导入test1.py,同样可以访问MyClass类。

import test1
if __name__ == __main__:
    MyClass = getattr(test1, "MyClass")  # 这里也可以用sys.modules["test1"]来替代test1
    print(MyClass.country)
    my = MyClass("Li", 24)
    print(getattr(my, "name"))          # 获取实例的变量(属性)
    getattr(my, "hello")()              # 获取并执行实例的方法
    getattr(my, "gender")("female")     # 传入参数

三、__import__和importlib.import_module

  python3提供了一个特殊的方法:__import__(字符串参数)。__import__()方法会根据参数,动态的导入同名的模块。它的功能和getattr相似。

  将test2.py中的代码改为下面两行,即可实现同样的功能。

module = __import__("test1")
fun = getattr(module, "func")()

  或者这样写:

import importlib
module = importlib.import_module("test1")
fun = getattr(module, "func")()

  它们都是根据模块名来访问该模块的内存空间,从而获取全局变量、函数或者类等对象。

  来看一段文档介绍:

  Docstring:
  __import__(name, globals=None, locals=None, fromlist=(), level=0) -> module

  Import a module. Because this function is meant for use by the Python interpreter and not for general use, 
  it is better to use importlib.import_module() to programmatically import a module.
  # 导入模块应该用importlib.import_module(),__import__是提供给Python解释器使用的。
  # globals和locals、level参数可以忽略。
  The fromlist should be a list of names to emulate ``from name import ...‘‘, or an empty list to emulate ``import name‘‘.   When importing a module from a package, note that __import__(‘A.B‘, ...) returns package A when fromlist is empty, but its submodule B when fromlist is not empty.
  # fromlist是a list of names blabla。但是试了几次,好像只能用True来设置,其它不起作
module = __import__("test1", )  # 它相当于import test1
# module = __import__("test.person", fromlist=True)  # 它相当于from test import person
# 从模块中导入py文件,test是一个package,包含__init__.py和person.py
# person.py中包含MyClass类
Myclass = getattr(module, "MyClass")
my = Myclass("Li", 24)
my.hello()

  importlib的用法也相似。

import importlib
# module = importlib.import_module("test1")  # import test1
# module = importlib.import_module("test.person", package=True)  # from test import person
module = importlib.import_module("test_outer.test.person", package=True)  # from test_outer.test import person
Myclass = getattr(module, "MyClass")
my = Myclass("Li", 24)
my.hello()

四、例子

  1.遍历模块查找所需函数。

# 文件夹
test
    - __init__.py
    - drink.py   # 定义一个drink函数,打印"I‘m drinking."
    - person.py
    - say.py      # 同drink.py
    - sleep.py    # 同drink.py

  person.py

import os
import importlib

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

    def py_list(self):
        pys = os.listdir(os.path.dirname(__file__))
        modules = []
        for py in pys:
            if py.endswith(".py") and not py.startswith("__"):
                modules.append(importlib.import_module(py.split(".")[0]))  # 将多个模块全部导入到一个list中
        return modules

    def action(self):
        while True:
            imp = input("小明 >>> ")
            fun = None
            for module in self.modules:  # 遍历查询module,查找imp函数
                if hasattr(module, imp):
                    fun = getattr(module, imp)
                    break
            if fun:
                fun()
            else:
                 print("Order isn‘t correct.")

if __name__ == __main__:
    per = Person("Li")
    per.action()

  2.动态导入模块

  当然,也可以根据模块名和对象名,来获取相应的模块,并调用相应的方法。从而不必将所有的模块都导入进来。

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

    def action(self):
        while True:
            imp = input("小明 >>> ")
            try:
                module, fun = imp.split("/")
                module = importlib.import_module(module,)
                if hasattr(module, fun):
                    getattr(module, fun)()
                else:
                    print("Order isn‘t correct.")
            except:
                print("input not correct.")

if __name__ == __main__:
    per = Person("Li")
    per.action()   # 需要输入sleep/sleep 或者drink/drink

  3.setattr的使用

import importlib
class Person:
    def __init__(self, name):
        self.name = name
    def sleep(self):
        getattr(importlib.import_module("sleep"), "sleep")()
    def drink(self):
        getattr(importlib.import_module("drink"), "drink")()
    def fun(self, action):
        if hasattr(self, action):
            getattr(self, action)
        else:
            setattr(self, action, self.error)  # 设置aciton的函数,当然可以在上面的getattr的default关键字中设置
            getattr(self, action)()  # 调用action的函数
    def error(self):
        print("error.")

if __name__ == __main__:
    per = Person("Li")
    per.fun("wth")

 

以上是关于python:反射的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL片段着色器不照亮场景

将 OpenGL 片段着色器设置为仅通过漫反射减少 vec4 色点的 RGB 值,而不是 alpha

反射机制

反射机制入门

反射机制入门

反射机制入门