python 动态属性和特性

文章目录

    • 1. 使用动态属性转换数据
    • 2. @property
      • 2.1 help() 文档
    • 3. 特性工厂函数
    • 4. 属性删除操作
    • 5. 处理属性的重要属性和函数
      • 5.1 处理属性的内置函数
      • 5.2 处理属性的特殊方法

learn from 《流畅的python》

1. 使用动态属性转换数据

  • 在 Python 中,数据的属性和处理数据的方法统称属性(attribute)。其实,方法只是可调用的属性
  • 我们还可以创建特性 (property)
from urllib.request import urlopen
import warnings
import os
import jsonURL = 'http://www.oreilly.com/pub/sc/osconfeed'
JSON = './osconfeed.json'def load():if not os.path.exists(JSON):msg = 'downloading {} to {}'.format(URL, JSON)warnings.warn(msg)  # 发出提醒with urlopen(URL) as remote, open(JSON, 'wb') as local:# 使用两个上下文管理器local.write(remote.read())# 读取和保存远程文件with open(JSON) as fp:return json.load(fp)feed = load()
print(feed)
print(sorted(feed['Schedule'].keys()))
for key, value in sorted(feed['Schedule'].items()):print('{:3} {}'.format(len(value), key))
print(feed['Schedule']['speakers'][-1]['serial'])
# 这种句法太长了。。。如何改进
from collections import abcclass FrozenJSON:# 一个只读接口,使用属性表示法访问JSON类对象def __init__(self, mapping):self.__data = dict(mapping)def __getattr__(self, name):if hasattr(self.__data, name):  # 有属性,获取return getattr(self.__data, name)# 调用 keys 等方法就是通过这种方式处理的else:  # 没有,构建 FrozenJSONreturn FrozenJSON.build(self.__data[name])@classmethod  # 备选构造方法,@classmethod 装饰器经常这么用def build(cls, obj):if isinstance(obj, abc.Mapping):return cls(obj)  # 构建 FrozenJSONelif isinstance(obj, abc.MutableSequence):# 是序列,对每个元素都进行 buildreturn [cls.build(item) for item in obj]else:return objraw_feed = load()
feed = FrozenJSON(raw_feed)
print(len(feed.Schedule.speakers))
print(sorted(feed.Schedule.keys()))  
# ['conferences', 'events', 'speakers', 'venues']
print(feed.Schedule.events[-1].name)  
# Why Schools Don't Use Open Source to Teach Programming
p = feed.Schedule.events[-1]
print(type(p))  
# <class '__main__.FrozenJSON'>
print(p.name)  
# Why Schools Don't Use Open Source to Teach Programming
print(p.speakers)  
# [157509]
print(p.age)  
# KeyError: 'age'
  • 处理无效属性名, 例如内置的关键字keyword.iskeyword
grad = FrozenJSON({'name': 'Jim Bo', 'class': 1982})
# print(grad.class) # invalid syntax
print(getattr(grad, 'class')) # 1982

修改类的构造函数:

def __init__(self, mapping):self.__data = {}for k,v in mapping.items():if keyword.iskeyword(k): # 如果是关键字,则增加下划线后缀k += "_"self.__data[k] = v
  • 无效的命名,str.isidentifier()
grad = FrozenJSON({'2name': 'Jim Bo', 'class': 1982})
print(grad.2name) # SyntaxError: invalid syntax

改名

def __init__(self, mapping):self.__data = {}for k,v in mapping.items():if keyword.iskeyword(k):k += "_"if not k.isidentifier(): # 不是合法的命名,改之k = "_" + kself.__data[k] = v
print(grad._2name) # Jim Bo
  • __init__ 方法其实是 “初始化方法”。真正的构造方法是 __new__(但是几乎不需要自己编写)
