python 属性描述符_Python属性描述符(二)

Python存取属性的方式特别不对等,通过实例读取属性时,通常返回的是实例中定义的属性,但如果实例未曾定义过该属性,就会获取类属性,而为实例的属性赋值时,通常会在实例中创建属性,而不会影响到类本身。这种不对等的方式对描述符类也有影响。

def cls_name(obj_or_cls): # 传入一个实例,返回类名

cls = type(obj_or_cls)

if cls is type:

cls = obj_or_cls

return cls.__name__.split('.')[-1]

def display(obj):

cls = type(obj)

if cls is type: # 如果obj是一个类,则进入该分支

return ''.format(obj.__name__)

elif cls in [type(None), int]: # 如果obj是None或者数值,进入该分支

return repr(obj)

else: # 如果obj是一个实例

return ''.format(cls_name(obj))

def print_args(name, *args):

pseudo_args = ', '.join(display(x) for x in args)

print('-> {}.__{}__({})'.format(cls_name(args[0]), name, pseudo_args))

class Overriding: # <1>

"""a.k.a. data descriptor or enforced descriptor"""

def __get__(self, instance, owner):

print_args('get', self, instance, owner)

def __set__(self, instance, value):

print_args('set', self, instance, value)

class OverridingNoGet: # <2>

"""an overriding descriptor without ``__get__``"""

def __set__(self, instance, value):

print_args('set', self, instance, value)

class NonOverriding: # <3>

"""a.k.a. non-data or shadowable descriptor"""

def __get__(self, instance, owner):

print_args('get', self, instance, owner)

class Managed: # <4>

over = Overriding()

over_no_get = OverridingNoGet()

non_over = NonOverriding()

def spam(self): # <5>

print('-> Managed.spam({})'.format(display(self)))

有__get__和__set__方法的典型覆盖型描述符,在这个示例中,各个方法都调用了print_args()函数

没有__get__方法的覆盖型描述符

没有__set__方法,所以这是非覆盖型描述符

托管类,使用各个描述符类的一个实例

spam方法放这里是为了对比,因为方法也是描述符

覆盖型描述符

实现__set__方法的描述符属于覆盖型描述符,虽然描述符是类属性,但是实现了__set__方法的话,会覆盖对实例属性的赋值操作。特性也是覆盖型描述符,见Python动态属性和特性(二),如果没有提供设值函数,property类中的fset方法就会抛出AttributeError异常,指明那个属性时只读的

下面我们来看下覆盖型描述符的行为:

>>> obj = Managed() # <1>

>>> obj.over # <2>

-> Overriding.__get__(, , )

>>> Managed.over # <3>

-> Overriding.__get__(, None, )

>>> obj.over = 8 # <4>

-> Overriding.__set__(, , 8)

>>> obj.over # <5>

-> Overriding.__get__(, , )

>>> obj.__dict__["over"] = 9 # <6>

>>> vars(obj) # <7>

{'over': 9}

>>> obj.over # <8>

-> Overriding.__get__(, , )

创建托管实例Managed对象

obj.over触发描述符的__get__方法,__get__方法第一个参数是描述符实例,第二个参数的值是托管实例obj,第三个参数是托管类对象

Managed.over 触发描述符的__get__方法,第二个参数(instance)的值是 None,第一个和第三个同上

为obj.over赋值,触发描述符的__set__方法,最后一个参数的值是8

读取 obj.over,仍会触发描述符的__get__方法

跳过描述符,直接通过obj.__dict__属性设值

确认值在obj.__dict__属性中,在over键名下

然而,即使是名为over的实例属性,Managed.over描述符仍会覆盖读取 obj.over 这个操作

没有__get__方法的覆盖型描述符

>>> obj.over_no_get # <1>

>>> Managed.over_no_get # <2>

>>> obj.over_no_get = 8 # <3>

-> OverridingNoGet.__set__(, , 8)

>>> obj.over_no_get # <4>

>>> obj.__dict__['over_no_get'] = 6 # <5>

>>> obj.over_no_get # <6>

6

>>> obj.over_no_get = 7 # <7>

-> OverridingNoGet.__set__(, , 7)

>>> obj.over_no_get # <8>

6

描述符实例没有__get__方法,所以obj.over_no_get从类中获取描述符实例

直接从托管类中读取over_no_get属性,也就是描述符实例

为obj.over_no_get赋值hui会触发描述符类的__set__方法

因为__set__方法没有修改属性,所以在此读取obj.over_no_get获取的仍然是托管类中的描述符实例

通过实例的__dict__属性设置名为over_no_get的实例属性

现在,over_no_get实例属性会覆盖描述符实例

为obj.over_no_get实例属性赋值,仍然会经过__set__方法

读取时,只要有同名的实例属性,描述符实例就会被覆盖

