反射 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. ios获取本机网络IP地址方法

    #include <ifaddrs.h> #include <arpa/inet.h>     - (NSString *)getIPAddress {           N ...

  2. 吾八哥学Python(六):运算符与表达式

    上篇简单学习了数学运算符,今天来学习下完整的Python运算符与表达式,具体看下面的表格吧! 表1 运算符与它们的用法 运算符 名称 说明 例子 + 加 两个对象相加 3 + 5得到8.’a’ + ‘ ...

  3. iOS 记录近期遇到的几个bug

    1. actionSheet与pickerView 不兼容 发生环境:ios 9以上,其他无测试. actionSheet与pickerView在一起使用时,当actionSheet弹出后,紧接着再弹 ...

  4. Java实用知识记录 —— 截止到Java8

    记录Java实用知识点,截止(包括)到Java8,只作概要的描述,不涉及到具体细节.变量:int.long的包装类支持无符号位操作,即其在内存中的位可以用来全部表示正数."_"可以 ...

  5. (转)uml各类图

    原文:http://www.cnblogs.com/way-peng/archive/2012/06/11/2544932.html 一.UML是什么?UML有什么用? 二.UML的历史 三.UML的 ...

  6. (转)log4j使用介绍

    原文出自: log4j使用介绍 日志是应用软件中不可缺少的部分,Apache的开源项目Log4j是一个功能强大的日志组件,提供方便的日志记录.以下是个人经验,具体请参考Log4j文档指南. Log4j ...

  7. 相对定位的div没有出现纵向滚动条

    在一个相对定位的div中绝对定位很多html元素,纵向没有滚动条出现.原因:我们期望作为原点的点在窗口原点的下方,但是滚动条以窗口原点作为原点,所以没有出现滚动条.解决:在div外面再套一个div,o ...

  8. jquery ajax 数据传输

    在 form表单中,需要发送给后台的是一串长数据,后台才能接受,而用户则只需要输入字符串中的一部分,这种情况下,就需要将用户输入内容,和剩余部分进行拼串,然后添加进 formData 中传输. 另一种 ...

  9. ASP.NET中登录时记住用户名和密码(附源码下载)--ASP.NET

    必需了解的:实例需要做的是Cookie对象的创建和对Cookie对象数据的读取,通过Response对象的Cookies属性创建Cookie,通过Request对象的Cookies可以读取Cookie ...

  10. Mybatis通用Mapper

    极其方便的使用Mybatis单表的增删改查 项目地址:http://git.oschina.net/free/Mapper 优点? 不客气的说,使用这个通用Mapper甚至能改变你对Mybatis单表 ...