反射 reflection 也有人称之为自省

作用:

运行时获取、添加对象的类型定义信息,包括类

内建方法:

getattr(object, name[, default])   返回object对象的name属性的值(name必须为字符串),当属性不存在时,将使用default返回,如果没有default,就会抛出AttributeError异常。
setattr(object, name, value)   设置object对象的name属性值,如果存在则覆盖,不存在就新增。
hasattr(object, name)     判断ojbect对象是否有name属性

魔术方法:

__getattr__         当通过搜索实例、实例的类及祖先类查找不到指定属性时调用此方法

__setattr__          通过.访问实例属性,进行增加、修改时调用此方法

__delattr__          当通过实例来删除属性时调用次方法

__getattribute__  实例所有的属性调用第一个都会调用该方法

三种动态增加属性的方式:

编译期:
  装饰器/Mixin
运行期:
  反射

# 回顾下命令分发器
def dispatcher():
cmds = {}
def reg(cmd,fn):
if isinstance(cmd,str):
cmds[cmd] = fn
else:
print('error') def run():
print(cmds.items())
while True:
cmd = input('>>>').strip()
if cmd == 'quit':
return
cmds.get(cmd,defaultfn)() def defaultfn():
print('default') return reg,run reg,run = dispatcher() reg('cmd1', lambda : print(1))
reg('cmd2', lambda : print(2))
print(run())

  

# 将命令分发器改装成类
class dispatcher: def cmd1(self):
return 'cmd1' # def reg(self):
# pass def run(self):
while True:
cmd = input('>>>').strip()
if cmd == 'quit':
return
print(getattr(self,cmd,self.default)()) def default(self):
return 'default' dis = dispatcher() print(dis.run())

  

# 使用反射为改造命令分发器
class dispatcher: def cmd1(self):
return 'cmd1' def reg(self,cmd,fn):
if isinstance(cmd,str):
# setattr(self,cmd.strip(),fn) #报TypeError异常,反射不会自动为实例自动绑定self参数
setattr(self.__class__, cmd.strip(), fn) #为类添加属性,正常,观察运行结果__dict__的值
else:
return 'Error' def run(self):
print(dispatcher.__dict__)
print(self.__dict__)
while True:
cmd = input('>>>').strip()
if cmd == 'quit':
return
print(getattr(self,cmd,self.default)()) def default(self):
return 'default' dis = dispatcher()
dis.reg('cmd2',lambda self: 'cmd2')
dis.reg('cmd3',lambda self: 'cmd3')
print(dis.run())

  

一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到该属性。

查找属性顺序为:

instance.dict --> instance.class.dict --> 继承的祖先类(直到object类)的dict --> 调用__getattr__()

__getattr__() 魔术方法举例:

例一:

# 类装饰器本质上就是调用__call__方法
# class A:
def __init__(self,x):
self.x = x def __getattr__(self, item):
return '__getitem__ {}'.format(item) a = A(10)
print(a.x)
---运行结果--
10

  

例二:

# 找不到指定属性时就会调用__getattr__方法
class A:
def __init__(self,x):
self.x = x def __getattr__(self, item):
return "missing: {}".format(item) print(A(10).y)
---运行结果--
missing: y

  

例三:

# 继承情况下同样是找不到指定属性就调用__getattr__方法
class Base:
n = 17 class A(Base):
m = 19
def __init__(self,x):
self.x = x def __getattr__(self, item):
return "missing: {}".format(item) print(A(10).y)
print(A(10).n)
print(A(10).m)
------
missing: y
17
19

  

__setattr__() 魔术方法举例:

可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例的__dict__。

# 实例化时和,和实例化后,为属性赋值时就会调用__setattr__方法
class Base:
n = 17 class A(Base):
m = 19
def __init__(self,x):
self.x = x def __getattr__(self, item):
return "__getattr__: {}".format(item)
# return self.item def __setattr__(self, key, value):
# return "{},{}".format(key,value)
# print('__setattr__:',key,value)
self.__dict__[key] = value
return self a = A(10)
print(1,a.y)
print(2,a.n)
print(3,a.m) a.y = 100
print(a.__dict__)
print(4,a.y)
------运行结果---------
1 __getattr__: y
2 17
3 19
{'y': 100, 'x': 10}
4 100

  