非覆盖型描述符:没有实现__set__方法的描述符称为是非覆盖型描述符,如果设置了同名的实例属性,描述符会被覆盖,致使描述符无法处理那个实例的那个属性

>>> obj.non_over # <1>

-> NonOverriding.__get__(, , )

>>> obj.non_over = 6 # <2>

>>> obj.non_over # <3>

6

>>> Managed.non_over # <4>

-> NonOverriding.__get__(, None, )

>>> del obj.non_over # <5>

>>> obj.non_over # <6>

-> NonOverriding.__get__(, , )

obj.non_over触发描述符的__get__方法,第二个参数的值是obj

Managed.non_over是非覆盖型描述符,因此没有干涉赋值操作的__set__方法

obj有个名为non_over的实例属性,把Managed类的同名描述符属性遮盖掉

Managed.non_over描述符依然存在,会通过类截获这次访问

删除non_over实例属性

读取obj.non_over时,会触发类中描述符的__get__方法

覆盖类中的描述符:不管描述符是不是覆盖型的,为类属性赋值都能覆盖描述符

>>> obj = Managed() # <1>

>>> Managed.over = 1 # <2>

>>> Managed.over_no_get = 2

>>> Managed.non_over = 3

>>> obj.over, obj.over_no_get, obj.non_over # <3>

(1, 2, 3)

新建一个Managed实例

覆盖类中的描述符属性

通过实例访问描述符属性,新的值覆盖描述符

方法是描述符:在类中定义的函数属于绑定方法,用户定义的函数都有__get__方法,所以依附到类上时,就相当于描述符,方法是非覆盖型描述符

>>> obj = Managed()

>>> obj.spam # <1>

>

>>> Managed.spam # <2>

>>> obj.spam = 7 # <3>

>>> obj.spam

7

obj.spam获取的是绑定方法对象

Managed.spam获取的是函数

如果为obj.spam赋值,会遮盖类属性,导致无法通过obj实例访问spam方法

函数没有__set__方法,因此是非覆盖型描述符,从上面的例子来看,obj.spam和Managed.spam获取的是不同对象。与描述符一样,通过托管类访问时,函数__get__方法会返回自身的引用,但是通过实例访问时,函数的__get__方法返回的是绑定方法对象:一种可调用的对象,里面包装着函数,并把托管实例(如obj)绑定给函数的第一个参数(即self),这与functools.partial函数的行为一致

为了了解这种机制,让我们看下面一个例子:

import collections

class Text(collections.UserString):

def __repr__(self):

return 'Text({!r})'.format(self.data)

def reverse(self):

return self[::-1]

测试Text类:

>>> word = Text("forward")

>>> word # <1>

Text('forward')

>>> word.reverse() # <2>

Text('drawrof')

>>> Text.reverse(Text('backward')) # <3>

Text('drawkcab')

>>> type(Text.reverse), type(word.reverse) # <4>

(, )

>>> list(map(Text.reverse, ['repaid', (10, 20, 30), Text('stressed')])) # <5>

['diaper', (30, 20, 10), Text('desserts')]

>>> func = Text.reverse.__get__(word) # <6>

>>> func() # <7>

Text('drawrof')

>>> Text.reverse.__get__(None, Text) # <8>

>>> Text.reverse

>>> word.reverse # <9>

>>> Text.reverse.__get__(word)

>>> word.reverse.__self__ # <10>

Text('forward')

>>> word.reverse.__func__ is Text.reverse # <11>

True

Text实例的repr方法返回一个类似Text构造方法调用的字符串,可用于创建相同的实例

reverse方法返回反向拼写的单词

在类上调用方法并传入一个实例相当于调用实例的函数

从类获取方法和从实例获取方法的类型是不同的,一个是function,一个是method

Text.reverse相当于函数,甚至可以处理Text实例之外的其他对象

函数都是非覆盖型描述符。在函数上调用__get__方法时传入实例,得到的是绑定到那个实例上的方法

我们执行第六个步骤得到的func对象,与在实例上调用函数效果一样

调用函数的__get__方法时,如果instance 参数的值是None,那么得到的是函数本身

word.reverse表达式其实会调用Text.reverse.__get__(word),返回对应的绑定方法

绑定方法对象有个__self__属性,其值是调用这个方法的实例引用

绑定方法的__func__属性是依附在托管类上那个原始函数的引用

绑定方法对象还有个__call__方法,用于处理真正的调用过程,这个方法调用__func__属性引用的原始函数,把函数的第一个参数设置为绑定方法的__self__属性,这就是形参self的隐式绑定方式

描述符用法建议:

使用特性以保持简单:内置的property类创建的其实是覆盖型描述符,__set__方法和__get__方法都实现了,即便不定义设值方法也是如此。特性的__set__方法默认抛出AttributeError: can't set attribute异常,因此创建只读属性最简单的方式是使用特性

