zoukankan      html  css  js  c++  java
  • 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")
  • 相关阅读:
    JS闭包
    css3 背景渐变
    css扩展技术:Less和Sass的区别
    HTML5 Canvas八大核心技术及其API用法
    HTML5新标签含义,用法及其与HTML4的区别
    当离散遇见连续
    素数测试
    概率采样问题
    二分查找及其变种
    C++与Java多态的区别
  • 原文地址:https://www.cnblogs.com/kuaizifeng/p/9092057.html
Copyright © 2011-2022 走看看