反射 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. This application failed to start because it could not find or load the Qt platform plugin "windows" 的问题原因以及解决方案

    1. 问题原因非常简单,经过各种百度,都没有找到解决方案,在此做一个记录备用. 2.原因就在于,项目目录使用了中文路径,然后出现了这个问题. 3.我是在使用 syncfusion 下的HTML 转PD ...

  2. MySql入门(1)

    MySql入门(1) 安装 检查系统中是否已经安装了MySQL sudo netstat -tap | grep mysql 若没有显示已安装结果,则没有安装.否则表示已经安装. sudo apt-g ...

  3. maven overlays 合并多个war

    http://kyfxbl.iteye.com/blog/1678121 http://jdonee.iteye.com/blog/794226

  4. angularJS的一点小笔记

    AngularJS用法:-------------------------------------------------- AngularJS 指令: AngularJS 通过被称为 指令 的新属性 ...

  5. LeetCode 55. Jump Game (跳跃游戏)

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  6. SSM(Spring+SpringMVC+Mybatis)框架搭建详细教程【附源代码Demo】

    [前言] 应某网络友人邀约,需要一个SSM框架的Demo作为基础学习资料,于是乎,就有了本文.一个从零开始的SSM框架Demo对一个新手来说,是非常重要的,可大大减少在学习过程中遇到的各种各样的坑,说 ...

  7. js 两个日期比较相差多少天

    var day1 = new Date("2017-9-17"); var day2 = new Date("2017-10-18"); console.log ...

  8. spring框架应用系列三:切面编程(带参数)

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7786715.html 解决问题 1.分离业务监控与业务处理.简单点 ...

  9. ASP.NET Core的身份认证框架IdentityServer4(4)- 支持的规范

    IdentityServer实现以下规范: OpenID Connect OpenID Connect Core 1.0 (spec) OpenID Connect Discovery 1.0 (sp ...

  10. ⑧bootstrap组件 文字图片 下拉菜单 按钮组 使用基础案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...