反射 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. 我的第一个python web开发框架(7)——本地部署前端访问服务器

    PS:本系列内容进度节奏会放的很慢,每次知识点都尽量少一点,这样大家接触的知识点少了,会更容易理解,因为少即是多.另外,对于后面代码部分,虽然尽量不用那些复杂的封装和类,但它并不表示看了就能全部记住, ...

  2. git subtree pull 错误 Working tree has modifications

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

  3. C语言 实现base64

    #include <stdio.h> const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw ...

  4. Java常用类(一)之Object类详解

    大家都知道Object是所有类的父类,任何类都默认继承Object 理论上Object类是所有类的父类,即直接或间接的继承java.lang.Object类.由于所有的类都继承在Object类,因此省 ...

  5. IntelliJ IDEA 2016.2激活

    激活码 43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lI ...

  6. (转)log4j使用介绍

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

  7. JS难点--面向对象(继承)

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Consolas; color: #a5b2b9 } 继承 让一个对象拥有另一个对象的属性或者 ...

  8. HTTP认证方式与https简介

    HTTP认证与https简介 HTTP请求报头: Authorization [ˌɔ:θəraɪˈzeɪʃn] HTTP响应报头: WWW-Authenticate [ɔ:ˈθentɪkeɪt] HT ...

  9. LaunchScreen.storyboard 换图的问题

    之前设置了`LaunchScreen.storyboard`,在这个storyboard中加了一个imageView,里面设置了一张图片launch.png,今天需要更换这个启动图片,我就直接去工程里 ...

  10. 笔记-windbg及时调试

    当程序在测试或者老化的时候很有用,只要程序有异常抛出,就能启用windbg调试,这样就能及时的保存现场. 程序崩溃时,windows系统会调用系统默认调试器,其设置在注册表 HKEY_LOCAL_MA ...