技术图文:Python描述符 (descriptor) 详解
背景
今天在B站上学习“零基础入门学习Python”这门课程的第46讲“魔法方法:描述符”,这也是我们组织的 Python基础刻意练习活动 的学习任务,其中有这样的一个题目。
练习要求:
- 先定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性。
- 要求两个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转化后的结果。
- 华氏度与摄氏度的转换关系:
1 Fahrenheit = 1 Celsius*1.8 + 32
技术分析
为了解决这个问题,我们首先回顾__dict__
属性,以及__get__
,__set__
,__delete__
魔法方法,然后总结描述符这个 Python 语言特有的语法结构,最后写代码完成要求的任务。
1. __dict__
属性
class Test(object):cls_val = 1def __init__(self):self.ins_val = 10t = Test()
print(Test.__dict__)
# {'__module__': '__main__', 'cls_val': 1, '__init__': <function Test.__init__ at 0x000000EBCB65F598>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
print(t.__dict__)
# {'ins_val': 10}
根据 Python 的语法结构,t
为实例对象,Test
为类对象。其对应的属性ins_val
和cls_val
称为实例属性和类属性。实例t
的属性并不包含cls_val
,cls_val
是属于类Test
的。
t.cls_val = 20
print(Test.__dict__)
# {'__module__': '__main__', 'cls_val': 1, '__init__': <function Test.__init__ at 0x000000CB7EB5F598>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
print(t.__dict__)
# {'ins_val': 10, 'cls_val': 20}
可见,更改实例t
的属性cls_val
,只是新增了该属性,并不影响类Test
的属性cls_val
。
Test.cls_val = 30
print(Test.__dict__)
# {'__module__': '__main__', 'cls_val': 30, '__init__': <function Test.__init__ at 0x000000DAB2BFC048>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
print(t.__dict__)
# {'ins_val': 10, 'cls_val': 20}
可见,更改了类Test
的属性cls_val
的值,由于事先增加了实例t
的cls_val
属性,因此不会改变实例的cls_val
值。
2. __get__()
,__set__()
,__delete__()
魔法方法
__get__(self, instance, owner)
__set__(self, instance, value)
__del__(self, instance)
class Desc(object):def __get__(self, instance, owner):print("__get__...")print("self:", self)print("instance: ", instance)print("owner: ", owner)def __set__(self, instance, value):print('__set__...')print("self:", self)print("instance:", instance)print("value:", value)class TestDesc(object):x = Desc()t = TestDesc()
t.x# __get__...
# self: <__main__.Desc object at 0x0000009C9B980198>
# instance: <__main__.TestDesc object at 0x0000009C9B9801D0>
# owner: <class '__main__.TestDesc'>
可以看到,实例化类TestDesc
后,调用对象t
访问其属性x
,会自动调用类Desc
的__get__
方法,由输出信息可以看出:
self
:Desc
的实例对象,其实就是TestDesc
的属性x
instance
:TestDesc
的实例对象,其实就是t
owner
: 即谁拥有这些东西,当然是TestDesc
这个类,它是最高统治者,其他的一些都是包含在它的内部或者由它生出来的
3. 描述符的定义
某个类,只要是内部定义了方法__get__
,__set__
,__delete__
中的一个或多个,就可以称为描述符。Desc
类就是一个描述符(描述符是一个类)。
- 问题1. 为什么访问
t.x
的时候,会直接去调用描述符的__get__()
方法呢?
t
为实例对象,访问t.x
时,根据常规顺序。
首先,访问Owner
的__getattribute__()
方法(其实就是 TestDesc.__getattribute__()
),访问实例属性,发现没有,然后去访问父类!
其次,判断属性x
为一个描述符,此时,它就会做一些变动了,将TestDesc.x
转化为TestDesc.__dict__['x'].__get__(None, TestDesc)
来访问。
最后,进入类Desc
的__get__()
方法,进行相应的操作。
- 问题2. 从上面代码我们看到了,描述符的对象
x
其实是类TestDesc
的类属性,那么可不可以把它变成实例属性呢?
class Desc(object):def __init__(self, name):self.name = namedef __get__(self, instance, owner):print("__get__...")print('name = ', self.name)class TestDesc(object):x = Desc('x')def __init__(self):self.y = Desc('y')t = TestDesc()
t.x
t.y# __get__...
# name = x
咦,为啥没打印 t.y
的信息呢?
因为调用 t.y
时刻,首先会去调用TestDesc
(即Owner
)的 __getattribute__()
方法,该方法将 t.y
转化为TestDesc.__dict__['y'].__get__(t, TestDesc)
,但是呢,实际上 TestDesc
并没有y
这个属性,y
是属于实例对象的,所以,只能忽略了。
- 问题3. 如果 类属性的描述符对象 和 实例属性描述符的对象 同名时,咋整?
class Desc(object):def __init__(self, name):self.name = nameprint("__init__(): name = ", self.name)def __get__(self, instance, owner):print("__get__() ...")return self.namedef __set__(self, instance, value):self.value = valueclass TestDesc(object):_x = Desc('x')def __init__(self, x):self._x = xt = TestDesc(10)
t._x# __init__(): name = x
# __get__() ...
不对啊,按照惯例,t._x
会去调用 __getattribute__()
方法,然后找到了 实例t
的 _x
属性就结束了,为啥还去调用了描述符的 __get__()
方法呢?
这就牵扯到了一个查找顺序问题:当 Python 解释器发现实例对象的字典中,有与描述符同名的属性时,描述符优先,会覆盖掉实例属性。
我们再将代码改进一下, 删除 __set__()
方法试试看会发生什么情况?
class Desc(object):def __init__(self, name):self.name = nameprint("__init__(): name = ", self.name)def __get__(self, instance, owner):print("__get__() ...")return self.nameclass TestDesc(object):_x = Desc('x')def __init__(self, x):self._x = xt = TestDesc(10)
print(t._x)# __init__(): name = x
# 10
可见,一个类,如果只定义了 __get__()
方法,而没有定义 __set__()
, __delete__()
方法,则认为是非数据描述符;反之,则成为数据描述符。非数据描述符,优先级低于实例属性。
- 问题4. 天天提属性查询优先级,就不能总结一下吗?
① __getattribute__()
, 无条件调用
② 数据描述符
③ 实例对象的字典
④ 类的字典
⑤ 非数据描述符
⑥ 父类的字典
⑦ __getattr__()
方法
代码实现
class Celsius:def __init__(self, value=26.6):self.value = valuedef __get__(self, instance, owner):return self.valuedef __set__(self, instance, value):self.value = float(value)class Fahrenheit:def __get__(self, instance, owner):return instance.cel * 1.8 + 32def __set__(self, instance, value):instance.cel = (float(value) - 32) / 1.8class Temperature:cel = Celsius()fah = Fahrenheit()temp = Temperature()
print(temp.cel) # 26.6
print(temp.fah) # 79.88
temp.cel = 30
print(temp.cel) # 30
print(temp.fah) # 86.0
temp.fah = 79.88
print(temp.cel) # 26.599999999999998
print(temp.fah) # 79.88
总结
通过以上的介绍我们了解了 Python 中描述符的定义,以及属性调用的优先级。由于Python魔法方法非常复杂需要下很大的功夫才能把这块搞明白。今天就到这里吧,See you!
参考文献
- https://www.runoob.com/python3/python3-tutorial.html
- https://www.bilibili.com/video/av4050443
- http://c.biancheng.net/view/2371.html
- https://www.cnblogs.com/seablog/p/7173107.html
- https://www.cnblogs.com/Jimmy1988/p/6808237.html
相关图文:
- 资料分享:数学建模资料分享 – 图论部分
- 资料分享:数学建模资料分享 – 神经网络部分
- 如何利用 C# 实现 K 最邻近算法?
- 如何利用 C# 实现 K-D Tree 结构?
- 如何利用 C# + KDTree 实现 K 最邻近算法?
- 如何利用 C# 对神经网络模型进行抽象?
- 如何利用 C# 实现神经网络的感知器模型?
- 如何利用 C# 实现 Delta 学习规则?
- 如何利用 C# 实现 误差反向传播 学习规则?
- 如何利用 C# 爬取带 Token 验证的网站数据?
- 如何利用 C# 向 Access 数据库插入大量数据?
- 如何利用 C# + Python 破解猫眼电影的反爬虫机制?
相关文章:

