热门标签 | 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


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
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社区 版权所有