简介
Python的property属性从表面上来看是一个比较简单的特性,实际上它的实现和一些在工程上的应用里和结合了descriptor等东西。我们这里从一个简单的属性赋值和访问开始一步步的推导。 同时,这里也和一些对应于java里的用法做了一个比较。通过这些比较我们可以看到一些python的典型用法能够带来一定的灵活性。
初始代码
有的时候,我们写一些python的类里,需要定义一些属性,比如如下的代码:
class Person: def __init__(self, first_name): self.first_name = first_name
这里的代码再简单不过了,就是设置一个对象里first_name属性。
仅仅是以上的这么一个简单的代码,我们可以在如下的代码里来使用Person:
>>> person = Person("firstname") >>> person.first_name 'firstname' >>> person.first_name = "another name" >>> person.first_name 'another name'
因为在python里,所有的属性默认都是public的,所以这里就相当于java里将属性设置成public一样的效果。我们也可以得到一个类似的java类:
public class Person { public String firstName; public Person(String firstName) { this.firstName = firstName; } }
我们从一般的常识里会有这么一种感觉,就是代码这样写有点不妥。因为将对象的属性都暴露在外面了。而且容易被多个地方修改导致错误。这是一个方面的问题,另外一方面,我们希望代码更加defensive,可能会在里面加入很多限制和检查,比如说传入的对象不要为空了。甚至在有的时候我们需要对传入的属性做一些其他的限制,比如邮件地址的格式,传入数据的长度或者 数字的范围等等。
这个时候,如果只是一个暴露出来的这么个属性确实不合适了。
property和属性封装
在python里,如果我们要封装一个属性,那么我们会考虑使用property。假设在前面的代码里,我们需要在设置属性的时候检查它的类型,然后对于它的删除操作不支持,我们可以使用如下的代码:
class Person: def __init__(self, first_name): self.first_name = first_name @property def first_name(self): return self._first_name @first_name.setter def first_name(self, value): if not isinstance(value, str): raise TypeError('Expected a string') self._first_name = value @first_name.deleter def first_name(self): raise AttributeError("Can't delete attribute")
前面的这部分代码,我们定义了一个first_name的属性。这样以后我们每次访问它们的时候,可以通过person.first_name的方式来访问,和前面的使用方法是一样的。唯一不同的就是我们在实现里增加了类型检查。这里的实现也比较有意思,我们需要考虑的几个属性就是读,写和删除。这几个属性都用同样的方法名,唯一不同的就是对属性的读我们是在first_name方法上增加了@property修饰,而写是@first_name.setter,删除则是@first_name.deleter。这些就是python里设置property的套路。有了这些设置,我们使用一些方式来访问属性的时候会产生如下的结果:
>>> a = Person("first_name") >>> a.first_name 'first_name' >>> a.first_name = "new name" >>> a.first_name = 43 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/frank/programcode/python/person.py", line 12, in first_name raise TypeError('Expected a string') TypeError: Expected a string >>> del a.first_name Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/frank/programcode/python/person.py", line 17, in first_name raise AttributeError("Can't delete attribute") AttributeError: Can't delete attribute
这里正好对应我们定义的各种行为,因为a.first_name对应了设置property的方法,而那里我们设置了参数类型的检查,所以会有这么个异常。而del a.first_name对应我们删除属性的方法,所以才会出现AttributeError。
当然,我们并不是一定要定义这所有的方法,有时候如果我们只是需要这个property只读的,设置那个@property读方法就可以了。
针对这个问题,我们虽然修改了python类里面的代码,但是从使用者的角度来说,基本上没有变化。完全看不出来我们是使用了它的property还是我们最开始设置的public attribute。而对于java代码来说呢?这个时候不可避免的,我们就需要设置属性访问方法了,我们要在方法里进行参数检查。那么一个大致对应的代码实现如下:
public class Person { private String firstName; public getFirstName() { return firstName; } public void setFirstName(String firstName) { if(firstName == null) throw new IllegalArgumentException(); this.firstName = firstName; } public Person(String firstName) { this.firstName = firstName; } }
而这里因为有了参数检查,所以我们使用它们的代码如果原来是直接访问属性的则需要修改为getFirstName和setFirstName了。这也是为什么java里推荐使用get, set方法来访问属性。因为有了这些方法我们可以更加方便的去检查属性的合法性。
减少重复
前面关于property的使用确实比较合理。可是当我们有多个属性的时候呢?比如说,我们类里有first_name, last_name等等几个同样类型的属性。如果我们需要访问他们的话,都采用同样的property来做吗?
从前面的代码里已经看到,光定义一个property就要扯上3个方法,如果我们有3, 4个这样的属性要设置...其实就算用property还是满无聊的。那么有没有办法来达到这方面的代码重用呢?我们这里需要使用若干个同样的参数,而且对它们的访问以及参数检查都是一样的,每个property里都这么去检查显得太傻。
在python这里,还有一个办法可以解决,那就是descriptor类。python里descriptor类是做什么的呢?通常来说,当我们访问一个对象里的属性时,可以通过它来做一些定制化的工作。它是怎么来定制的呢?我们先来看对应的定制代码实现:
class String: def __init__(self, name): self.name = name def __get__(self, instance, cls): if instance is None: return self else: return instance.__dict__[self.name] def __set__(self, instance, value): if not isinstance(value, str): raise TypeError('Expect a string') if len(value) < 4: raise AttributeError('Invalid length') instance.__dict__[self.name] = value def __del__(self, instance): del instance.__dict__[self.name]
上面实现的代码类似于一个类,它定义了__get__, __set__, __del__这几个方法。从官方的文档定义来说,任何一个对象只要实现其中的任何一个方法,它就可以称为descriptor。这里对__get__, __set__, __del__里定义的方法似乎有点难以理解,我们一个个的讨论过来。在python里,如果我们访问一个对象的属性,其默认的访问方式其实就是对这个对象的字典进行get, set和delete操作。比如说我们访问一个对象的属性a.x,在其内部的实现是通过去查看对象a的字典a.__dict__['x'],如果这里找不到对应的属性,则查找type(a).__dict__['x'],这里type(a)相当于a对象的类,如果这里也找不到的话则去查找type(a)的父类。
所以有了前面这么个过程,我们就可以看到在get里,如果instance为空的话,我们就返回。这里是因为这个方法尝试返回instance,也就是对象实例或者类所对应的__dict__元素。__set__方法里除了做了一个类型检查以外,我还额外的增加了一个对参数长度的检查。
使用它们的代码如下:
相关推荐
综上所述,全国青少年软件编程等级考试Python标准解读(1-6级)的知识点涵盖了从基础环境的熟悉、Python基本语法规则的掌握、逻辑和计算思维的培养,到具体的编程技能和计算思维的应用。该考试标准旨在提高全国青...
Python 用Python实现文件对比分析并生成报告 Python源码Python 用Python实现文件对比分析并生成报告 Python源码Python 用Python实现文件对比分析并生成报告 Python源码Python 用Python实现文件对比分析并生成报告 ...
1.将目标文件与模板文件进行样式对比(docx) 2.统计段落、形状、图、表并与其相应的标题进行样式与数量的对比 3.将文件以样式为key进行序列化,...引用模块python-docx 已实现 样式对比 样式相似度评分 导出所有批注
总结,`property`是Python中用于构建面向对象编程的一个关键特性,它使得我们能够在不改变接口的情况下,控制属性的访问行为,增加代码的灵活性和安全性。理解并合理运用`property`,能够提高代码的质量和可维护性。
Python 的 `property` 内置函数是面向对象编程中的一个重要特性,它允许开发者创建属性的访问器、修改器和删除器,从而实现对类中数据的控制和封装。`property` 不仅可以增强代码的可读性和安全性,还能提供额外的...
Python 在通用应用程序、自动化插件、网站、网络爬虫、数值分析、科学计算、云计算、大数据和网络编程等领域有着极为广泛的应用,像 OpenStack 这样的云平台就是由 Python 实现的,许多平台即服务(PaaS)产品都支持...
"Python人脸相似度对比"这个主题涵盖了机器学习、深度学习以及图像处理等多个方面,使得我们能够利用预训练的模型来识别和比较人脸的相似性。 首先,人脸对比的核心在于人脸识别技术。在Python中,有许多库可以帮助...
基于PyQt5+Python实现Excel内容对比
本示例中,我们关注的是一个基于Python和Selenium库进行图片对比的操作。Selenium是一个强大的Web UI自动化测试工具,能够模拟用户行为,如点击按钮、填写表单等。在结合图片对比技术后,它可以帮助我们检测UI在不同...
Python OpenCV 图片相似度对比是使用 Python 语言和 OpenCV 库来实现的图片相似度对比算法。该算法可以比较两张图片的不同,并生成展现不同之处的图片。 图片相似度对比算法 图片相似度对比算法可以分为两步:首先...
在Python编程中,处理XML和Excel文件是常见的任务,尤其在数据处理和分析领域。XML(eXtensible Markup Language)是一种结构化数据格式,常用于存储和交换数据,而Excel则是Microsoft Office套件中的一个应用程序,...
Python 用networkx模块解读人物关系 Python源码Python 用networkx模块解读人物关系 Python源码Python 用networkx模块解读人物关系 Python源码Python 用networkx模块解读人物关系 Python源码Python 用networkx模块...
1、对比两个数据库的表结构差异 2、对比两个数据库的表字段差异 3、可全部导出或者选择其中一种比较方式
分别基于Transformer和CNN网络实现CIFAR-100数据集分类任务python源码(用于对比).zip分别基于Transformer和CNN网络实现CIFAR-100数据集分类任务python源码(用于对比).zip分别基于Transformer和CNN网络实现CIFAR-100...
Python-mysqldiff是一款专为数据库管理设计的轻量级工具,主要功能是对比不同版本的MySQL数据库,并生成相应的SQL脚本以实现数据库结构和数据的同步。它简化了数据库版本控制的过程,尤其适用于多版本数据库管理和...
2018版全国计算机等级考试二级Python科目考纲解读 基本要求 1. 掌握Python语言的基本语法规则。 2. 掌握不少于2个基本的Python标准库。 3. 掌握不少于2个 Python第三方库,掌握获取并安装第三方库的方法。 4...
### Python Property 属性的两种用法详解 #### 引言 在面向对象编程语言中,属性(Property)是一个非常实用的功能。它允许我们以一种更自然的方式管理对象的状态,通过封装来隐藏对象内部数据的实现细节。Python ...
Python中的`property`语法是一种强大的特性,用于封装和控制对象的属性访问。它允许开发者创建具有getter和setter方法的属性,使得在访问或修改属性值时可以进行额外的逻辑处理,而外部代码看起来仍然像直接操作属性...
总之,Python的办公自动化能力和文件对比功能在日常工作中有着广泛的应用。通过掌握相关的库和技巧,你可以轻松实现文件管理、数据处理的自动化,从而节省大量时间,提高工作质量。这个源码集锦是一个很好的学习资源...