python描述符和属性查找
python描述符
定义
一般说来,描述符是一种访问对象属性时候的绑定行为,如果这个对象属性定义了__get__()
,__set__()
, and __delete__()
一种或者几种,那么就称之为描述符。描述符在属性查找的时候可以覆盖默认的属性查找行为。
如果一个对象定义了__get__()
和__set__()
方法,那么称之为数据描述符,如果只定义了__get__()
称之为非数据描述符。
描述符调用
描述符可以直接通过方法名调用,如d.__get__(obj)
。也可以通过属性查找调用,如: obj.d
,如果d定义了__get__()
,那么就会调用d.__get__(obj)
描述符的调用实现定义在object.__getattribute__()
方法中:
- 对于实例对象
b
,将b.x
转为type(b).__dict__['x'].__get__(b, type(b))
- 对于类
B
,将B.x
转为B.__dict__['x'].__get__(None, B)
__getattribute__()
用纯python写的话类似下面:
def __getattribute__(self, key):
"Emulate type_getattro() in Objects/typeobject.c"
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
注意点:
- 描述符由
__getattribute__()
方法调用 - 重载
__getattribute__()
方法可以屏蔽描述符的调用 - 描述符只能在新式类(即objects的子类)中才会起作用
object.__getattribute__()
与type.__getattribute__()
调用__get__()
过程不同- 属性查找时数据描述符总是覆盖实例属性(具体在python属性查找中解释)
- 非数据描述符可能被实例属性覆盖
实例属性指的是对于实例b,存在于b.__dict__中的属性。b.__dict__只包含有实例特有的属性,不包含类共有的属性。
描述符应用
Properties
property可以将一个类的方法构建成数据描述符,从而可以使得以类的属性调用的方式调用方法。
例如:
class C(object):
def getx(self): return self.__x
def setx(self, value): self.__x = value
def delx(self): del self.__x
x = property(getx, setx, delx, "I'm the 'x' property."
从而调用c.x
隐式调用c.getx()
如果Property用纯python写,那么等价于:
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
函数和方法
在python中,一切皆是对象。python的方法也不例外,所有python函数均是function
这个类的实例,function
定义了__get__()
方法(使用type(方法).__dict__可以得到__get__
这个属性),也就是所有函数均为非数据描述符。根据是实例调用还是类调用,描述符返回绑定方法和非绑定方法。
>>> class D(object):
... def f(self, x):
... return x
...
>>> d = D()
>>> D.__dict__['f'] # Stored internally as a function
<function f at 0x00C45070>
>>> D.f # Get from a class becomes an unbound method
<unbound method D.f>
>>> d.f # Get from an instance becomes a bound method
<bound method D.f of <__main__.D object at 0x00B18C90>>
绑定方法:实例对象调用方法时候会传出一个self参数,将这个实例对象与方法绑定。
非绑定方法:类直接调用方法属性
将function类用纯python写,等价如下:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj, objtype)
type.MethodType()
手动创建一个绑定方法来使用。只有当实例被使用的时候绑定方法才会被创建.
static method类和class method类
两者都是使用非数据描述符的方式构建描述符。
若使用纯python写,等价如下:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
python属性查找
如果有obj = Clz()
, 那么obj.attr
查找顺序如下:
- 调用
__getattribute_
- 如果
attr
是出现在Clz或其基类的__dict__中,且attr是data descriptor, 那么调用其__get__
方法 - 如果
attr
出现在obj的__dict__中, 那么直接返回obj.__dict__['attr']
- 如果
attr
出现在Clz或其基类的__dict__中
4.1 如果attr是non-data descriptor,那么调用其__get__
方法
4.2 返回__dict__['attr']
- 如果Clz有__getattr__方法,调用__getattr__方法,否则
- 抛出AttributeError
属性查找中__getattribute_
方法是无论如何都会被调用的,且从python描述符调用章节可知__getattribute_
实际上实现了描述符的调用。如果类同时定义了__getattr__
方法,只有在__getattribute_
显式调用__getattr__
或者__getattribute_
抛出AttributeError才会调用__getattr__
。
注意如果要重载
__getattribute_
方法,需要显式调用object.__getattribute__(self, name)
以防止无限循环.
例如:
class test(object):
t = 5
def __getattribute__(self, item):
if item=='t':
return super(test, self).__getattribute__(item)
return 6
t = test()
print t.t
print t.s
结果为:
5
6
python属性赋值
python通过对象的__setattr__
方法赋值,也可以通过setattr
方法,事实上setattr
就是通过调用__setattr__
完成赋值。属性赋值也会受到数据描述符影响。
举例来说:x.b=3
,属性赋值过程如下:
- 如果类重载了
__setattr__
方法,优先调用__setattr__
- 如果没有重载
__setattr__
方法,且b
是数据描述符,就会调用b.__set__()
- 如果以上都没有,调用
obj.__dict__['b'] = 3
(__setattr__
方法的默认行为)
推测跟
object.__getattribute__()
一样,object.__setattr__()
中实现了数据描述符的调用。
参考资料
https://docs.python.org/2/howto/descriptor.html
https://www.cnblogs.com/xybaby/p/6270551.html
https://zhuanlan.zhihu.com/p/48185978
https://zhuanlan.zhihu.com/p/48185978
python描述符和属性查找的更多相关文章
- python之属性描述符与属性查找规则
描述符 import numbers class IntgerField: def __get__(self, isinstance, owner): print('获取age') return se ...
- Python 属性描述符和属性的查找过程
属性描述符可以用来控制给属性赋值的时候的一些行为 import numbers class IntField: def __get__(self, instance, owner): return s ...
- python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解
1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...
- python高级编程之最佳实践,描述符与属性01
# -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' #最佳实践 """ 为了避免前面所有的 ...
- python描述符 descriptor
descriptor 在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor.descriptor通常用来改 ...
- Python描述符的使用
Python描述符的使用 前言 作为一位python的使用者,你可能使用python有一段时间了,但是对于python中的描述符却未必使用过,接下来是对描述符使用的介绍 场景介绍 为了引入描述符的使用 ...
- Python描述符 (descriptor) 详解
1.什么是描述符? python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问.这些方法有 __get__(), __set__(), 和__delete__().如 ...
- Python 描述符 (descriptor)
1.什么是描述符? 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性的一个代理.当需要属性时,可根据你遇到的情况,通过描述符进行访问他(摘自Pyth ...
- 杂项之python描述符协议
杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...
随机推荐
- layui(弹出层)
首先引入文件 layui.css jquery.min.js layui.js 弹出层 data-method 后面的属性控制是什么弹窗,在js中写方法 <div class="sit ...
- 中文版 R-FCN: Object Detection via Region-based Fully Convolutional Networks
R-FCN: Object Detection via Region-based Fully Convolutional Networks 摘要 我们提出了基于区域的全卷积网络,以实现准确和高效的目标 ...
- PIC kit3问题
1.通过pic kit3烧录pic16F1938的时候,pic kit3自动更新了firmware,但是仍然烧录不了pic16F1938,然后再次用pic kit3烧录pic18F45k80时,一直显 ...
- python内置的一些模块
logging模块: 默认情况下,logging将日志打印到屏幕,日志级别为WARNING:日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO & ...
- BZOJ2225: [Spoj 2371]Another Longest Increasing CDQ分治,3维LIS
Code: #include <cstdio> #include <algorithm> #include <cstring> #define maxn 20000 ...
- PHP XML To Array将XML转换为数组
// Xml 转 数组, 包括根键,忽略空元素和属性,尚有重大错误 function xml_to_array( $xml ) { $reg = "/<(\\w+)[^>]*?& ...
- [洛谷P1892][codevs2597]团伙
题目大意:有n个强盗,他们有这样的关系:1.朋友的朋友是朋友:2.敌人的敌人是朋友. 两个人是朋友,则他们在一个团伙中,是敌人则在不同团伙中. 现在给出一些朋友或敌人的关系,问最多有多少团伙.输入保证 ...
- java 获取config 配置文件
static ResourceBundle PropertiesUtil = ResourceBundle.getBundle("config"); public static S ...
- ubuntu安装和使用
1.查看ubuntu是32位还是64位 教程:jingyan.baidu.com/article/db55b609ab531f4ba30a2f13.html 2.安装maven 教程:www.linu ...
- ASP.NET-本地化、全球化
在<system.web>中加入一个全球化的标识,网站就可以自适应全球化了 也可以将出错信息全球化 上面的这种方式测试过对google浏览器好像没用,但是对IE内核的是可行的,可能goog ...