第四周: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的私有方法其实就是_类名__私有方法名这样存储的。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,列表是一种有序、可变的数据类型,可以存储多个元素。列表反射是指通过字符串来获取或操作列表中的元素或属性。 要实现列表反射,可以使用以下几种方法: 1. 通过索引获取元素:可以使用列表名加上索引的方式来获取列表中的元素。例如,如果有一个列表`my_list = [1, 2, 3]`,可以通过`my_list`来获取第一个元素。 2. 通过切片获取子列表:可以使用切片操作来获取列表中的一部分元素。例如,如果有一个列表`my_list = [1, 2, 3, 4, 5]`,可以通过`my_list[1:3]`来获取索引为1和2的元素,即返回`[2, 3]`。 3. 使用内置函数获取列表信息:Python提供了一些内置函数来获取列表的信息,如`len()`函数可以获取列表的长度,`max()`和`min()`函数可以获取列表中的最大值和最小值。 4. 使用反射函数`getattr()`:反射函数`getattr()`可以通过字符串来获取对象的属性或方法。对于列表来说,可以使用`getattr()`函数来获取列表的方法或属性。例如,如果有一个列表`my_list = [1, 2, 3]`,可以使用`getattr(my_list, 'append')`来获取列表的`append()`方法。 5. 使用eval()函数:eval()函数可以将字符串作为Python代码进行求值。对于列表来说,可以使用eval()函数来执行字符串中的列表操作。例如,如果有一个字符串`"my_list.append(4)"`,可以使用`eval("my_list.append(4)")`来将元素4添加到列表中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值