描述符示例详解
代码
这里要创建一个描述符,根据要求(如隐藏敏感信息、正确地设置日期的格式)对属性的值进行变换,并返回修改后的版本: from dataclasses import dataclass from datetime import datetime from functools import partial from typing import Callable class BaseFieldTransformation: def __init__(self, transformation: Callable[[], str]) -> None: print("初始化") self._name = None self.transformation = transformation def __get__(self, instance, owner): print("get执行") if instance is None: return self raw_value = instance.__dict__[self._name] return self.transformation(raw_value) def __set_name__(self, owner, name): print("set_name执行") self._name = name def __set__(self, instance, value): print("set执行") instance.__dict__[self._name] = value ShowOriginal = partial(BaseFieldTransformation, transformation=lambda x: x) HideField = partial( BaseFieldTransformation, transformation=lambda x: "**redacted**" ) FormatTime = partial( BaseFieldTransformation, transformation=lambda ft: ft.strftime("%Y-%m-%d %H:%M"), ) @dataclass class LoginEvent: username: str = ShowOriginal() password: str = HideField() ip: str = ShowOriginal() timestamp: datetime = FormatTime() def serialize(self) -> dict: return { "username": self.username, "password": self.password, "ip": self.ip, "timestamp": self.timestamp, } if __name__ == '__main__': le = LoginEvent("john", "secret password", "1.1.1.1", datetime.utcnow()) >>> le = LoginEvent("john", "secret password", "1.1.1.1", datetime. utcnow()) >>> vars(le) {'username': 'john', 'password': 'secret password', 'ip': '1.1.1.1', 'timestamp': ...} >>> le.serialize() {'username': 'john', 'password': '**redacted**', 'ip': '1.1.1.1', 'timestamp': '...'} >>> le.password '**redacted**' 这段代码使用了 Python 中的描述符(Descriptor)和数据类(Data Class)来实现一个 LoginEvent 类,该类用于表示用户的登录事件,并包含了一些敏感信息。其中,BaseFieldTransformation 类是一个基类,它定义了描述符的通用行为。LoginEvent 类继承了 dataclass 装饰器,这使得该类具有自动生成 init__、__repr 等方法的特性。 BaseFieldTransformation 描述符有三个方法: __init__: 在对象创建时被调用,用于初始化对象状态; __get__: 在属性被访问时被调用,用于获取属性的值; __set__: 在属性被赋值时被调用,用于设置属性的值; __set_name__: 在类中定义属性时被调用,用于设置描述符名称。 在该代码中,我们定义了三个具体的 BaseFieldTransformation 子类:ShowOriginal、HideField 和 FormatTime。这些子类通过 partial 函数将 transformation 参数预设为不同的函数,以便对应不同的字段转换方式。例如,ShowOriginal 字段保持原样,而 HideField 字段使用 "redacted" 来代替其真实值。 最后,我们使用 LoginEvent 类创建了一个数据记录。该数据记录用于存储用户的登录事件,包括用户名、密码、IP 地址和时间戳。其中,用户名和 IP 地址字段使用 ShowOriginal 描述符,这意味着它们不会被转换。密码字段使用 HideField 描述符,这意味着它将被替换为 "redacted"。时间戳字段使用 FormatTime 描述符,这意味着它将按照指定格式进行格式化。登录事件还包括一个 serialize 方法,用于将该事件序列化为 dict 格式的数据。
示例
class CelsiusWithDescriptor: def __init__(self, temperature=0): print("CelsiusWithDescriptor初始化") self._temperature = temperature # print(self._temperature) # 0 def __get__(self, instance, owner): print("Getting value...") return self._temperature def __set__(self, instance, value): if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value.......................") print(value) # 25 self._temperature = value class TemperatureWithDescriptor: def __init__(self, celsius): self.celsius = celsius # 描述符对应的值25 def __str__(self): return f"{self.celsius} degrees Celsius is {self.fahrenheit:.2f} degrees Fahrenheit" @property def fahrenheit(self): return self.celsius * 1.8 + 32 class TemperatureDataModel: temperature = CelsiusWithDescriptor() def __init__(self, temperature): self.temperature = temperature # print(self.temperature == TemperatureDataModel.temperature) # True # print(self.temperature) # 25 def __str__(self): """打印对象时会执行此函数""" return str(TemperatureWithDescriptor(self.temperature)) if __name__ == '__main__': """代码分析TemperatureDataModel(25) 实例化 1.类的执行顺序,会先执行类中定义的变量属性等 2.会先执行CelsiusWithDescriptor()的初始化__init__方法,此时它的self._temperature为0 3.执行初始化TemperatureDataModel的__init__方法,此时temperature为25 4.其中的self.temperature描述符实际是调用CelsiusWithDescriptor()中的__set__方法,并将上面的temperature的值25当做value传递进去 5.经过上面的__set__方法后,CelsiusWithDescriptor对象的self._temperature为25. 6.打印tmp 7.会执行TemperatureDataModel类的__str__方法 8.此时的self.temperature为25,当做参数传递给TemperatureWithDescriptor类 9.会先执行当做参数传递给TemperatureWithDescriptor类的__init__方法 10.初始化实例属性self.celsius = 25 11.外面的str,会执行TemperatureWithDescriptor类中定义的__str__方法,所以最终返回25 degrees Celsius is 77.00 degrees Fahrenheit """ tmp = TemperatureDataModel(25) # print(tmp) tmp.temperature = 50 # 跟上面一样,执行__set__ # print(tmp.temperature) # 执行__get__ tmp1 = TemperatureDataModel(59) print(tmp1.temperature) # 59 print(tmp.temperature) # 59 如上代码有问题:可以看出不同的对象,却打印了同一个值。原因是CelsiusWithDescriptor类中get和set魔法方法中它自己存储数据, 而不是将数据存储到每个对象中去所以才会出现上面的问题。需要修改CelsiusWithDescriptor类中的代码 class CelsiusWithDescriptor: def __init__(self, temperature=0): self.name = None self._temperature = temperature # print(self._temperature) # 0 def __set_name__(self, owner, name): self.name = name def __get__(self, instance, owner): print("Getting value...") return instance.__dict__[self.name] # 必须要使用__dict__ def __set__(self, instance, value): if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value.......................") instance.__dict__[self.name] = value # 必须要使用__dict__ tmp = TemperatureDataModel(25) # print(tmp) tmp.temperature = 50 # 跟上面一样,执行__set__ # print(tmp.temperature) # 执行__get__ tmp1 = TemperatureDataModel(59) print(tmp1.temperature) # 59 print(tmp.temperature) # 50
__EOF__

本文作者:404 Not Found
本文链接:https://www.cnblogs.com/weiweivip666/p/17299269.html
关于博主:可能又在睡觉
版权声明:转载请注明出处
声援博主:如果看到我睡觉请喊我去学习
本文链接:https://www.cnblogs.com/weiweivip666/p/17299269.html
关于博主:可能又在睡觉
版权声明:转载请注明出处
声援博主:如果看到我睡觉请喊我去学习
-------------------------------------------
个性签名:代码过万,键盘敲烂!!!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人