只读描述符必须有__set__方法:如果使用描述符类实现只读属性,__get__和__set__两个方法都必须定义,否则实例的同名属性会遮盖住描述符,只读属性的__set__方法只需要抛出AttributeError异常,并提供合适的错误信息

用于验证的描述符可以只有__set__方法:对仅用于验证的描述符来说,__set__方法应该检查value参数获得的值,如果有效,使用描述符实例的名称为键,直接在实例的__dict__ 属性中设置。这样,从实例中读取同名属性的速度很快,因为不用经过 __get__方法处理

仅有__get__方法的描述符可以实现高效缓存:如果只编写了__get__方法,那么创建的是非覆盖型描述符。这种描述符可用于执行某些耗费资源的计算,然后为实例设置同名属性,缓存结果。 同名实例属性会遮盖描述符,因此后续访问会直接从实例的__dict__ 属性中获取值,而不会再触发描述符的__get__方法

非特殊的方法可以被实例属性遮盖:由于函数和方法只实现了__get__方法,它们不会处理同名实例属性的赋值操作。因此,像 my_obj.the_method = 7这样简单赋值之后, 后续通过该实例访问the_method得到的是数字7——但是不影响类或其他实例。然而,特殊方法不受这个问题的影响。解释器只会在类中寻找特殊的方法,也就是说,repr(x)执行的其实是x.__class__.__repr__(x),因此x的__repr__属性对repr(x)方法调用没有影响。出于同样的原因,实例的_getattr__属性不会破坏常规的属性访问规则

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python3.6sysos_求大佬,这是什么情况啊

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Traceback (most recent call last): File "manager.py", line 13, in sys.exit(main()) File "manager.py", line 8, in main manager.run() File "/root/imooc/lib/python3.5/site-packages/flask_sc…

6个座位办公室最佳位置_四人办公室座次的首选最佳座位在哪儿

四人办公室虽然不再拥挤&#xff0c;但座次依然有好坏之分&#xff0c;首选最佳的座位应靠着窗户并不与门相对。选择一个好的座位&#xff0c;不仅有助于我们工作效率和工作质量的提高&#xff0c;还能帮助我们的事业运和财运得到提升。在大多数人较多的办公室里&#xff0c;并…

.Net学习笔记----2015-06-30(超市收银系统01-仓库类)

GUID&#xff1a; 产生一个不会重复的ID static void Main(string[] args){//产生一个不会重复的编号Console.WriteLine(Guid.NewGuid().ToString());Console.WriteLine(Guid.NewGuid().ToString());Console.WriteLine(Guid.NewGuid().ToString());Console.WriteLine(Guid.NewG…

python xml etree_python xml.etree解析xml

config.xml <?xml version"1.0" encoding"UTF-8"?>linux 30windows 201 &#xff0c;解析出xml文件的根元素 from xml.etree import ElementTree as ET treeET.parse(config.xml) roottree.getroot() 或者 &#xff08;从字符串中解析&#xff09;…

http协议报文体_HTTP协议扫盲(七)请求报文之 GET、POST-FORM 和 POST-FILE

ORACLE临时表空间总结临时表空间概念 临时表空间用来管理数据库排序操作以及用于存储临时表.中间排序结果等临时对象,当ORACLE里需要用到SORT的时候,并且当PGA中sort_area_size大小不够时,将会把数据放入 ...web自学网站coursera 网站很多新的技术,都是大牛和牛大学的,和外国新…

Winform开发之ADO.NET对象Connection、Command、DataReader、DataAdapter、DataSet和DataTable简介...

ADO.NET技术主要包括Connection、Command、DataReader、DataAdapter、DataSet和DataTable等6个对象&#xff0c;下面对这6个对象进行简单的介绍&#xff1a;&#xff08;1&#xff09;Connection对象的主要功能是与数据库进行连接&#xff08;事物处理也使用此对象&#xff09;…

python电子英汉词典显示_python网页抓取之英汉字典

linux的字典本人实在用起来不舒服&#xff08;stardict挺不错的&#xff0c;但是界面好看些&#xff0c;功能简单易用就好了&#xff09; &#xff0c;在线翻译又得打开庞大的浏览器....就打算自己写个&#xff0c;但是时间有限&#xff0c;为了简单&#xff0c; 还是用python抓…

清空缓存的命令_超详细的mysql数据库查询缓存原理解析、涉及命令、流程分析等...

概述mysql查询缓存在数据库优化可以起到很大的作用&#xff0c;今天主要针对这一块做一个总结&#xff0c;下面一起来看看吧~一、缓存条件&#xff0c;原理MySQL Query Cache是用来缓存我们所执行的SELECT语句以及该语句的结果集&#xff0c;MySql在实现Query Cache的具体技术细…

python与tensorflow的关系_python – 在TensorFlow,Session.run()和Tensor.eval()之间有什么区别?...

