热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PyTips0x14Python描述符

项目地址:https:git.iopytips本篇主要关于三个常用内置方法:property(),staticmethod()࿰

项目地址:https://git.io/pytips

本篇主要关于三个常用内置方法:property(),staticmethod(),classmethod()

在 Python 语言的设计中,通常的语法操作最终都会转化为方法调用,例如:

a = 1
b = 2
print("a + b = {}".format(a+b))# 相当于
print("a.__add__(b) = {}".format(a.__add__(b)))

a + b = 3
a.__add__(b) = 3

Python 中的描述符(Descriptor)就是将对象属性的获取、赋值以及删除等行为转换为方法调用的协议:

descr.__get__(self, obj, type=None) --> valuedescr.__set__(self, obj, value) --> Nonedescr.__delete__(self, obj) --> None

例如我们要获取一个对象的属性,可以通过o.x的方式取得:

class Int:ctype = "Class::Int"def __init__(self, val):self._val = vala = Int(1)
print(a.ctype)

Class::Int

而通过.的方式寻找属性的值实际上调用了object.__getattribute__(self, name)方法:

class Int:ctype = "Class::Int"def __init__(self, val):self._val = valdef __getattribute__(self, name):print("? doesn't want to give `{}' to you!".format(name))return "?"
a = Int(2)
print(a.ctype)

? doesn't want to give `ctype' to you!
?

而这里的__getattribute__(self, name)方法实际上就是将.的属性获取方法转化为描述符协议定义的descr.__get__(self, key):

class Str:def __init__(self, val):self._val = valdef __get__(self, name, ctype=None):print("You can __get__ anything from here!")return self._val
class Int:ctype = Str("Class::Int")def __init__(self, val):self._val = valdef __getattribute__(self, name):return type(self).__dict__[name].__get__(None, type(self))
a = Int(2)
print(a.ctype)

You can __get__ anything from here!
Class::Int

这里的 a.ctype = (Int.__dict__['ctype']).__get__(None, Int),即通过描述符的方式获取了 ctype 属性的值。同样的道理,你也可以通过 descr.__set__(self, obj, val) 设置属性的值:

class Str:def __init__(self, val):self._val = valdef __get__(self, name, ctype=None):print("You can __get__ anything from here!")return self._valdef __set__(self, name, val):print("You can __set__ anything to me!")self._val = val
class Int:ctype = Str("Class::Int")def __init__(self, val):self._val = val
a = Int(3)
print(a.ctype)
a.ctype = "Class::Float"
print(a.ctype)

You can __get__ anything from here!
Class::Int
You can __set__ anything to me!
You can __get__ anything from here!
Class::Float

将这些取值、赋值的操作转换为方法调用让我们有办法在做这些操作的过程中插入一些小动作,这么好用的东西自然是已加入豪华内置函数阵容,正是我们常见的

  • property()

  • classmethod()

  • staticmethod()

property

property(fget=None, fset=None, fdel=None, doc=None) 方法简化了上面的操作:

class Int:def __init__(self, val):self._val = valself._ctype = Nonedef get_ctype(self):print("INFO: You can get `ctype`")return self._ctypedef set_ctype(self, val):print("INFO: You're setting `ctype` =", val)self._ctype=valctype = property(fget=get_ctype, fset=set_ctype, doc="Property `ctype`")a = Int(4)
print(a.ctype)
a.ctype = "Class::Int"
print(a.ctype)

INFO: You can get `ctype`
None
INFO: You're setting `ctype` = Class::Int
INFO: You can get `ctype`
Class::Int

显然,更方便一些的用法是将 property 当做修饰器:

class Int:_ctype = Nonedef __init__(self, val):self._val = val@propertydef ctype(self):print("INFO: You can get `ctype` from me!")return self._ctype@ctype.setterdef ctype(self, val):print("INFO: You're setting `ctype` =", val)self._ctype = val
a = Int(5)
print(a.ctype)
a.ctype = "Class::Int"
print(a.ctype)

INFO: You can get `ctype` from me!
None
INFO: You're setting `ctype` = Class::Int
INFO: You can get `ctype` from me!
Class::Int

staticmethod & classmethod

顾名思义,property 是关于属性的全部操作,如果是要获取类中的方法,则需要用到 staticmethodclassmethod。顾名思义,staticmethod 将方法变成静态方法,即类和实例都可以访问,如果不用 staticmethod 我们可以用下面这种别扭的方法实现:

class Int:def __init__(self, val):self._val = valdef _get_ctype(self=None):print("INFO: You can get `ctype` from here!")return "Class::Int"@staticmethoddef get_ctype():print("INFO: You can get `ctype` from here!")return "Class::StaticInt" a = Int(6)
print(a._get_ctype())
print(Int._get_ctype())print(a.get_ctype())
print(Int.get_ctype())

INFO: You can get `ctype` from here!
Class::Int
INFO: You can get `ctype` from here!
Class::Int
INFO: You can get `ctype` from here!
Class::StaticInt
INFO: You can get `ctype` from here!
Class::StaticInt

可以看到,静态方法与类和实例无关,也就不再(不能)需要 self 关键词;与之相反,当我们需要在方法中保留类(而非实例)的引用时,则需要用 classmethod:

class Int:_ctype = ""def __init__(self, val):self._val = val@classmethoddef set_ctype(klass, t):klass._ctype = treturn "{}.ctype = {}".format(klass.__name__, t)
a = Int(7)
print(a.set_ctype("Class::Int"))
print(Int.set_ctype("Class::Float"))
b = Int(8)
print(b._ctype)

Int.ctype = Class::Int
Int.ctype = Class::Float
Class::Float

总结

Python 的描述符给出一种通过方法调用来实现属性(方法)获取、赋值等操作的规则,通过这一规则可以方便我们深入程序内部并实施操控,因此 property/staticmethod/classmethod 在 Python 是通过底层(如 CPython 中的 C)实现的,如果想要进一步深入了解其实现原理,可以访问参考链接的教程,其中包括了这三个内置方法的 Python 实现版本,我也把它们 copy 过来方便查看。


欢迎关注公众号 PyHub 每日推送

欢迎关注公众号 PyHub!

参考

  1. Descriptor HowTo Guide

class Property(object):"Emulate PyProperty_Type() in Objects/descrobject.c"def __init__(self, fget=None, fset=None, fdel=None, doc=None):self.fget = fgetself.fset = fsetself.fdel = fdelif doc is None and fget is not None:doc = fget.__doc__self.__doc__ = docdef __get__(self, obj, objtype=None):if obj is None:return selfif 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__)class StaticMethod(object):"Emulate PyStaticMethod_Type() in Objects/funcobject.c"def __init__(self, f):self.f = fdef __get__(self, obj, objtype=None):return self.fclass ClassMethod(object):"Emulate PyClassMethod_Type() in Objects/funcobject.c"def __init__(self, f):self.f = fdef __get__(self, obj, klass=None):if klass is None:klass = type(obj)def newfunc(*args):return self.f(klass, *args)return newfunc


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
author-avatar
华浦即热式电热水龙头_376
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有