[转]自定义hadoop map/reduce输入文件切割InputFormat
本文转载自:http://hi.baidu.com/lzpsky/blog/item/99d58738b08a68e7b311c70d.html hadoop会对原始输入文件进行文件切割,然后把每个split传入mapper程序中进行处理,FileInputFormat是所有以文件作 为数据源的InputFormat实现的基类&…

使用深度学习检测DGA(域名生成算法)——LSTM的输入数据本质上还是词袋模型...
from:http://www.freebuf.com/articles/network/139697.html DGA(域名生成算法)是一种利用随机字符来生成C&C域名,从而逃避域名黑名单检测的技术手段。例如,一个由Cryptolocker创建的DGA生成域xeogrhxquuubt.com,如…

学习Python开发培训有用吗
学习Python开发培训有用吗?这是目前很多人都比较关注的一个问题,Python语言在最近几年是广受IT互联网行业关注的, 下面我们就针对这问题来详细的分析一下。 学习Python开发培训有用吗?Python是被广泛使用的高级编程语言,Python解释器本身几…

Web性能优化实践——应用层性能优化
随着公司项目的进一步推广,用户数量的增加,已经面临着单台服务器不能负载的问题。 这次的优化由于时间关系主要分两步走,首先优化应用层代码以提高单台服务器的负载和吞吐率。之后再进行分表,引入队列、MemCached等分布式应用。 项…