如果你有Tensor t&#xff0c;调用 t.eval()相当于调用tf.get_default_session()。run(t)。 您可以将会话设置为默认值&#xff0c;如下所示&#xff1a; t tf.constant(42.0) sess tf.Session() with sess.as_default(): # or with sess: to close on exit assert sess is t…

和lua的效率对比测试_Unity游戏开发Lua更新运行时代码!

最近沉迷lua脚本热更&#xff0c;想说这个可以提高多少菜鸡的调试效率&#xff0c;找了网上好多文章&#xff0c;但是都不行&#xff0c;尝试了很久&#xff0c;并且自己测试和学习&#xff0c;写了一遍&#xff0c;勉强能热更了。下面记录一下热更Lua的过程。一、用来卸载表格…

nodejs cluster ip hash_redis集群架构了解一下?一致性hash了解吗?

在前几年&#xff0c;redis 如果要搞几个节点&#xff0c;每个节点存储一部分的数据&#xff0c;得借助一些中间件来实现&#xff0c;比如说有 codis&#xff0c;或者 twemproxy&#xff0c;都有。有一些 redis 中间件&#xff0c;你读写 redis 中间件&#xff0c;redis 中间件…

硬盘主分区和拓展分区

主分区,也称为主磁盘分区,和扩展分区、逻辑分区一样,是一种分区类型。主分区中不能再划分其他类型的分区,因此每个主分区都相当于一个逻辑磁盘(在这一点上主分区和逻辑分区很相似,但主分区是直接在硬盘上划分的,逻辑分区则必须建立于扩展分区中)。 1. 一个硬盘可以有1到3个主分…

python如何比较大小_python列表如何比较大小

python列表如何比较大小 发布时间:2020-09-22 13:58:58 来源:亿速云 阅读:59 作者:小新 这篇文章给大家分享的是有关python列表如何比较大小的内容。小编觉得挺实用的,因此分享给大家做个参考。一起跟随小编过来看看吧。 Python中可以使用cmp()函数比较两个列表的大小。 c…

python 连接oracle_常用的Python库,给大家分享一下!

Tkinter———— Python默认的图形界面接口。Tkinter是一个和Tk接口的Python模块&#xff0c;Tkinter库提供了对Tk API的接口&#xff0c;它属于Tcl/Tk的GUI工具组。Tcl/Tk是由John Ousterhout发展的书写和图形设备。Tcl(工具命令语言)是个宏语言&#xff0c;用于简化shell下复…

js 获取某年的某天是第几周

/**2 * 判断年份是否为润年3 *4 * param {Number} year5 */6 function isLeapYear(year) {7 return (year % 400 0) || (year % 4 0 && year % 100 ! 0);8 }9 /**10 * 获取某一年份的某一月份的天数11 *12 * param {Number} year13 * param {Number} month14 *…

python 前端学习_python学习之路7 前端学习3

1.页面布局PosTion :fixedTitle.left{float:left;}.right{float:right;}.head{height:58px;background-color:#3c3c3c;}.head .fontsquarl{width:120px;height:58px;background-color:#F22E00;line-height:58px;}.head .logosuqarl{width:120px;height:58px;background-color:#…

2.5d generator 2.0_ps插件【2.5D插件】

近一两年来创意插画在视觉上的使用越来越多&#xff0c;设计师们都愈加倾向于使用漂亮的插画和产品结合让自己的设计脱颖而出。在最近这段时间新涌现的设计作品当中&#xff0c;强烈的视觉冲击力和产品结合的展现是它们共通的特点。那么2.5D插画也是作为近一两年的发展趋势&…

(转)女生应该找一个玩ACM的男生

1、强烈的事业心 将来&#xff0c;他也一定会有自己热爱的事业。而且&#xff0c;男人最性感的时刻之一&#xff0c;就是他专心致志做事的时候。所以&#xff0c;找一个机会在他全神贯注玩ACM的时候&#xff0c;从侧面好好观察他&#xff0c;你就会发现我说的话没错。 2、永不放…

人工神经网络_制作属于自己的人工神经网络

在本文中&#xff0c;我已经实现了具有Dropout和L2正则化的人工神经网络的完全向量化代码。在本文中&#xff0c;我实现了一个在多个数据集上测试的人工神经网络的完全向量化python代码。此外&#xff0c;并对Dropout和L2正则化技术进行了实现和详细说明。强烈建议通过人工神经…

decorators 参数_Python Decorators(二):Decorator参数

Python Decorators II: Decorator ArgumentsOctober 19, 2008(本文是(Python3之模式和用法)一书的章节节选第二部分&#xff0c;点击阅读第一部分)回顾&#xff1a;不含参数的decorators在前文中&#xff0c;我介绍了如何使用不含参数的decorators&#xff0c;并使用类来实现。…