# 构建对象的伪代码 
def object_maker(the_class, some_arg): new_object = the_class.__new__(some_arg)  # new 方法也可以返回其它类对象if isinstance(new_object, the_class): the_class.__init__(new_object, some_arg) return new_object
class FrozenJSON:# 一个只读接口,使用属性表示法访问JSON类对象def __new__(cls, arg):  # 第一个参数是类本身if isinstance(arg, abc.Mapping):return super().__new__(cls)elif isinstance(arg, abc.MutableSequence):return [cls(item) for item in arg]else:return argdef __init__(self, mapping):self.__data = {}for k, v in mapping.items():if keyword.iskeyword(k):k += "_"if not k.isidentifier():k = "_" + kself.__data[k] = vdef __getattr__(self, name):if hasattr(self.__data, name):  # 有属性,获取return getattr(self.__data, name)# 调用 keys 等方法就是通过这种方式处理的else:  # 没有,构建 FrozenJSONreturn FrozenJSON(self.__data[name])

2. @property

https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208
请利用 @property 给一个Screen对象加上width和height属性,以及一个只读属性resolution

class Screen(object):def __init__(self):self._w = 0self._h = 0self._r = 786432@property # 将方法变成属性, 调用不要加()def width(self):return self._w@propertydef height(self):return self._h@width.setter # 可以设置属性值,没有该方法是只读属性def width(self, v):self._w = v@height.setterdef height(self, v):self._h = v@propertydef resolution(self):return self._r
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
if s.resolution == 786432:print('测试通过!')
else:print('测试失败!')
  • 特性都是类属性,但是特性管理的其实是实例属性的存取
class Class:data = "class data attr"@propertydef prop(self):return "prop value"obj = Class()
print(vars(obj)) # {}, vars 函数返回 obj 的 __dict__ 属性
print(obj.data) # class data attr
obj.data = "changed"
print(vars(obj)) # {'data': 'changed'}
print(Class.data) # class data attr
# 实例修改了data,但是 类属性没有被修改print(Class.prop) # <property object at 0x0000021A91E4A680>
print(obj.prop) # prop value
# obj.prop = "changed prop"  # 报错 can't set attribute
obj.__dict__["prop"] = "changed prop1"
print(vars(obj)) # {'data': 'changed', 'prop': 'changed prop1'}
print(obj.prop) # prop value #
# 读取 obj.prop 时仍会运行特性的读值方法。特性没被实例属性遮盖
Class.prop = "haha" # 覆盖 Class.prop 特性,销毁特性对象
print(obj.prop) # changed prop1
# 现在,obj.prop 获取的是实例属性。
# Class.prop 不是特性了,因此不会再覆盖 obj.prop。
print(obj.data) # changed
print(Class.data) # class data attr
Class.data = property(lambda self : "data prop value")
# 使用新特性覆盖 Class.data
print(obj.data) # data prop value
# obj.data 被 Class.data 特性遮盖了
del Class.data # 删除特性
print(obj.data) # changed
# 恢复原样,obj.data 获取的是实例属性 data

obj.attr 这样的表达式不会obj 开始寻找 attr,而是从 obj.__class__ 开始,而且,仅当类中没有名为 attr 的特性时,Python 才会obj 实例中寻找。

这条规则不仅适用于特性, 还适用于一整类描述符——覆盖型描述符(overriding descriptor)

2.1 help() 文档

使用装饰器创建 property 对象时,读值方法(有 @property 装饰器 的方法)的文档字符串作为一个整体,变成特性的文档

>>> class Foo:@propertydef bar(self):'''说明文档'''return self.__dict__['bar']@bar.setterdef bar(self, val):self.__dict__['bar'] = val>>> help(Foo)
Help on class Foo in module __main__:class Foo(builtins.object)|  Data descriptors defined here:|  |  __dict__|      dictionary for instance variables (if defined)|  |  __weakref__|      list of weak references to the object (if defined)|  |  bar|      说明文档>>> help(Foo.bar)
Help on property:说明文档
  • 经典写法,传入 doc 参数
weight = property(get_weight, set_weight, doc='weight in kilograms')

3. 特性工厂函数

为了减少编写 getter,setter,可以使用特性工厂函数

def quantity(storage_name):def qty_getter(instance):return instance.__dict__[storage_name]def qty_setter(instance, value):if value > 0:instance.__dict__[storage_name] = valueelse:raise ValueError("value must be > 0")return property(qty_getter, qty_setter)
class LineItem:weight = quantity('weight') # 使用特性工厂函数定义weight类属性price = quantity('price') # price 属性def __init__(self, description, weight, price):self.description = descriptionself.weight = weight # 激活属性,确保不为负数和0self.price = pricedef subtotal(self):return self.weight * self.price # 使用特性中存储的值line1 = LineItem("name1", 8, 13.5)
print(line1.weight, line1.price) # 8 13.5
print(sorted(vars(line1).items()))
# [('description', 'name1'), ('price', 13.5), ('weight', 8)]