__delattr__() 魔术方法举例:

# 删除实例属性时调用__delattr__方法
class Base:
n = 17 class A(Base):
m = 19
def __init__(self,x):
self.x = x def __getattr__(self, item):
print(self.__dict__)
return "__getattr__: {}".format(item)
# print("__getattr__: {}".format(item))
# return self.__dict__[item] def __setattr__(self, key, value):
# return "{},{}".format(key,value)
# print('__setattr__:',key,value)
self.__dict__[key] = value
# print(self.__dict__)
return self def __delattr__(self, item):
print('delattr:{}'.format(item))
del self.__dict__[item] #删除的是实例的属性,不是类的属性
# del self.__class__.__dict__[item] #测试是可以删除类的属性
return self a = A(10)
print(1,a.y)
print(2,a.n)
print(3,a.m) a.y = 100
print(a.__dict__)
print(4,a.y) del a.y #只能删除实例属性,不能删除类属性
# del a.m #无法删除类的属性,否则抛异常:TypeError: 'mappingproxy' object does not support item deletion
print(a.__dict__)
print(A.__dict__)
---运行结果----
{'x': 10}
1 __getattr__: y
2 17
3 19
{'y': 100, 'x': 10}
4 100
delattr:y
{'x': 10}
{'__doc__': None, '__module__': '__main__', '__delattr__': <function A.__delattr__ at 0x00000152B9112F28>, '__getattr__': <function A.__getattr__ at 0x00000152B9112E18>, 'm': 19, '__init__': <function A.__init__ at 0x00000152B9112B70>, '__setattr__': <function A.__setattr__ at 0x00000152B9112EA0>}

  

# 类的属性字典是一个mappingproxy对象
# 参考:https://stackoverflow.com/questions/32720492/why-is-a-class-dict-a-mappingproxy
In [1]: class A:
...: n = 5
...: In [2]: a = A() In [3]: a.n
Out[3]: 5 In [5]: a.__dict__
Out[5]: {} In [6]: A.__dict__
Out[6]:
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'n': 5}) In [7]: print(A.__dict__)
{'n': 5, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} In [11]: type(A.__dict__)
Out[11]: mappingproxy In [12]: type(type(A.__dict__))
Out[12]: type

  

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

它的return值将作为属性查找的结果。如果抛出AttributeError异常,则会调用__getattr__方法,因为表示属性没有找到。

__getattribute__ 方法举例:

拦截在字典查找之前

# 访问一个属性时就会调用__getattribute__方法,找不到就返回AttributeError异常
# 如果抛出了AttributeError异常,就会调用 __getattr__ 方法
class Base:
n = 17 class A(Base):
m = 19
def __init__(self,x):
self.x = x def __getattribute__(self, item):
print('__getattribute__:',item)
raise AttributeError def __getattr__(self, item):
return "__getattr__: {}".format(item) def __setattr__(self, key, value):
print('__setattr__:',key,value) def __delattr__(self, item):
print('delattr:{}'.format(item))
if hasattr(self,item):
print('{} has attribute {}'.format(self,item))
del self.__dict__[item] #删除的是实例的属性,不是类的属性
return self a = A(10)
print(a.y)
-----运行结果-------
__setattr__: x 10
__getattribute__: y
__getattr__: y

  

  