技术图文:Python魔法方法之属性访问详解
背景 今天在B站学习“零基础入门学习 Python”中的第45节“魔法方法:属性访问”,这也是我们组织的 Python基础刻意练习活动 的学习任务,其中有这样的一个题目。 练习要求: 写一个矩形类,默认有宽和高两个属性。如果…

chmod权限设置
drwxr-xr-x. 7 root root 4096 Sep 26 20:16 sysconfig-rw-r--r--. 1 root root 1150 Aug 31 18:46 sysctl.conflrwxrwxrwx. 1 root root 14 Aug 31 17:21 system-release -> centos-release例如:-rw-r--r--第一个代表文件类型:-普通文件:…

【Python培训基础】一篇文件教你py文件打包成exe
场景: 如果要将我们编写好的代码给别人使用,如果要他们直接使用我们的代码,就需要安装各种编译软件以及第三方模块,还要对软件操作,编程有一定的了解,这对使用者的要求比较高,不是很方便,为了解决这一问题,我们可以选择将我们编写的代码,编译成一个可执行文件,这样,就可以实现跨…

刻意练习:Python基础 -- Task06. 字典与集合
背景 我们准备利用17天时间,将 “Python基础的刻意练习” 分为如下任务: Task01:变量、运算符与数据类型(1day)Task02:条件与循环(1day)Task03:列表与元组(…

WCF - Session 剖析
WCF中的Session WCF是MS基于SOA建立的一套在分布式环境中各个相对独立系统进行通信的构架,实现了最新的基于WS-*规范。按照SOA的原则,相对独自的业务逻辑以service的形式封装,调用者通过Messaging的方式调用Service。对于承载着某个业务功能的…

mui 微信支付 与springMVC服务器交互
昨天搞定了微信支付,没有想象中的难,主要是官方的demo不全好多东西要自己琢磨,mui端的就不写了支付宝的有了一模一样.上java端的首先是jar包 一个是用来解析xml文件 一个是用来解析json 当然可以替代 然后是工具类当然并不是全都用的到. public class ConfigUtil { /** * 服务…

Python零基础自学会有哪些弊端
Python在人工智能领域的发展前景非常好,很多人都想要学习Python技术,有一些小伙伴会选择通过自学来学习,但是如果是零基础,自学的话一定要注意这些弊端,下面就为大家详细的介绍一下Python零基础自学会有哪些弊端? Pyt…

技术图文:如何利用 Turtle 绘制一棵漂亮的樱花树
背景 最近看到很多机构在推动“青少年编程能力等级标准”的制定以及相关考试的测评,看样子今年年底这个事情就能够确定,明天上半年在一些大中城市就会全面铺开。 《青少年编程能力等级》标准发布,年底前将在部分地区落地青少年编程能力等级…

Python 是否是下一个 PHP?为什么?
前几天和一个看好 Python 的 Rails 开发者聊天,他看好 Python 的原因就是 PHP 统治今天的网络应用开发。而 Python 很像下一个 PHP 。 『下一个 PHP』如何定义?是指流行程度么?如果是的话,我觉得 Python 不会像 PHP 那样流行。根本…

正确使用STL-MAP中Erase函数
一切尽在代码中。 #include <iostream> #include <map> #include <string> using namespace std ;int main(void) { map<int, string> m ;m.insert(pair<int, string>(1, "abc")) ;m.insert(pair<int, string>(2, "def&qu…

学完UI设计可以从事哪些工作
最近有很多同学都会问到一个问题,就是学完UI设计可以从事哪些工作?对于正在学习UI设计的同学和已经学完UI设计的同学们,可以来看看下面文章的详细介绍就知道了。 学完UI设计可以从事哪些工作? 一、交互设计师。 学习UI设计之后就可以做交互设计师了&am…

刻意练习:Python基础 -- Task08. 异常处理
背景 我们准备利用17天时间,将 “Python基础的刻意练习” 分为如下任务: Task01:变量、运算符与数据类型(1day)Task02:条件与循环(1day)Task03:列表与元组(…

Winform 控件自适应 JSP 入门登录案例
明儿在放,先睡转载于:https://www.cnblogs.com/javabin/archive/2011/09/26/2192402.html

MyEclipse对Struts2配置文件较检异常 Invalid result location value/parameter
有时在编写struts.xml时会报错,但是找不出有什么她方有问题。也能正常运行 MyEclipse有地方去struts的xml进行了验证,经查找把这里 的build去掉就可以了 本文转自lpxxn博客园博客,原文链接:http://www.cnblogs.com/li-peng/p/3791…

学Python有哪些优势
Python在人工智能领域应用是比较广泛的,近几年,越来越多的人对Python技术比较感兴趣,想要学习,那么具体学Python有哪些优势呢?我们来看看下面的详细介绍就知道了。 学Python有哪些优势? 1.Python很受欢迎 流行程度似乎不是衡量价…

MongoDB 正则表达式
阅读目录 示例不区分大小写数组使用正则表达式正则中包含变量回到顶部示例 MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。 > db.col.find() { "_id" : ObjectId("56c6bbef64799370c0ef358a"), "x" : "hello world", &…

刻意练习:Python基础 -- Task09. else 与 with 语句
背景 我们准备利用17天时间,将 “Python基础的刻意练习” 分为如下任务: Task01:变量、运算符与数据类型(1day)Task02:条件与循环(1day)Task03:列表与元组(…

Java学习必不可少的网站,快收藏起来
java技术在IT互联网行业的发展前景一直在提升,越来越多的人都在学习java技术,今天小编来给大家提供一些学习Java的网站集合,希望能够帮助到正在学习java技术的同学。 Java学习必不可少的网站,快收藏起来! 1. Stackoverflow Stacko…

刻意练习:Python基础 -- Task11. 魔法方法
背景 我们准备利用17天时间,将 “Python基础的刻意练习” 分为如下任务: Task01:变量、运算符与数据类型(1day)Task02:条件与循环(1day)Task03:列表与元组(…

Oracle中的MERGE语句
转自http://blog.chinaunix.net/space.php?uid16981447&doblog&cuid430716做了简单的格式整理,加入了一点点原创的东西。Oracle9i引入了MERGE命令,你能够在一个SQL语句中对一个表同时执行inserts和updates操作. MERGE命令从一个或多个数据源中选择行来upda…

C#从数据库导出数据[excel]
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using MySql.Data.MySqlClient;using Microsoft.Office.Interop.Excel;using Excel Microsoft.Office.Interop.Excel; //使用命名空间别名using System.Reflection; …

UI设计培训中的扁平化理念
本文是为正在学习UI设计的同学们整理的一份资料,主要讲的是UI设计培训中的扁平化理念,扁平化的设计是抛弃一切装饰的设计,扁平化设计使得用户操作起来更加简洁、高效和舒适。简洁大方的交互界面设计自然能够引导用户,并且在短时间…
刻意练习:Python基础 -- Task12. 模块
背景 我们准备利用17天时间,将 “Python基础的刻意练习” 分为如下任务: Task01:变量、运算符与数据类型(1day)Task02:条件与循环(1day)Task03:列表与元组(…

Linux JSP连接MySQL数据库
Linux(Ubuntu平台)JSP通过JDBC连接MySQL数据库,与Windows平台类似,步骤如下: 下载 jdbc: mysql-connector-java-5.1.18.tar.gz 解压 jdbc: tar -zxvf mysql-connector-java-5.1.18.tar.gz 配置 …

h5 getUserMedia error PermissionDeniedError
HTML5 在使用非 localhost 地址访问时打开摄像头失败 。报getUserMedia error PermissionDeniedError,火狐下是可以正常调取的。 需要https: 火狐: 转载于:https://www.cnblogs.com/cosyer/p/7646672.html

女生零基础学软件测试难不难
软件测试属于一门IT技术编程语言,很多人都觉得IT技术都是男性比较多,按照目前的行业数据来看,确实是男性居多,但最近几年,女性程序猿也越来越多,其中就有软件测试这个岗位,下面具体来看看女生零…