weight 特性 覆盖了 weight 实例属性,因此对 self.weightobj.weight 的 每个引用都由特性函数处理,只有直接存取 __dict__ 属性才能跳过特性的处理逻辑

4. 属性删除操作

del 操作,删除属性很少见,但是 python 支持该操作

class BlackKnight:def __init__(self):self.members = ['an arm','another arm','a leg','another leg']self.phrases = ["'Tis but a scratch.","It's just a flesh wound.","I'm invincible!","All right, we'll call it a draw."]@propertydef member(self):print("next member is:")return self.members[0]@member.deleterdef member(self):text = 'BLACK KNIGHT (loses {})\n-- {}'print(text.format(self.members.pop(0), self.phrases.pop(0)))knight = BlackKnight()
print(knight.member)
# next member is:
# an arm
del knight.member
# BLACK KNIGHT (loses an arm)
# -- 'Tis but a scratch.
del knight.member
# BLACK KNIGHT (loses another arm)
# -- It's just a flesh wound.
del knight.member
# BLACK KNIGHT (loses a leg)
# -- I'm invincible!
del knight.member
# BLACK KNIGHT (loses another leg)
# -- All right, we'll call it a draw.
del knight.member
# IndexError: pop from empty list
  • 经典写法,fdel 参数设置删除函数
member = property(member_getter, fdel=member_deleter)
  • 如果不使用特性,还可以实现低层特殊的 __delattr__ 方法处理 删除属性 的操作

5. 处理属性的重要属性和函数

  • __class__ 对象所属类的引用(即 obj.__class__type(obj) 的作用相 同)
    Python 的某些特殊方法,例如 __getattr__,只在对象的类中寻找,而不在实例中寻找
  • __dict__ 一个映射,存储对象或类的可写属性。
    __dict__ 属性的对象, 任何时候都能随意设置新属性
    如果类有 __slots__ 属性,它的实例可能没有 __dict__ 属性
  • __slots__
    类可以定义这个这属性,限制实例能有哪些属性
    __slots__ 属性 的值是一个字符串组成的元组,指明允许有的属性
    如果 __slots__ 中没有 '__dict__',那么该类的实例没有 __dict__ 属性,实例只允许有指定名称的属性

5.1 处理属性的内置函数

  • dir([object])
    列出对象的大多数属性,dir 函数也不会列出类的几个特殊属性,例如 __mro__、__bases__ 和 __name__
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', 
'__getattribute__', '__gt__', '__hash__', '__init__','__init_subclass__', '__le__', '__lt__', '__module__','__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar']
  • getattr(object, name[, default])
    从 object 对象中获取 name 字符串对应的属性
    获取的属性可能来自 对象所属的类或超类
    如果没有指定的属性getattr 函数抛出 AttributeError 异常,或者返回 default 参数的值(如果设定了这个参数的话)
  • hasattr(object, name),调用上面的函数,看是否返回异样
  • setattr(object, name, value),可能会创建一个新属性,或者 覆盖现有的属性
  • vars([object]),返回 object 对象的 __dict__ 属性
    如果实例所属的类定义了 __slots__ 属性,实例没有 __dict__ 属性,那么 vars 函数不能处理 那个实例

5.2 处理属性的特殊方法

  • 使用点号或内置的 getattr、hasattr 和 setattr 函数存取属性都会 触发下述列表中相应的特殊方法

  • 但是直接通过实例的 __dict__ 属性读写属性不会触发这些特殊方法,通常会使用这种方式 跳过特殊方法

  • 特殊方法不会被同名实例属性 遮盖

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

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

相关文章

bash 不是内部或外部命令_Win10_cmd下提示:‘xxx’不是内部或外部命令,也不是可运行的程序 或批处理文件...

