百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Python中怎么给属性增加类型检查或合法性验证?

off999 2024-11-25 15:52 32 浏览 0 评论

问题

你想给某个实例 attribute 增加除访问与修改之外的其他处理逻辑,比如类型检查或合法性验证。

解决方案

自定义某个属性的一种简单方法是将它定义为一个 property。例如,下面的代码定义了一个 property,增加对一个属性简单的类型检查:

class Person:
    def __init__(self, first_name):
        self.first_name = first_name

    # Getter function
    @property
    def first_name(self):
        return self._first_name

    # Setter function
    @first_name.setter
    def first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value

    # Deleter function (optional)
    @first_name.deleter
    def first_name(self):
        raise AttributeError("Can't delete attribute")

上述代码中有三个相关联的方法,这三个方法的名字都必须一样。第一个方法是一个getter 函数,它使得first_name成为一个属性。其他两个方法给 first_name属性添加了setter和 deleter函数。需要强调的是只有在 first_name 属性被创建后,后面的两个装饰器@first_name.setter和@first_name.deleter才能被定义。 property 的一个关键特征是它看上去跟普通的 attribute没什么两样,但是访问它的时候会自动触发getter 、setter 和 deleter 方法。例如:

>>> a = Person('Guido')
>>> a.first_name # Calls the getter
'Guido'
>>> a.first_name = 42 # Calls the setter
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "prop.py", line 14, 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>
AttributeError: can`t delete attribute
>>>

在实现一个property的时候,底层数据 (如果有的话) 仍然需要存储在某个地方。 因此,在get 和 set 方法中,你会看到对_first_name属性的操作,这也是实际数据保存的地方。另外,你可能还会问为什么__init__()方法中设置了self.first_name 而不是self._first_name。在这个例子中,我们创建一个 property 的目的就是在设置 attribute 的时候进行检查。因此,你可能想在初始化的时候也进行这种类型检查。通过设置 self.first_name,自动调用setter方法,这个方法里面会进行参数的检查,否则就是直接访问 self._first_name 了。 还能在已存在的 get 和 set 方法基础上定义 property。例如:

class Person:
    def __init__(self, first_name):
        self.set_first_name(first_name)

    # Getter function
    def get_first_name(self):
        return self._first_name

    # Setter function
    def set_first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value
 
    # Deleter function (optional)
    def del_first_name(self):
        raise AttributeError("Can't delete attribute")

    # Make a property from existing get/set methods
    name = property(get_first_name, set_first_name, del_first_name)

讨论

一个 property 属性其实就是一系列相关绑定方法的集合。如果你去查看拥有 property 的类,就会发现 property 本身的 fget、fset 和 fdel 属性就是类里面的普通方法。比如:

>>> Person.first_name.fget
<function Person.first_name at 0x1006a60e0>
>>> Person.first_name.fset
<function Person.first_name at 0x1006a6170>
>>> Person.first_name.fdel
<function Person.first_name at 0x1006a62e0>
>>>

通常来讲,你不会直接去调用 fget 或者 fset,它们会在访问 property 的时候自动被触发。 只有当你确实需要对 attribute 执行其他额外的操作的时候才应该使用到 property。 有时候一些从其他编程语言 (比如 Java) 过来的程序员总认为所有访问都应该通过 getter 和 setter,所以他们认为代码应该像下面这样写:

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):
        self._first_name = value

不要写这种没有做任何其他额外操作的 property。首先,它会让你的代码变得很臃肿,并且还会迷惑阅读者。其次,它还会让你的程序运行起来变慢很多。最后,这样的设计并没有带来任何的好处。特别是当你以后想给普通 attribute 访问添加额外的处理逻辑的时候,你可以将它变成一个property而无需改变原来的代码。因为访问 attribute 的代码还是保持原样。Properties 还是一种定义动态计算attribute 的方法。这种类型的 attributes 并不会被实际的存储,而是在需要的时候计算出来。比如:

import math
class Circle:
    def __init__(self, radius):
        self.radius = radius
 
    @property
    def area(self):
        return math.pi * self.radius ** 2
 
    @property
    def diameter(self):
        return self.radius * 2
  
    @property
    def perimeter(self):
        return 2 * math.pi * self.radius

在这里,我们通过使用 properties,将所有的访问接口形式统一起来,对半径、直径、周长和面积的访问都是通过属性访问,就跟访问简单的 attribute 是一样的。如果不这样做的话,那么就要在代码中混合使用简单属性访问和方法调用。下面是使用的实例:

>>> c = Circle(4.0)
>>> c.radius
4.0
>>> c.area # Notice lack of ()
50.26548245743669
>>> c.perimeter # Notice lack of ()
25.132741228718345
>>>

尽管 properties 可以实现优雅的编程接口,但有些时候你还是会想直接使用 getter 和 setter 函数。例如:

>>> p = Person('Guido')
>>> p.get_first_name()
'Guido'
>>> p.set_first_name('Larry')
>>>

这种情况的出现通常是因为 Python 代码被集成到一个大型基础平台架构或程序中。例如,有可能是一个 Python 类准备加入到一个基于远程过程调用的大型分布式系统中。这种情况下,直接使用 get/set 方法 (普通方法调用) 而不是 property 或许会更容易兼容。 最后一点,不要像下面这样写有大量重复代码的 property 定义:

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_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

    # Repeated property code, but for a different name (bad!)
    @property
    def last_name(self):
        return self._last_name

    @last_name.setter
    def last_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._last_name = value

重复代码会导致臃肿、易出错和丑陋的程序。好消息是,通过使用装饰器或闭包,有很多种更好的方法来完成同样的事情。

相关推荐

wifi万能密码破解器(wifi万能密码破解版)

万能钥匙主要的作用是分享与被分享的关系,你所用万能钥匙一件查询和破解的都是别人分享的密码,不是万能钥匙破解的作用,真正能破解的只是那些密码简单的,比如12345678或者豹子数比如88888888和1...

win8的稳定性(win8稳定还是win10稳定)

如果是玩游戏Win7相对win7稳定一些,能兼容大部分的游戏。其它的应该各有千秋,具体上可以从如下几点了解:1、Win8相对Win7开机更快,内存管理更高效,HTML5支持更好,兼容暂时落后。2、Wi...

怎么切任务管理器(任务管理)

任务管理器切换方法如下1.先按WIN+X,再按T,即可呼出任务管理器2.同时按Ctrl+Shift+Esc,即可呼出任务管理器。3.同时按Ctrl+Alt+Del,在跳转的界面里...

windows激活无法连接到组织网络

1、在桌面新建一个文本文档,把代码复制进去2、点击文件选择“另存为”,在弹出的界面中,将保存位置选择在桌面,保存类型改为所有文件,文件名改为.bat格式的文件,然后点击“保存”按钮; 3、右...

企业邮箱注册申请流程(企业邮箱怎么注册申请)
企业邮箱注册申请流程(企业邮箱怎么注册申请)

点击进入官网,进入邮箱后,点击下方的企业邮箱,开通邮箱有两个版本,一个是免费版,一个是专业版,这边点击免费版的立即开通,弹出的界面,输入账号、密码以及手机号码,输入验证码。扩展知识:企业邮箱特点1、便于管理企业可以自行设定管理员来分配和管理...

2026-01-14 13:43 off999

window截图快捷键(windows自带截屏的方法)
window截图快捷键(windows自带截屏的方法)

1、按Prtsc键截图这样获取的是整个电脑屏幕的内容,按Prtsc键后,可以直接打开画图工具,接粘贴使用。也可以粘贴在QQ聊天框或者Word文档中,之后再选择保存即可。2、按Ctrl+Prtsc键截图截屏获得的内容也是整个电脑屏幕,与上面的...

2026-01-14 13:15 off999

win10一定要创建账户吗(win10需要创建microsoft账户吗)

win10系统安装不需要申请微软账号。如果是在安装win10的过程中,则使用本地账户登录,从安装主要步骤完成之后进入后续设置阶段开始,步骤如下:1、首先就是要输入产品密钥,或者点击左下角“以后再说”。...

win10显示已禁用输入法(w10系统已禁用输入法)

在使用win10的过程中,有时候利用第三方软件过度优化开机启动项目就容易导致win10无法打开输入法问题,这个情况是由于ctfmon程序无法正常启动所致,一般表现在电脑桌面右下角显示已禁用ime的提示...

windows pad(windowspad官方网站入口)

平板电脑安装windows方法如下1、首先,下载并安装U启动PE制作工具,这里要特别注意的是,要下载装机版的。2、点开PE制作工具的主界面,插入U盘,等待U盘被制作工具识别出来后。3、点击归还空间,然...

为什么电脑一开机就死机(为什么电脑一开机就死机重启)

一、软件问题:  1、导致死机的一个重要原因就是病毒程序的入侵。大家都知道,病毒程序是一种会破坏计算机软件系统,并占用极大的系统资源的一种恶意攻击程序,它会给计算机本身的软件造成很大的伤害。死机时的首...

0x0000007a蓝屏解救方法win7

0x0000007A说明是内存或虚拟内存(硬盘)的问题,你可以按顺序尝试如下操作:1、更改虚拟内存页面文件位置:我的电脑→右键→属性→高级→性能设置→高级→虚拟内存更改→取消原来选择的驱动器(默认在C...

系统小说排行榜完本经典之作

超级兑换系统超级修仙超级客栈系统貌似高手在异界重生之修仙系统超级修仙系统异界之兑换成圣(贱圣VS奸神)+超级兑换(火山飞狐)+穿越之无敌兑换(开心小帅)+兑换器修仙(轻舞流芒)+...

手机能修复u盘吗(手机修复u盘工具下载)

1.在手机上可以恢复u盘,当手机SD卡或U盘插入电脑中时,如果提示“文件或目录损坏且无法读取”的信息时,我们首先需要对手机SD卡或U盘进行目录修复操作。插入待修复的U盘,打开“我的电脑”,找到Sd卡...

怎么查电脑显卡的信息(电脑怎么查看显卡信息)

要查看电脑的显卡信息,可以按照以下步骤进行操作:1.使用快捷键Win+R打开“运行”对话框。2.在运行对话框中输入“dxdiag”并点击“确定”按钮,打开“DirectX诊断工具”。3....

电脑上找不到输入法怎么办(电脑中找不到输入法)

如果电脑上不显示输入法,您可以尝试以下解决方法:1.检查输入法设置:首先,您可以检查电脑的输入法设置。在Windows系统中,您可以点击任务栏右下角的输入法图标(一般为字母或语言标志),然后选择“显...

取消回复欢迎 发表评论: