• python——反射


    1、概述

    运行时,区别于翻译时,指的是程序被加载到内存中执行的时候。
    反射,reflection,指的是运行时获取类型定义信息。
    一个对象能够在运行时,像照镜子一样,反射出其类型信息。简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省。
    具有反射能力的函数有type(),isinstance(),callable().dir().getattr()等

    2、反射相关函数的用法

     1 class Point:
     2     '''反射函数用法举例'''
     3     name = 'alex'
     4     def __init__(self, x, y):
     5         self.x = x
     6         self.y = y
     7     def __str__(self):
     8         return "{} and {}".format(self.x, self.y)
     9     def show(self):
    10         print(self.x, self.y)
    11 
    12 p = Point(4, 5)
    13 print(p)  # 触发__str__()  输出:4 and 5
    14 print(p.__dict__)   # 不会有类变量和方法存在,只存在私有属性,可以清楚实例的机制  输出:{'x': 4, 'y': 5}
    15 print(Point.__dict__)
    16 # 修改实例的属性
    17 p.__dict__['y'] = 15
    18 print(p.__dict__)  #输出:{'x': 4, 'y': 15}
    19 # 增加实例的属性
    20 p.z = 10
    21 print(p.__dict__)   # 输出:{'x': 4, 'y': 15, 'z': 10}
    22 # 对象可进行的操作
    23 print(dir(p))

     上例通过属性字典__dict__来访问对象的属性,本质上就是利用反射的能力(可以将字典理解为一种反射),但是上面的例子中,访问的方式不优雅,Python提供了内置的函数

     3、反射函数

    内建函数 意义
    getattr(object,name[,default]) 通过name返回object的属性值,当属性不存在,当属性不存在,将使用default返回,如果没有default,则抛出AttributeError,name必须是字符串
    setattr(object,name,value) 若存在object.name这是属性,则覆盖,不存在则新增
    hasattr(object,name 判断对象是否具有这个名字的属性,返回bool值,name必须为字符串
    delattr(object,name) 用于删除对象的属性,相当于del object.name

     

    getattr()和hasattr()

    (1)动态调用方法和增添方法

     1 def bulk(self):
     2     print('%s is yelling...' % self.name)
     3 class Dog(object):
     4     num = 123
     5     def __init__(self, name):
     6         self.name = name
     7 
     8     def eat(self, food):
     9         print('%s is eating %s' % (self.name, food))
    10 
    11 obj = Dog('alex')
    12 
    13 for i in range(2):
    14     choice = input('>>>').strip()
    15     # 动态调用方法
    16     if hasattr(obj, choice):
    17         func = getattr(obj, choice)
    18         func('hotdog')
    19     # 增添方法
    20     else:
    21         setattr(obj, choice, bulk)
    22         func = getattr(obj, choice)
    23         func(obj)

    执行结果:

    >>>eat
    alex is eating hotdog
    >>>talk
    alex is yelling...

    (2)动态调用属性和增添属性

     1 for i in range(2):
     2     choice = input('>>>')
     3 #动态调用属性
     4     if hasattr(obj, choice):
     5         argu = getattr(obj, choice)
     6         print('%s=%s'%(choice,argu))
     7     else:
     8         setattr(obj, choice, 5)
     9         argu_new = getattr(obj, choice)
    10         print('{}={}'.format(choice, argu_new))

    执行结果:

    >>>num
    num=123
    >>>age
    age=5

    delattr()删除属性或方法

     1 >>> class Myclass:
     2 ...     num = 2
     3 ...     def __init__(self,name):
     4 ...             self.name=name
     5 ...     def func(self):
     6 ...             print('This is func')
     7 ...
     8 >>> obj = Myclass('alex')
     9 >>> obj.num
    10 2
    11 >>> obj.func()
    12 This is func
    13 >>> delattr(obj,'num')   # num是类变量只能通过类去删除,在实例对象中不存在num这一个变量
    14 Traceback (most recent call last):
    15   File "<stdin>", line 1, in <module>
    16 AttributeError: num
    17 >>> obj.num
    18 2
    19 >>> delattr(Myclass,'num')  # 删除属性num
    20 >>> obj.num
    21 Traceback (most recent call last):
    22   File "<stdin>", line 1, in <module>
    23 AttributeError: 'Myclass' object has no attribute 'num'
    24 >>> delattr(Myclass,'func')    # 删除方法
    25 >>> func()
    26 Traceback (most recent call last):
    27   File "<stdin>", line 1, in <module>
    28 NameError: name 'func' is not defined

    4、在类中使用内建反射函数(慎用,和直接外部使用反射函数有所不同)

    (1)getattr(object,name[,default])

     1 class Base(object):
     2     n = 0
     3 class Point(Base):
     4     z = 6
     5     def __init__(self, x, y):
     6         self.x = x
     7         self.y = y
     8     def show(self):
     9         print(self.x, self.y)
    10     def __getattr__(self, item):
    11         return item
    12 
    13 p = Point(4,5)
    14 print('p.x>>>>', p.x)
    15 print('p.y>>>', p.y)
    16 print('p.n>>>', p.n)
    17 print('p.t>>>', p.t)

    执行结果:

    1 p.x>>>> 4
    2 p.y>>> 5
    3 p.n>>> 0
    4 p.t>>> t

    实例属性会按照继承关系寻找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常标识找不到属性
    查找属性顺序为:
    p.__dict__---->继承的祖先类(直到object)的__dict__—>找不到—>调用getattr()

    2、setattr(object,name,value)

     1 class Base:
     2     n = 0
     3 class Point(Base):
     4     z = 6
     5     def __init__(self, x, y):
     6         self.x = x
     7         self.y = y
     8     def show(self):
     9         print(self.x, self.y)
    10     def __getattr__(self, item):
    11         return item
    12     def __setattr__(self, key, value):
    13         print('in the setattr:
    key->{}	value->{},'.format(key,value))

    (1)实例化对象:

    p1 = Point(4, 5)
    print(p1.__dict__)
    执行结果
    in the setattr:
    key->x    value->4,
    in the setattr:
    key->y    value->5,
    {}

    注释掉第12、13行代码

    p1 = Point(4, 5)
    print(p1.__dict__)
    执行结果:
    {'x': 4, 'y': 5}

    结论:我们可以看出,__setattr__()函数会先于__init__()执行,且并不会在实例化对象开辟的内存中保存,而是单独开辟了内存,保存变量初始化的值。其实执行顺序是先执行__setattr__(),再执行__init__(),

    只是变量的初始化已经完成了,不会再在实例对象的内存中保存实例变量了。

    (2)属性查找顺序

    1 print(p1.x, p1.y)
    2 print(p1.z)
    3 print(p1.n)
    4 执行结果:
    5 x y
    6 6
    7 0

    结论:其实我们变量的初始化函数根本就没有生效,在实例对象p1.__dict__什么都没有保存进去,应为setattr()另外开辟地址进行存储啦。所以找查找属性x,y时,先在实例属性中查找——》在类中查找——》在父类中查找——》找不到,触发__getattr__().

    1 p1.x = 50
    2 print(p1.x)
    3 print(p1.__dict__)
    4 执行结果>>>
    5 key->x    value->50,
    6 x
    7 {}

    (3)在设置属性的时候,属性需要加到实例的__dict__中,才会生效。

    1 p1.__dict__['x']=60
    2 print(p1.__dict__)
    3 print(p1.x)
    4 执行结果>>>
    5 {'x': 60}
    6 60

    3.__delattr__()

     1 class Point:
     2     z = 5
     3     def __init__(self, x, y):
     4         self.x = x
     5         self.y = y
     6     def __delattr__(self,item):
     7         print(item)
     8 p = Point(14,5)
     9 del p.x
    10 p.z = 15
    11 del p.z
    12 del p.Z
    13 print(Point.__dict__)

    执行结果:

    x
    z
    Z
    {'__module__': '__main__', 'z': 5, '__init__': <function Point.__init__ at 0x000002049C0A7620>, 

    '__delattr__': <function Point.__delattr__ at 0x000002049C0A76A8>, '__dict__': <attribute '__dict__' of 'Point' objects>,

    '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}

    结论:其实我们并未删除实例对象中的实例变量,我们最后可以看到p.__dict__的内容{'x': 14, 'y': 5},这说明del x操作只能触发__delattr_()函数,实际并没有完成相应的功能。

    4、getattribute()

     1 class  Base:
     2     n=0
     3 class Point(Base):
     4     z=6
     5     def __init__(self,x,y):
     6         self.x=x
     7         self.y=y
     8     def __getattr__(self,item):
     9         return item
    10     def __getattribute__(self,item):
    11         return item
    12 p1=Point(4,5)
    13 print(p1.__dict__)
    14 print(p1.x)
    15 print(p1.z)
    16 print(p1.n)
    17 print(p1.t)
    18 print(Point.__dict__)
    19 print(Point.z)

    执行结果:

    __dict__
    x
    z
    n
    t
    {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x000001F1C07C7620>, 

    '__getattr__': <function Point.__getattr__ at 0x000001F1C07C76A8>, '__getattribute__': <function Point.__getattribute__ at 0x000001F1C07C7730>, '__doc__': None} 6

    实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回值或者抛出一个AttributeError异常

    它的return值将作为属性查找的结果
    如果抛出AttributeError异常,则会直接调用__getattr__方法,因为属性没有找到
    __getattribute__方法中为了避免在该方法中无线递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,需要注意的是,除非明确知道__getattrtbute__方法用来做什么,否则不要使用
    属性查找顺序:
    实例调用__getattribute__()—>instance.dict–>instance.class.dict–>继承的祖先类(直到object)的__dict__–>调用__getattr__()

  • 相关阅读:
    calc PI
    c# 设置水印,消除水印
    设置windows10 背景颜色
    C# 获取当前路径
    反编译工具
    c# 窗口API,以及全屏锁定一些tips
    c# 几种singleton 实现
    List<T> JIT 分配策略
    软件工程
    mariaDB 安装/卸载+启动/关闭 服务
  • 原文地址:https://www.cnblogs.com/weststar/p/11427903.html
Copyright © 2020-2023  润新知