一、windows下命令行查看ip: win+R,调出【运行】,输入cmd,回车打开终端,输入ipconfig,查看成功: 二、可能出现的问题: ping 不是内部或外部命令,也不是可运行的程序或批处理文件。 ipconfig 不是内部或外部命令,也不是可运行的程序或批处理文件。 解决: 1、控制面板…

python 属性描述符

文章目录1. 描述符示例&#xff1a;验证属性2. 自动获取储存属性的名称3. 继承改进4. 覆盖型与非覆盖型描述符对比4.1 覆盖型描述符4.2 没有 __get__ 方法的覆盖型描述符4.3 非覆盖型描述符4.4 在类中覆盖描述符5. 描述符用法建议learn from 《流畅的python》 1. 描述符示例&a…

计算机声音与视频教程,电脑怎么录屏幕视频带声音 电脑录屏幕视频带声音教程...

电脑怎么录屏幕视频带声音&#xff0c;相信有许多的用户在工作上或者学习上也会有这种需求&#xff0c;自己对此也还是不太的了解&#xff0c;毕竟也是没有怎么使用过&#xff0c;对此也是想要知道这电脑怎么录屏幕视频带声音&#xff0c;其实也是有几个原因&#xff0c;下面就…

tensor转换为图片_为大家介绍图片转换pdf的经验总结!你找对方法了吗?

图片转换pdf怎么做&#xff1f;不少朋友们在学习和工作中都发现了PDF这种文件格式似乎格外的吃香&#xff0c;你收到的很多培训文件和在网上搜罗的一些学习文件&#xff0c;全部都是PDF格式&#xff0c;PDF格式这么吃香&#xff1f;甚至有人让你把图片都转成PDF格式&#xff0c…

LeetCode 2021 力扣杯全国秋季编程大赛(第384名)

文章目录1. 无人机方阵2. 心算挑战3. 黑白翻转棋4. 玩具套圈5. 十字路口的交通2021.9.11&#xff0c;周六比赛之前&#xff1a;早上去交大看看&#xff0c;本科毕业10年了&#xff0c;由于限流&#xff0c;校园里没有多少回校的校友。逛了逛&#xff0c;跟太太和的她的同学一起…

URAL 2047 Maths 打表 递推

MathsTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.action?cid87157#problem/B Description Android Vasya attends Maths classes. His group started to study the number theory recently. The teacher gave them severa…

java 画笔跟swing组件_Java学习教程(基础)--Java版本历史(二)

Java语言自JDK1.0版本以来经历了许多次更新&#xff0c;也在基本程序库中增加了大量的类别和包。从J2SE 1.4开始&#xff0c;Java语言的变动由 Java Community Process&#xff08;JCP&#xff09;管理&#xff0c;JCP使用Java规范请求&#xff08;Java Specification Requests…

计算机网络路由选择协议,IP路由选择协议原理和作用

IP路由选择协议原理和作用(2008-10-20 19:26:17)标签&#xff1a;杂谈IP路由选择如果目的主机与源主机直接相连或都在一个共享网络上,那就直接把包发送到目的主机,如果不是,那把ip数据报送到默认路由器,由它转发路由器使用路由表保存自己知道的网络的信息,它包括:目的IP地址,它…

LeetCode 2000. 反转单词前缀

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始的字符串 word 和一个字符 ch 。 找出 ch 第一次出现的下标 i &#xff0c;反转 word 中从下标 0 开始、直到下标 i 结束&#xff08;含下标 i &#xff09;的那段字符。 如果 word 中不存在字符 ch &#xff0c;则无需进…

oss图片跨域问题_图片存储解决方案-阿里云对象存储

开通对象存储OSS1. 打开阿里云官网&#xff0c;选择产品对象存储 OSS 2.开通对象存储OSS 需要支付宝扫码登录3.进入管理控制台 4.新建存储空间 5.跨域资源共享(CORS)的设置管理文件整合Springboot实现图片上传服务端签名直传并设置上传回调 : 在服务端完成签名&#xff0c;并且…

2015-8-10工作日志

1. 工作规划&#xff1a;完成系统请假管理的功能。 &#xff08;1&#xff09;根据系统需求完成请假管理model&#xff1b; &#xff08;2&#xff09;进行请假流程的deployment&#xff1b; &#xff08;3&#xff09;进行请假流程的流程定义管理&#xff1b; &#xff08;4&a…

