Pthon魔术方法(Magic Methods)-反射
Pthon魔术方法(Magic Methods)-反射
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.反射概述
运行时,区别于编译时,指的时程序被加载到内存中执行的时候。 反射,reflection,指的时运行时获取类型定义信息。 一个对象能够再运行时,像照镜子一样,反射出其类型信息。 简单的说,再python这种,能够通过一个对象,找出其type,class,attribute或method的能力,称为反射或者自省。 具有反射能力的函数有type(),isinstance(),callable(),dir(),getattr()等。
二.反射相关的函数和方法
1>.看一个简单需求
有一个Point类,查看它的实例的属性,并修改它。动态为实例增加属性
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Point:
def __init__(self,x,y):
self.x = x
self.y = y def __str__(self):
return "Point({},{})".format(self.x,self.y) def show(self):
print(self.x,self.y) p = Point(10,20)
print(p)
print(p.__dict__)
p.__dict__['y'] = 30
print(p.__dict__)
p.z = 10
print(p.__dict__)
print(dir(p))
print(p.__dir__())
Point(10,20)
{'x': 10, 'y': 20}
{'x': 10, 'y': 30}
{'x': 10, 'y': 30, 'z': 10}
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
['x', 'y', 'z', '__module__', '__init__', '__str__', 'show', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
以上代码执行结果戳这里
2>.Python提供了内置的反射函数
getattr(object,name[,default])
通过name返回object的属性值。当属性不存在,将使用default返回,如果没有default,则抛出AttributeError。name必须为字符串。 setattr(object,name,value)
object的属性存在,则覆盖,不存在,则新增。 hasattr(object,name)
判断对象是否有这个名字的属性,name必须为字符串。 用上面的方法来修改上例的代码,具体代码如下:
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Point:
def __init__(self,x,y):
self.x = x
self.y = y def __str__(self):
return "Point({},{})".format(self.x,self.y) __repr__ = __str__ def show(self):
print(self.x,self.y) p1 = Point(10,20)
p2 = Point(50,100) print(repr(p1),repr(p2),sep="\n")
print(p1.__dict__)
setattr(p1,"y",33)
setattr(p1,"z",66)
print(getattr(p1,"__dict__")) #动态调用方法
if hasattr(p1,"show"):
getattr(p1,"show")() #动态为类增加方法
if not hasattr(Point,"add"):
setattr(Point,"add",lambda self,other:Point(self.x + other.x,self.y + other.y)) print(Point.add)
print(p1.add) #绑定方法
print(p1.add(p2)) #由于是绑定方法,因此只需要给add传递一个参数即可 #为实力增加方法
if not hasattr(p1,"sub"):
setattr(p1,"sub",lambda self,other:Point(self.x - other.x,self.y - other.y)) print(p1.sub) #未绑定方法
print(p1.sub(p1,p1)) #观察add咋子在谁里面,sub在谁里面
print(p1.__dict__)
print(Point.__dict__)
Point(10,20)
Point(50,100)
{'x': 10, 'y': 20}
{'x': 10, 'y': 33, 'z': 66}
10 33
<function <lambda> at 0x00000151BA593708>
<bound method <lambda> of Point(10,33)>
Point(60,133)
<function <lambda> at 0x00000151BA5A5438>
Point(0,0)
{'x': 10, 'y': 33, 'z': 66, 'sub': <function <lambda> at 0x00000151BA5A5438>}
{'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000151BA5A5678>, '__str__': <function Point.__str__ at 0x00000151BA5A51F8>, '__repr__': <function Point.__str__ at 0x00000151BA5A51F8>, 'show': <function Point.show at 0x00000151BA5A5318>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x00000151BA593708>}
以上代码执行结果戳这里
3>.这种动态增加属性的方式和装饰器修饰一个类,Mixin方式的差异在哪?
这种动态增删属性的方式是运行时来改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射能力具有更大的灵活性。
三.反射相关的魔术方法
1>.反射相关的魔术方法概述
__getattr__():
当通过搜索实例,实例的类及在祖先类查不到属性,就会调用此方法。 __setattr__():
通过"."访问实例属性,进行增加,修改都要调用它。 __delattr__():
当通过实例来删除属性时调用此方法。 __getattribute():
实例所有的属性调用都从这个方法开始。 属性查找顺序:
实例调用__getattribute__() ---> instance.__dict__ ---> instance.__class__.__dict__ ---> 继承的祖先类(知道object)的__dict__ ---> 调用__getattr__()
2>."__getattr__()"方法测试
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Base:
m = 100 class Point(Base):
n = 200 def __init__(self,x,y):
self.x = x
self.y = y def __str__(self):
return "Point({},{})".format(self.x,self.y) def __getattr__(self, item):
return "missing {}".format(item) p1 = Point(10,20)
print(p1.x)
print(p1.n)
print(p1.m)
print(p1.t) #实例属性会按照继承关系找,如果找不到,就会执行"__getattr__()"方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性。
10
200
100
missing t
以上代码执行结果戳这里
3>."__setattr__()"方法测试
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Base:
m = 100 class Point(Base):
n = 200 def __init__(self,x,y):
self.x = x
self.y = y def __str__(self):
return "Point({},{})".format(self.x,self.y) def __getattr__(self, item):
return "missing {}".format(item) def __setattr__(self, key, value):
"""
实例通过点号(".")设置属性,例如"self.x = x"属性赋值,就会调用"def __setattr__(self, key, value)"方法,该方法可用拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的"__dict__"。
很显然,我们在"__setattr__"方法中并没有给修改实例的"__dict__",而是仅仅时执行了打印语句,因此实例的"__dict__"中默认不会存在任何属性。
"""
print("setattr {} = {}".format(key,value)) p1 = Point(10,20) #实例化过程中会调用"__init__"方法,而"__init__"方法存在"self.x = x"的语句,因此每当执行类似语句都会调用"__setattr__"方法
print(p1.x) #如上所述,"def __setattr__(self, key, value)"并没有将"x"属性加入到实例字典中,且p1实例的类和父类Base以及Object类都没有"x"属性,因此在这里会调用"__getattr__"方法。
print(p1.n)
print(p1.m)
print(p1.t) #实例属性会按照继承关系找,如果找不到,就会执行"__getattr__()"方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性。 p1.x = 50
print(p1.x)
print(p1.__dict__)
p1.__dict__["x"] = 100 #实例属性要加到实例的"__dict__"中才能访问到,既然"def __setattr__(self, key, value)"没有帮我们完成这样的操作,我们就需要自己完成
print(p1.__dict__)
print(p1.x) #由于将"x"属性加入到实例字典中,因此我们可用访问到"x"属性啦!
setattr x = 10
setattr y = 20
missing x
200
100
missing t
setattr x = 50
missing x
{}
{'x': 100}
100
以上代码执行结果戳这里
4>."__delattr__()"方法测试
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Base:
m = 100 class Point(Base):
n = 200
d = {}
def __init__(self,x,y):
self.x = x
setattr(self,"y",y)
self.__dict__["a"] = 50 def __str__(self):
return "Point({},{})".format(self.x,self.y) def __getattr__(self, item):
print( "missing {}".format(item))
return self.d[item] def __setattr__(self, key, value):
print("setattr {} = {}".format(key,value))
self.d[key] = value def __delattr__(self, item):
"""
可用阻止通过实例来删除属性的操作。
"""
print("Can not del {}".format(item)) def show(self):
print(self.x,self.y) p1 = Point(10,20)
p1.show()
print(p1.a)
print(p1.__dict__)
print(Point.__dict__)
del p1.x
p1.q = 15
print(p1.__dict__)
del p1.q
del p1.n
del Point.n
print(Point.__dict__) #可用阻止通过实例来删除属性的操作,但是通过类依然可以删除
setattr x = 10
setattr y = 20
missing x
missing y
10 20
50
{'a': 50}
{'__module__': '__main__', 'n': 200, 'd': {'x': 10, 'y': 20}, '__init__': <function Point.__init__ at 0x00000276AEF35318>, '__str__': <function Point.__str__ at 0x00000276AEF35438>, '__getattr__': <function Point.__getattr__ at 0x00000276AEF354C8>, '__setattr__': <function Point.__setattr__ at 0x00000276AEF35558>, '__delattr__': <function Point.__delattr__ at 0x00000276AEF358B8>, 'show': <function Point.show at 0x00000276AEF35948>, '__doc__': None}
Can not del x
setattr q = 15
{'a': 50}
Can not del q
Can not del n
{'__module__': '__main__', 'd': {'x': 10, 'y': 20, 'q': 15}, '__init__': <function Point.__init__ at 0x00000276AEF35318>, '__str__': <function Point.__str__ at 0x00000276AEF35438>, '__getattr__': <function Point.__getattr__ at 0x00000276AEF354C8>, '__setattr__': <function Point.__setattr__ at 0x00000276AEF35558>, '__delattr__': <function Point.__delattr__ at 0x00000276AEF358B8>, 'show': <function Point.show at 0x00000276AEF35948>, '__doc__': None}
以上代码执行结果戳这里
5>."__getattribute__()"方法测试
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Base:
m = 100 class Point(Base):
n = 200
def __init__(self,x,y):
self.x = x
setattr(self,"y",y) def __getattr__(self, item):
print( "missing {}".format(item))
return self.d[item] def __getattribute__(self, item):
"""
实例的所有的属性访问,第一个都会调用"def __getattribute__(self, item)"方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常。
它的return值将作为属性查找的结果
如果抛出AttributeError异常,则会直接调用"__getattr__"方法,因为表述属性没有找到。 温馨提示:
"__getattribute__"方法中为了避免在该方法中无线的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如"object.__getattribute__(self,name)"。
注意,除非你明确地知道"__getattribute__"方法用来做什么,否则不要使用它。
"""
return item p1 = Point(10,20)
print(p1.__dict__)
print(p1.x)
print(p1.n)
print(p1.n)
print(p1.t)
print(Point.__dict__)
print(Point.n)
__dict__
x
n
n
t
{'__module__': '__main__', 'n': 200, '__init__': <function Point.__init__ at 0x00000230DA543708>, '__getattr__': <function Point.__getattr__ at 0x00000230DA555678>, '__getattribute__': <function Point.__getattribute__ at 0x00000230DA5551F8>, '__doc__': None}
200
以上代码执行结果戳这里
Pthon魔术方法(Magic Methods)-反射的更多相关文章
- php中的魔术方法(Magic methods)和魔术常亮
PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __construct(),类的构造函数 __destruct(),类的析构函数 __cal ...
- Pthon魔术方法(Magic Methods)-描述器
Pthon魔术方法(Magic Methods)-描述器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.描述器概述 1>.描述器定义 Python中,一个类实现了&quo ...
- Pthon魔术方法(Magic Methods)-上下文管理
Pthon魔术方法(Magic Methods)-上下文管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.上下文管理方法 __enter__: 进入与此对象相关的上下文.如果 ...
- Pthon魔术方法(Magic Methods)-可调用对象
Pthon魔术方法(Magic Methods)-可调用对象 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.可调用对象方法 __call__: 类中定义一个该方法,实例就可以像 ...
- Pthon魔术方法(Magic Methods)-容器相关方法
Pthon魔术方法(Magic Methods)-容器相关方法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.容器相关方法汇总 __len__: 内建函数len(),返回对象的 ...
- Pthon魔术方法(Magic Methods)-运算符重载
Pthon魔术方法(Magic Methods)-运算符重载 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Python运算符对应的魔术方法 1>.比较运算符 <: ...
- Pthon魔术方法(Magic Methods)-bool
Pthon魔术方法(Magic Methods)-bool 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.bool方法 __bool__: 内建函数bool(),或者对象放在逻 ...
- Pthon魔术方法(Magic Methods)-hash
Pthon魔术方法(Magic Methods)-hash 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.hash方法 __hash__: 内建函数hash()调用的返回值,返 ...
- Pthon魔术方法(Magic Methods)-可视化
Pthon魔术方法(Magic Methods)-可视化 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.关于可视化的魔术方法简介 __str__: str()函数,format ...
随机推荐
- python初级(302) 6 对象(五)
一.复习 1.什么是多态 2.什么是继承 二.继承:向父母学习 在面向对象编程中,类可以从其他类继承属性和方法.这样就有了类的整个家族,这个家族中的每个类共享相同的属性和方法.这样一来,每次向家族增加 ...
- Operation之变换操作符
buffer buffer方法的作用是缓冲组合, 第一个参数是缓冲时间, 第二个参数是缓冲个数, 第三个参数是线程 该方法简单来说就是缓存Observable中发出的新元素, 当元素达到某个数量, 或 ...
- Maya编程——沿Curve绘制圆柱
操作流程: 1. VS运行代码,生成插件 2. 打开Maya绘制曲线,加载插件 3. 选中绘制的曲线,运行插件 Posts1.0 代码: #include <maya/MSimple.h> ...
- oracle 高级用法, DECODE 排序, OVER(PARTITION BY X ORDER BY Y DESC) 开窗函数
场景 01 (IN 语句 排序 decode() 函数): 1,我们在查询中会经常使用这样的用法, select * from table_name t where t.id in (1, 3, 7, ...
- nginx入门系列之应用场景介绍
目录 HTTP服务器 反向代理服务器 作为一个虚拟主机下多个应用的反向代理 作为多个虚拟主机的反向代理 负载均衡器 简单轮训策略 最小连接数策略 客户端IP哈希策略 服务器权重策略 邮件代理服务器 官 ...
- Node.js实现PC端类微信聊天软件(一)
Github StackChat 技术栈 写这个软件StackChat的主要目的是巩固练习Node和对React的实践,也是为了学习东西,所以选用了这些自己还没在项目里使用过的技术,边学变写 Elec ...
- 一段隐藏文字的css代码,记录下
<span style="width:1px; height:1px; color:#fff; outline-width:hidden; overflow:hidden; displ ...
- hive 字符串截取
语法 :substr(字段,starindex,len) 下标从 1 开始 测试 ,) from siebel_cx_order limit ; -- -- -- -- -- -- -- -- -- ...
- Python之logging.basicConfig函数各参数
filename: 指定日志文件名 filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a' format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所 ...
- 【leetcode-135,62,63 动态规划】分发糖果 ,不同路径
分发糖果 老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分. 你需要按照以下要求,帮助老师给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果. 相邻的孩 ...