Python 面向对象(四) 反射及其魔术方法的更多相关文章

  1. Python面向对象之反射,双下方法

    一. 反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序 ...

  2. python 面向对象(四)反射

    ####################总结########## 1. isinstance: 判断xxx是否是xxx类型的(向上判断) type: 返回xx对象的数据类型 issubclass: 判 ...

  3. 百万年薪python之路 -- 面向对象之 反射,双下方法

    面向对象之 反射,双下方法 1. 反射 计算机科学领域主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省) python面向对象中的反射:通过字符串的形式操作对象相关的属性.python ...

  4. Python 面向对象之反射

    Python 面向对象之反射 TOC 什么是反射? hasattr getattr setattr delattr 哪些对象可以使用反射 反射的好处 例子一 例子二 什么是反射? 程序可以访问.检查和 ...

  5. Python面向对象之常用的特殊方法(5)

    Python面向对象里面有很多特殊方法,例如__init__(构造方法),__del__(析构方法),这些方法对于面向对象编程非常重要,下面列出一些常用的特殊方法 (1)__call__ class ...

  6. 第三十四篇 Python面向对象之 反射(自省)

    什么是反射? 反射的概念是由Smith在1982年提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序语 ...

  7. python 面向对象之反射及内置方法

    面向对象之反射及内置方法 一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静 ...

  8. python面向对象进阶 反射 单例模式 以及python实现类似java接口功能

    本篇将详细介绍Python 类的成员.成员修饰符.类的特殊成员. 类的成员 类的成员可以分为三大类:字段.方法和特性. 注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存 ...

  9. python 面向对象之多态与绑定方法

    多态与多态性 一,多态 1,多态指的是一类事物有多种形态(python里面原生多态) 1.1动物有多种形态:人,狗,猪 import abc class Animal(metaclass=abc.AB ...

随机推荐

  1. 国内为什么没有好的 Stack Overflow 的模仿者?,因为素质太低?没有分享精神?

    今天终于在下班前搞定一个技术问题,可以准时下班啦.当然又是通过StackOverflow找到的解决思路,所以下班路上和同事顺便聊起了它,两个资深老程序猿,还是有点感叹,中国的程序员群体人数应该不少,为 ...

  2. 组件 layui 常用控件输入框

    一.普通输入框 input <div class="layui-form-item"> <label class="layui-form-label&q ...

  3. git subtree pull 错误 Working tree has modifications

    git subtree 是不错的东西,用于 git 管理子项目. 本文记录我遇到问题和翻译网上的答案. 当我开始 pull 的时候,使用下面的代码 git subtree pull --prefix= ...

  4. UWP win10 app 新关键字x:Bing

    原本使用MVVM开发,我们使用数据绑定是x:Binging 新的关键字x:Bing使用和原来x:Binging区别不大. <TextBox x:Name="textBox" ...

  5. springcloud干货之服务消费者(ribbon)

    本章介绍springcloud中的服务消费者 springcloud服务调用方式有两种实现方式: 1,restTemplate+ribbon, 2,feign 本来想一篇讲完,发现篇幅有点长,所以本章 ...

  6. mysql 中翻页

    万变不离其中 select * from tableName where 条件 limit 当前页码*页面容量-1 , 页面容量

  7. es6总结

    主要包括let const,模板字符串,解构赋值,箭头函数,扩展运算符,Promise,类,import export等 一.let和const 1.let所声明的变量只在let所在的代码块内有效.l ...

  8. eclipse中project facet问题

    一般出现在从别处import的项目上,只有项目文件夹上有红叉,其他地方都正常,现总结个人的几个解决方案: 有几种可能: 1,编码设置是否一致,也即是你项目原来的编码和现在eclipse用的默认编码是否 ...

  9. iOS 之 runtime --- 集百家之言

    runtime runtime用在什么地方? 说法 在程序运行过程中,动态的创建一个类(比如KVO的底层实现) 在程序运行过程中,动态地为某个类添加属性.方法,修改属性值\方法(method swiz ...

  10. 白皮书之C++学习第一天

    大三荒废了一年的时间在lol上,上头了吧.后悔也来不及了,总之我会拼命追回来的! 今天重拾起这本白皮书,也是很感慨啊! 废话不多说,好好学,好好找工作吧!大三结束了啊! 每个C++程序都有一个main ...