zoukankan      html  css  js  c++  java
  • 第四周:Python反射

    反射(reflection):指的是在运行时获取类型的定义信息。

    本质:利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,是一种基于字符串的事件驱动

    简单来说,Python可以通过字符串来操作对象(类/方法/模块的)属性和方法(也可操作类),这就是Python的反射。

    在Python中实例化对象当前模块其他模块可以使用反射机制。

    Python反射的四个方法

    getattr(obj, name,default):获取指定对象的属性

    • 从对象和类中获取属性/方法

      • class A:
            
            id = 1
            
            def eat(self):
                print("吃")
                
        a = A()
        
        # 分别从对象和类中获取属性/方法
        print(getattr(A, "id"))  # 1
        print(getattr(a, "id"))  # 1
        
        print(getattr(A, "eat"))  # <function A.eat at 0x0000021D287F5620>
        print(getattr(a, "eat"))  # <bound method A.eat of <__main__.A object at 0x00000130DEC926A0>>
        
        e1 = getattr(A, "eat")
        print(e1, type(e1))  # <function A.eat at 0x000001D1391E6620> <class 'function'> 类型是方法
        e1(a)  # 放一个对象进去就可以运行该方法了,其实就是A().eat()
        
        e2 = getattr(a, "eat")
        print(e2, type(e2))  # <bound method A.eat of <__main__.A object at 0x0000021D28802748>> <class 'method'> 类型是函数
        e2()  # 从对象中获取方法就可以直接运行该方法了
        
    • 从其他模块获取属性和方法和类

      • # test.py
        
        class B:
            name = "这是类B"
            
            def run(self):
                print("跑")
                
        num = 10
        
        def count():
            print("加加加")
        
      • import test
        
        # 获取其他模块中的属性和方法
        print(getattr(test, "num"))  # 10
        print(getattr(test, "count"))  # <function count at 0x000001EE59ED6620>
        print(getattr(test, "B"))  # <class 'test.B'>
        
        # print(getattr(test, "B.name"))  # 这样就会报错
        # print(getattr(test, "B().name"))  # 这样也会报错
        
        b = getattr(test, "B")()  # 实例化这个获取的类
        # 这样就可以获取到其中的方法和属性了
        print(getattr(b, "name"))  # 这是类B
        print(getattr(b, "run"))  # <bound method B.run of <test.B object at 0x000001F4BBA42940>>
        
    • 从当前模块获取属性和方法和类

      • # 当前模块的反射本体就是当前模块的文件
        import sys
        str = "abc"
        def say(s="cba")->str:
        print(s)
            
        class C:
            pass
        
        obj = sys.modules[__name__]  # 这样就获得到了当前模块
        print(getattr(obj, "str"))  # abc
        print(getattr(obj, "say"))  # <function say at 0x000001F455622E18>
        print(getattr(obj, "C"))  # <class '__main__.C'>
        

    hasattr(obj, name):判断对象是否有对应的属性

    • class A:
          id = 1
          
          def eat(self):
              print("吃")
      a = A()
      print(hasattr(A, "id"))  # True
      print(hasattr(A, "eat"))  # True
      print(hasattr(a, "eat"))  # True
      
      print(hasattr(A, "name"))  # False
      print(hasattr(A, "say"))  # False
      print(hasattr(a, "A"))  # False
      
    • 其他模块和当前模块判断有没有指定属性和方法安照上面来就行,很简单。

    setattr(obj, name, value):设定指定类/对象的属性和方法

    • class A:
          id = 1
          
          def eat(self):
              print("吃")
              
      # 向类中设置属性
      setattr(A, "name", "哈哈哈")
      a = A()
      print(A.name)  # 哈哈哈
      print(a.name)  # 哈哈哈
      
      # 注意这里!!!!!!!!!!!!
      # 要想把函数放进类中,一定要给一个参数!用于表示调用类中方法的实例对象
      def drink(self):
          print("喝", self, type(self))
          
      # 向类中设置方法 这里取名drink0只为了区别drink函数本身,其实去掉0也没事
      setattr(A, "drink0", drink)
      print(getattr(A, "drink0"))  # <function drink at 0x0000018830F767B8>
      
      print(a.drink0)  # <bound method drink of <__main__.A object at 0x0000018830F82D68>>
      a.drink0()
      # 喝 <__main__.A object at 0x0000018830F82D68> <class '__main__.A'>
      
    • def func():
          text = "文本"
          print("hhh")
            
      # 向方法中添加
      setattr(func, "word", "一段话")
      # print(func.text)  # 这样会抛异常,没有该属性!
      print(func.word)  # 一段话
      
    • # 那么就可以进一步这样写
      def func():
          text = "文本"
          print("hhh")
          if(hasattr(func, "word")): # 打印后续传进来的变量
              print(getattr(func, "word"))
      
      print("*******")
      func()
      print("*******")
      
      # 设置新的
      setattr(func, "word", "一段话")
      # print(func.text)  # 这样会抛异常,说没有该属性!
      print(func.word)  # 一段话
      
      print("-------")
      func()
      print("-------")
      
      """
      打印结果:
      *******
      hhh
      *******
      一段话
      -------
      hhh
      一段话
      -------
      """
      
    • 有点Java反射的感觉了。可以在运行时添加和获取指定类或指定方法等

      • import test
        
        class A:
            id = 1
            
            def eat(self):
                print("吃")
        
        # 给别的模块添加一个新的类
        setattr(test, "A", A)
        
        # 这时可以实例化这个新添加的类了
        a = test.A()
        a.eat() # 吃
        
      • # 甚至可以添加初始化方法
        def __init__(self, name="默认名"):
            print("这是初始化方法")
            self.name = name
        
        setattr(A, "__init__", __init__)
        
        a = A("哈哈哈")
        print(a.name)
        """
        这是初始化方法
        哈哈哈
        """
        

    delattr(object, name):删除对象的指定属性/方法/类

    # 删除上面刚给test模块添加的类A
    delattr(test, "A")
    
    a2 = test.A()  # 抛异常
    

    删除操作也好理解。

    类中私有属性的获取和设置

    class C:
        
        def __init__(self):
            self.__name = "CCC"
        
        def get_id(self):
            print(self.__id)  # 假装可以设置私有方法,其实不行
        
        def print_page(self):
            if(hasattr(C.print_page, "page")):  # 有这个属性时打印出来
                print("page:", getattr(C.print_page, "page"))
        
    c = C()
    

    __init__方法添加的属性,算作局部变量而不是类的属性

    page = 10
    setattr(C.__init__, "self.page", page)
    setattr(C.__init__, "page", page)
    print(hasattr(c, "self.page"))  # False
    print(hasattr(c, "page"))  # False
    print(hasattr(C, "self.page"))  # False
    print(hasattr(C, "page"))  # False
    # print(c.page)  # 抛异常
    # print(C.page)
    print(hasattr(c.__init__, "page"))  # True	因此page这时是局部变量
    print(hasattr(C.__init__, "page"))  # True
    
    # 可以给类中普通方法添加方法
    setattr(C.print_page, "page", page)
    c.print_page()  # 10
    

    但是可以获取到私有方法

    print(hasattr(c, "__name"))  # False
    print(hasattr(c, "_C__name"))  # True	这有这样才能
    print(c._C_name)  # CCC
    

    那么引申出

    class D:
        
        def __init__(self):
            print("初始化D类")
            self.page = 10
            self.__ID = 2
        
        def print_id(self):
            print(self.__id)
            
        def print_id2(self):
            print(self._D_id)
            
        def print_word(self):
            print("打印word:" + self.__word)
    
    def __init__(self, name="DDD"):
        print("D类初始化")
        self.name = name
        self.__id = 1
        self._D_word = "word" # 这样可以曲线添加给__init__添加私有属性
        _D_text = "text"  # 这不算类的属性,只能算局部变量
    
    
    d = D()  # 初始化D类
    print(d.page)  # 10
    
    # 设置初始化方法,会覆盖掉原有的初始化方法
    setattr(D, "__init__", __init__)
    d2 = D()  # D类初始化
    
    print(d.page)  # 10
    # print(d2.page)  # 新实例化对象没有page属性
    
    
    # 查看私有属性
    # print(d.__ID)
    print(d._D__ID)  # 2
    
    # 看看新替换的__init__方法的私有属性可起作用
    print(d2.__id)  # 但这时__id就不是私有方法了,当作普通方法来了
    # d2.print_id()  # 抛异常
    # d2.print_id2()  # 抛异常
    
    # 所以想这样设置私有属性,这能_D__word这样类设置
    print(d2._D__word) # word
    d2.print_word()  # 打印word:word
    
    # print(d2._D_text)  # 抛异常,找不到这个属性
    

    可以通过类的__dict__来查看对象有哪些属性

    print(d.__dict__)  # {'page': 10, '_D__ID': 2}
    print(d2.__dict__)  # {'name': 'DDD', '__id': 1, '_D__word': 'word'}
    

    可以发现修改__init__初始化方法后,对象的属性变了,也可以动态添加私有属性了。

    另外,也可以通过__init__来查看类有哪些属性

    print(D.__dict__)
    
    """
    {'__module__': '__main__', 
    '__init__': <function __init__ at 0x0000022CD9B166A8>, 
    'print_id': <function D.print_id at 0x0000022CD9B16950>, 
    'print_id2': <function D.print_id2 at 0x0000022CD9B169D8>, 
    'print_word': <function D.print_word at 0x0000022CD9B16A60>, 
    '__dict__': <attribute '__dict__' of 'D' objects>, 
    '__weakref__': <attribute '__weakref__' of 'D' objects>, 
    '__doc__': None}
    """
    

    总结:

    • 反射的四个方法更常用在程序运行状态下,而不是在编程阶段。
    • 给类中setattr方法时,一定要给方法加上self形参并放在形参列表首位。
      • 添加创建的对象可以调用添加的方法和属性
    • 不能直接给类添加私有属性,但是可以单独写一个__init__(self,...)方法,里面按私有方法的存在方式_类名__属性名定义私有属性,再把该方法替换原有的类中的方法。
    • Python的私有方法其实就是_类名__私有方法名这样存储的。
  • 相关阅读:
    JVM指令
    spring源码分析之配置文件名占位符的解析(一)
    freemarker
    spring整合freemarker
    策略模式
    spring boot 学习笔记(一)之前端文件配置
    线程使用总结
    maven pom 配置 学习笔记(二)之搭建nexus私服
    删除数据库中所有存在表的数据
    自定义SWT控件七之自定义Shell(可伸缩窗口)
  • 原文地址:https://www.cnblogs.com/jiyou/p/14086985.html
Copyright © 2011-2022 走看看