会理天气预报软件测试,会理天气预报15天

发送给QQ好友★会理未来15天天气预报★会理天气预报7月22日 星期四:阵雨&#xff0c;18C~28C无持续风向&#xff0c;小于3级会理天气预报7月23日 星期五:阵雨&#xff0c;18C~28C无持续风向&#xff0c;小于3级会理天气预报7月24日 星期六:阵雨&#xff0c;18C~27C无持续风向&a…

LeetCode 2001. 可互换矩形的组数

文章目录1. 题目2. 解题1. 题目 用一个下标从 0 开始的二维整数数组 rectangles 来表示 n 个矩形&#xff0c;其中 rectangles[i] [widthi, heighti] 表示第 i 个矩形的宽度和高度。 如果两个矩形 i 和 j&#xff08;i < j&#xff09;的宽高比相同&#xff0c;则认为这两…

google 浏览器默认打开控制台_前端开发调试:浏览器console方法总结

今天突发奇想&#xff0c;准备总结下console的各个函数。以前都是只用一个console.log(),查了一下发现有好多&#xff0c;就记下来&#xff0c;方便以后查阅。速记consoleConsole对象提供浏览器控制台的接入&#xff0c;不同浏览器是不一样的&#xff0c;这里介绍普遍存在的Con…

计算机兴趣小组活动总结,兴趣小组活动总结优秀范文

总结&#xff0c;可以把零散的、肤浅的感性认识上升为系统、深刻的理性认识&#xff0c;从而得出科学的结论&#xff0c;以便发扬成绩、克服缺点、吸取经验教训&#xff0c;使今后的工作少走弯路、多出成果。下面是小编给大家精心挑选的工作总结&#xff0c;希望能帮助到大家!本…

江小白包装设计原型_雪碧和江小白的品牌跨界合作之旅可谓是一场品牌包装的视觉盛宴...

大家好&#xff0c;我是古小一&#xff0c;一个行走在酒水品牌包装设计不归路上的小编&#xff01;当下品牌间的跨界合作越来越多&#xff0c;消费者不但有审美疲劳的趋势&#xff0c;脑洞过大的跨界还容易引发群嘲。不过好在有热情网友的帮助&#xff0c;雪碧与江小白已经自然…

LeetCode 2002. 两个回文子序列长度的最大乘积(状态压缩+枚举状态子集+预处理)

文章目录1. 题目2. 解题2.1 超时2.2 预处理优化1. 题目 给你一个字符串 s &#xff0c;请你找到 s 中两个 不相交回文子序列 &#xff0c;使得它们长度的 乘积最大 。 两个子序列在原字符串中如果没有任何相同下标的字符&#xff0c;则它们是 不相交 的。 请你返回两个回文子…

2018年计算机CPU纳米制程,AMD发大招,推出7纳米制程CPU与GPU

原标题&#xff1a;AMD发大招&#xff0c;推出7纳米制程CPU与GPU处理器及图形芯片大厂AMD&#xff0c;7日凌晨在美国旧金山举办的技术大会上&#xff0c;正式公布了自家在CPU及GPU两部分的7纳米制程产品。其中&#xff0c;在GPU部分是Radeon Instinct MI60/MI50专业显示卡&…

[hdu5372 Segment Game]树状数组

题意&#xff1a;有两种操作&#xff1a;(1)插入线段&#xff0c;第i次插入的线段左边界为Li&#xff0c;长度为i (2)删除线段&#xff0c;删除第x次插入的线段。每次插入线段之前询问有多少条线段被它覆盖。 思路&#xff1a;由于插入的线段长度是递增的&#xff0c;所以第i次…

怎样从php转向java_Github标星10.8K!Java 实战博客项目分享

点击上方 Java后端&#xff0c;选择 设为星标优质文章&#xff0c;及时送达来源&#xff1a;开源最前线(ID&#xff1a;OpenSourceTop)作为程序员每天就是不停的敲代码&#xff0c;改Bug&#xff0c;写起代码来那真是行云如流水&#xff0c;但要你码出点文字&#xff0c;写点技…