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

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

off999 2024-11-25 15:52 26 浏览 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

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

相关推荐

无线usb网卡插上去没有反应(为什么usb无线网卡插上去没反应)

当出现电脑无法识别无线网卡的情况时,是简单的方法就是将无线USB网卡插到电脑后置USB接口上,以保证供电的充足。当然如果是偶然出现无法识别的情况,建议重启一下电脑试试。启用USB无线网卡驱动:右击“计...

怎么登录自己家的路由器(怎么登录自己家的路由器账号)

登陆家里的路由器方法:1、先查看ip,方法:win+r---输入:cmd---在再黑白界面输入:ipconfig,按回车。2、根据网关查看路由器地址。若网关是:192.168.2.1,那么路由器的ip...

linux操作系统安装步骤(linux系统详细安装步骤)

1.选择“中文(简体)”,然后点击“安装Ubuntu”。2.点击“继续”。3.然后点击“现在安装”。4.选择地址的时区,然后点击“继续”。5.选择“汉语”,然后点击“继续”。6.输入用户的名字。7.设...

苹果手机怎么设置定时关机(苹果手机怎么设置定时关机重启)

苹果手机可以设置定时关机,但无法设置定时开机。具体操作步骤如下:进入苹果手机自带的时钟。点击屏幕有下角的计时器。点击画面中间的计时结束启用选项。选择画面最下方的“停止播放”。之后再点击画面右上角的设定...

无线网wifi密码忘记了怎么办

忘记wifi密码后,可以在路由器后台查看。1.在浏览器的地址栏中,输入路由器上的管理地址,进入后台界面;2.在后台界面里,找到“无线设置”选项,点击它;3.在新界面里,点击wifi密码右侧的小眼睛图标...

win7系统无法正常开机怎么办
win7系统无法正常开机怎么办

解决方法如下1,出现无法启动的原因,要注意是开机启动不了,还是在进度条那里缓冲,过不去.如果是开机启动不了,那就要看一下内存条、电源等有没有问题?如果是在进度条那里,那就看下方的三种方法。2,第一种方法:1,开机按F8键.2,选择最近一次的...

2025-11-16 07:51 off999

现在装win7还需要激活吗(现在安装win7旗舰版还需密钥吗)

要激活  Windows7如果是预装在计算机中的,买来之后便不用激活,这里预装指的是在厂商那里。正版的Windows7安装到计算机中,有三十天的试用期,若要永久使用,就要使...

2025显卡性能排行榜天梯图(2020年显卡性能天梯图)

MacBookPro的显卡水平处于笔记本独立显卡Nvidia920M和940M之间。属于低端显卡级,玩玩LOL啥的还可以,其他的大型游戏就算了,MAC不适合打游戏。MacBookPro搭载的8代...

网络对时服务器(对时服务器端口)

对等网是指在网络中所有计算机的地位都是平等的,既是服务器也是客户机,所有计算机中安装的都是相同的单机操作系统如Windows98/XP/Vista/7等,它可以设置共享资源,但受连接数限制,一般是只允...

如何强制删除u盘文件(强制删除u盘内容)

1、电脑上下载安装安全杀毒类软件。2、使用强力卸载。3、找到U盘上需要卸载的文件,右击强力卸载可以卸载顽固型文件。4、被暂用的文件也删除不了可以退出U盘重启电脑重新开机插入U盘进行删除。5、不能删除的...

directx官方下载win7(directx download)

点开始-----运行,输入dxdiag,回车后打开“DirectX诊断工具”窗口,进入“显示”选项卡,看一下是否启用了加速,没有的话,单击下面的“DirectX功能”项中的“启用”按钮,这样便打开了D...

u盘视频无法播放怎么办(u盘上视频没办法播放)

解决办法:1.检查U盘存储格式是否为FAT32,如果不是,请将其格式化为FAT32; 2.检查U盘中视频文件是否损坏,如果有损坏文件,请尝试重新复制一份; 3.检查U盘中存储...

笔记本电脑无法正常启动怎么修复
笔记本电脑无法正常启动怎么修复

1.可以解决。2.Windows未能启动可能是由于系统文件损坏、硬件故障或病毒感染等原因引起的。解决方法可以尝试使用Windows安全模式启动、修复启动、还原系统、重装系统等方法。3.如果以上方法都无法解决问题,可以考虑联系专业的电脑...

2025-11-16 04:03 off999

联想设置u盘为第一启动项(联想怎么设置u盘启动为第一启动项)

联想电脑设置u盘为第一启动项方法如下一、将电脑开机,开机瞬间按F2键进入bios设置界面二、在上面5个选项里找到boot选项,这里按键盘上左右键来移动三、这里利用键盘上下键选到USB选项,然后按F5/...

家用路由器哪个牌子最好信号最稳定
家用路由器哪个牌子最好信号最稳定

TP-LINK最好,信号最稳定。路由器是连接两个或多个网络的硬件设备,在网络间起网关的作用,是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。它能够理解不同的协议,例如某个局域网使用的以太网协议,因特网使用的TCP/IP协议...

2025-11-16 03:03 off999

取消回复欢迎 发表评论: