python装饰器的使用以及私有化

@classmethod

  • 功能:用于定义类方法。类方法的第一个参数是类本身(通常约定为cls),而不是类的实例。这使得类方法可以在不创建类实例的情况下被调用,并且可以访问和修改类级别的属性和方法。
  • 使用场景示例
class MyClass:count = 0@classmethoddef increment_count(cls):cls.count += 1MyClass.increment_count()
print(MyClass.count)  

@staticmethod

  • 功能:用于定义静态方法。静态方法不需要传递类或实例的引用作为第一个参数,它与普通函数类似,但在逻辑上属于类的范围。静态方法主要用于将与类相关的实用函数组织在类的命名空间中,提高代码的可读性和可维护性,同时避免在全局命名空间中定义过多的函数。
  • 使用场景示例
class MathUtils:@staticmethoddef add(x, y):return x + yresult = MathUtils.add(3, 5)
print(result)  

@property

  • 功能:将一个方法转换为一个属性,使得可以像访问属性一样调用该方法,而不需要显式地使用括号。这提供了一种更简洁、自然的方式来访问类的属性,同时可以在获取属性值或设置属性值时添加额外的逻辑,例如数据验证、计算属性值等。
  • 使用场景示例
class Circle:def __init__(self, radius):self._radius = radius@propertydef radius(self):return self._radius@radius.setterdef radius(self, value):if value >= 0:self._radius = valueelse:raise ValueError("Radius cannot be negative.")c = Circle(5)
print(c.radius)  
c.radius = 7
print(c.radius)  

@abstractmethod(在abc模块中)

  • 功能:用于定义抽象方法,抽象方法是在抽象类中声明但不实现的方法,要求子类必须实现这些方法。这有助于实现抽象类和接口的概念,强制子类遵循特定的接口规范,提供统一的接口定义,增强代码的规范性和可扩展性。
  • 使用场景示例
from abc import ABC, abstractmethodclass Shape(ABC):@abstractmethoddef area(self):passclass Rectangle(Shape):def __init__(self, width, height):self.width = widthself.height = heightdef area(self):return self.width * self.heightr = Rectangle(3, 4)
print(r.area())  

@property

@property装饰器在Python中有以下重要作用:

1. 将方法转换为属性

  • 允许你像访问普通属性一样调用方法,而无需显式地使用括号。这使得代码在语法上更加简洁和自然,提高了代码的可读性。例如,对于一个表示矩形的类,你可以使用@property装饰器来定义获取矩形面积的方法,使其看起来像一个普通属性:
class Rectangle:def __init__(self, width, height):self.width = widthself.height = height@propertydef area(self):return self.width * self.heightr = Rectangle(3, 4)
print(r.area)  # 直接访问area属性,而不是r.area()

2. 提供属性访问控制

  • 隐藏实现细节:可以隐藏属性的实际存储方式和计算过程,只对外暴露属性的访问接口。例如,上述Rectangle类中,area属性的计算逻辑被封装在方法内部,外部使用者不需要了解其计算细节,只关心获取面积的值。
  • 增加额外逻辑:在获取属性值时,可以添加额外的逻辑处理,比如数据验证、日志记录、动态计算等。例如,在一个表示温度的类中,可以使用@property装饰器来获取摄氏温度,并在内部将其转换为华氏温度:
class Temperature:def __init__(self, celsius):self._celsius = celsius@propertydef celsius(self):return self._celsius@propertydef fahrenheit(self):return (self._celsius * 9/5) + 32t = Temperature(25)
print(t.celsius)  
print(t.fahrenheit)  

3. 支持属性的读写控制

  • 只读属性:通过只定义@property装饰的获取方法(getter),可以创建只读属性,防止外部对属性进行意外修改。例如,在一个表示学生信息的类中,学生的学号通常是只读的:
class Student:def __init__(self, student_id, name):self._student_id = student_idself.name = name@propertydef student_id(self):return self._student_ids = Student(12345, "Alice")
print(s.student_id)
# s.student_id = 67890  # 这将引发错误,因为student_id是只读属性
  • 读写属性:除了获取方法,还可以通过定义属性的设置方法(setter)来实现读写属性。在设置属性值时,可以进行数据验证等操作,确保属性值的合法性。例如,在上述Rectangle类中,如果要允许修改矩形的宽度和高度,可以添加相应的设置方法:
class Rectangle:def __init__(self, width, height):self._width = widthself._height = height@propertydef width(self):return self._width@width.setterdef width(self, value):if value > 0:self._width = valueelse:raise ValueError("Width must be positive.")@propertydef height(self):return self._height@height.setterdef height(self, value):if value > 0:self._height = valueelse:raise ValueError("Height must be positive.")r = Rectangle(3, 4)
print(r.width, r.height)
r.width = 5
r.height = 6
print(r.width, r.height)

4. 与其他特性协同工作

  • 可以与继承、描述符等Python特性结合使用,进一步扩展其功能。例如,在子类中可以重写父类中使用@property装饰的属性,以满足子类的特定需求;或者可以自定义描述符类,与@property一起实现更复杂的属性访问控制逻辑。

5. 提高代码的可维护性和可扩展性

  • 当需要修改属性的实现方式或添加额外功能时,只需要在@property装饰的方法内部进行修改,而不需要在所有使用该属性的地方进行更改。这符合面向对象编程的开闭原则(对扩展开放,对修改封闭),使得代码更容易维护和扩展。例如,如果要在上述Temperature类中更改摄氏温度和华氏温度之间的转换公式,只需要修改fahrenheit属性的获取方法即可,而不会影响到其他使用该类的代码。

python私有化在底层是如何完成的

在Python中,私有化是通过一种命名约定来实现的,而不是真正意义上的访问限制机制(如其他一些编程语言中的private关键字)。其底层原理主要涉及以下方面:

1. 名称改写(Name Mangling)

  • 当在类中定义一个以双下划线__开头(但不以双下划线结尾)的属性或方法时,Python解释器会自动将其名称进行改写。改写规则是在名称前面加上_类名,例如,对于类MyClass中的属性__private_attr,其实际存储的名称会变为_MyClass__private_attr
  • 这种名称改写的目的是为了避免在子类或外部代码中意外访问到这些被视为“私有”的成员。虽然从技术上讲,仍然可以通过改写后的名称访问这些成员,但这并不是一种推荐的做法,并且这种间接访问方式可以提醒开发者该成员被设计为内部使用。

2. 访问控制的实现方式

  • 类内部访问:在类的方法中,可以直接使用原始的私有属性名称(如__private_attr)来访问和修改这些属性,因为Python解释器在类定义时已经知道了这种命名约定,并且在编译字节码时会进行相应的处理,使得类内部的代码能够正确地访问这些“私有”成员。
  • 子类访问:在子类中,如果试图使用原始的私有属性名称(如__private_attr)来访问父类的私有属性,将会引发AttributeError异常,因为子类并不知道父类中私有属性的实际名称(已经被改写)。然而,如果子类知道名称改写的规则,也可以通过_父类名__private_attr的方式来访问父类的私有属性,但这违背了封装的原则,不应该被常规使用。
  • 外部访问:从类的外部直接访问以双下划线开头的私有属性(如obj.__private_attr,其中obj是类的实例)同样会引发AttributeError异常,因为外部代码不知道名称已经被改写。

3. 为什么使用这种方式

  • 灵活性与简洁性:Python的设计哲学强调简洁性和灵活性,这种基于命名约定的私有化方式相对简单,避免了引入复杂的访问控制语法(如在一些其他编程语言中)。同时,它给予开发者一定的自由度,在需要时可以绕过这种“伪私有”限制(虽然不鼓励这样做),例如在进行调试或在某些特定的框架内部实现中可能会需要这种灵活性。
  • 历史和文化原因:Python在发展过程中形成了这种约定俗成的编程规范,社区广泛接受并遵循这种方式来表示私有成员。这有助于提高代码的可读性,使其他开发者能够快速理解代码中哪些成员是被设计为内部使用的,从而遵循良好的编程习惯,减少不必要的外部依赖和干扰。

4. 示例

class MyClass:def __init__(self):self.__private_attr = 42def get_private_attr(self):return self.__private_attrdef set_private_attr(self, value):self.__private_attr = valueobj = MyClass()
print(obj.get_private_attr())  
# 以下访问方式将引发AttributeError异常
# print(obj.__private_attr)  
# obj.__private_attr = 100  
obj.set_private_attr(100)
print(obj.get_private_attr())  

在上述代码中,__private_attr被视为私有属性,通过类内部的get_private_attrset_private_attr方法来间接访问和修改。从外部直接访问__private_attr会失败,体现了Python中私有化的基本行为。需要注意的是,虽然Python的私有化机制不是绝对严格的访问限制,但在正常的编程实践中,应该尊重这种约定,以确保代码的良好结构和可维护性。

私有化的赋值和取值

在Python中,私有化属性(通过双下划线开头命名约定实现)的赋值和取值通常通过定义公有的方法来间接进行,这遵循了面向对象编程中的封装原则,以控制对内部数据的访问。以下是具体的方式:

1. 取值(访问私有属性)

  • 定义getter方法:在类中定义一个以@property装饰器修饰的方法,用于获取私有属性的值。这个方法的名称通常与私有属性的名称相关,以便在代码中直观地表示其功能。例如,如果有一个私有属性__private_value,可以定义如下的getter方法:
class MyClass:def __init__(self):self.__private_value = 42@propertydef private_value(self):return self.__private_value
  • 使用getter方法:在类的外部或其他方法中,可以像访问普通属性一样使用这个getter方法来获取私有属性的值:
obj = MyClass()
print(obj.private_value)  

2. 赋值(修改私有属性)

  • 定义setter方法:除了getter方法,还需要定义一个setter方法来允许对私有属性进行赋值操作。setter方法的名称与getter方法的名称相同,但需要使用@属性名.setter装饰器进行修饰(这里的属性名是getter方法的名称去掉@property装饰器后的部分)。在setter方法中,可以添加一些逻辑来验证或处理赋值操作,例如数据类型检查、范围限制等。例如,对于上述的__private_value属性,可以定义如下的setter方法:
class MyClass:def __init__(self):self.__private_value = 42@propertydef private_value(self):return self.__private_value@private_value.setterdef private_value(self, value):if isinstance(value, int):self.__private_value = valueelse:print("Error: 只能赋值整数类型。")
  • 使用setter方法:在类的外部或其他方法中,可以通过赋值语句来调用setter方法,从而修改私有属性的值:
obj = MyClass()
obj.private_value = 100  
print(obj.private_value)  
obj.private_value = "hello"  

以下是使用@property装饰器实现私有化属性的getset方法的详细步骤及示例:

1. 定义类和私有属性

首先,定义一个类,并在类中定义一个私有属性(以双下划线__开头)。例如:

class MyClass:def __init__(self):self.__private_value = 0

2. 定义getter方法(使用@property装饰器)

使用@property装饰器定义一个方法,用于获取私有属性的值。方法的名称通常与私有属性的名称相关,但去掉双下划线前缀。在这个方法中,直接返回私有属性的值。例如:

class MyClass:def __init__(self):self.__private_value = 0@propertydef private_value(self):return self.__private_value

3. 定义setter方法(使用@属性名.setter装饰器)

定义一个与getter方法同名的方法,但在方法上使用@属性名.setter装饰器(这里的属性名是getter方法的名称去掉@property装饰器后的部分,即private_value)。在setter方法中,接收一个参数用于赋值,并可以添加一些逻辑来验证或处理赋值操作,然后将值赋给私有属性。例如:

class MyClass:def __init__(self):self.__private_value = 0@propertydef private_value(self):return self.__private_value@private_value.setterdef private_value(self, value):if isinstance(value, int):self.__private_value = valueelse:print("Error: 只能赋值整数类型。")

如何使用@property实现对于私有化的get和set

使用示例

现在可以创建类的实例,并通过属性的方式来获取和修改私有属性的值,就像访问普通属性一样:

obj = MyClass()
print(obj.private_value)  # 调用getter方法获取私有属性的值obj.private_value = 10  # 调用setter方法修改私有属性的值
print(obj.private_value)obj.private_value = "hello"  # 尝试赋值非整数类型,会触发setter方法中的验证逻辑

总结

  • 通过@property装饰器和其对应的setter装饰器,可以为私有属性提供一种安全、可控的访问方式。这种方式遵循了数据封装的原则,隐藏了属性的内部实现细节,同时允许在获取和设置属性值时添加额外的逻辑,如数据验证、计算等。
  • 当属性的访问和修改需要更复杂的逻辑处理,或者需要对外部访问进行限制和控制时,使用@property实现getset方法是一种非常有效的方式,有助于提高代码的健壮性和可维护性。

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

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

相关文章

Matlab: 生成对抗网络,使用Datastore结构输入mat格式数据

使用matlab的生成对抗网络(Generative Adversarial Network,GAN)以及条件CGAN时,案例中 的生成器的输入为图像,改为.mat格式输入遇到的问题。解决方法 官方资源 训练条件生成对抗网络 (CGAN)- MATLAB & Simulink-…

Linux kernel 堆溢出利用方法(二)

前言 本文我们通过我们的老朋友heap_bof来讲解Linux kernel中off-by-null的利用手法。在通过讲解另一道相对来说比较困难的kernel off-by-null docker escape来深入了解这种漏洞的利用手法。(没了解过docker逃逸的朋友也可以看懂,毕竟有了root权限后&a…

设计模式:工厂方法模式和策略模式

工厂方法模式 什么是开闭原则? 开闭原则是扩展开发,对修改关闭 简单工厂(不是设计模式而是一种编程的习惯) 有三个角色 抽象产品:定义了产品的规范,描述了产品的特性和功能.具体产品:实现或者继承抽象产品的子类具体工厂:提供了创建产品的方法,调用者通过该方法获取产品 实…

深度学习代码笔记

一、U-NET 论文题目:U-Net: Convolutional Networks for Biomedical Image SegmentationUNet 的体系结构基于编码器-解码器范式,其中编码器从输入图像中提取特征,解码器基于这些特征生成分割图。但是,UNet还集成了编码器和解码器…

软件测试面试2024最新热点问题

大厂面试热点问题 1、测试人员需要何时参加需求分析? 如果条件循序 原则上来说 是越早介入需求分析越好 因为测试人员对需求理解越深刻 对测试工作的开展越有利 可以尽早的确定测试思路 减少与开发人员的交互 减少对需求理解上的偏差 2、软件测试与调试的关系 测…

L10.【LeetCode笔记】回文链表

目录 1.题目 2.自解 代码 提交结果 1.题目 给你一个单链表的头节点 head ,请你判断该链表是否为 回文链表 。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:tru…

Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)

作者:来自 Elastic Benjamin Trent Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)。 嵌入模型输出 float32 向量,通常对于高效处理和实际应用来说太大。Elasticsearch 支持 int8 标量量化,以减小向量大小,同时保持性能。其他…

猿创征文|Inscode桌面IDE:打造高效开发新体验

猿创征文|Inscode桌面IDE:打造高效开发新体验 引言 在当今快速发展的软件开发领域,一个高效、易用的集成开发环境(IDE)是每个开发者必不可少的工具。Inscode 桌面 IDE 作为一款新兴的开发工具,凭借其强大…

【VBA实战】用Excel制作排序算法动画续

为什么会产生用excel来制作排序算法动画的念头,参见【VBA实战】用Excel制作排序算法动画一文。这篇文章贴出我所制作的所有排序算法动画效果和源码,供大家参考。 冒泡排序: 插入排序: 选择排序: 快速排序:…

IPguard与Ping32全面对比——选择最适合企业的数据安全解决方案

在如今数据安全威胁日益加剧的时代,企业必须高度重视保护敏感数据与信息。因此,选择一款合适的数据安全软件,尤其是防泄密和信息保护软件,显得尤为重要。在市场上,有两款备受企业青睐的数据安全解决方案——IPguard和P…

《情商》提升:增强自我意识,学会与情绪共处

在当今社会,情商(Emotional Intelligence,EQ)的重要性越来越受到人们的关注。情商是指个体运用情绪、情感、认知和行为反应的能力,来理解、管理、表达和处理情感的一种综合素养。情商的高低对于个人的成长、人际关系、…

k8s集群安装(kubeadm)

k8s集群安装(kubeadm) 1、环境准备(master和node节点都执行)1.1、替换yum源1.2、关闭selinux1.3、永久关闭防火墙1.4、永久关闭swap1.5、修改主机名添加host1.6、时间同步1.7、将桥接的IPv4流量传递到iptables的链1.8、docker安装…

使用Matlab建立随机森林

综述 除了神经网络模型以外,树模型及基于树的集成学习模型是较为常用的效果较好的预测模型。我们以下构建一个随机森林模型。 随机森林是一种集成学习方法,通过构建多个决策树并结合其预测结果来提高模型的准确性和稳定性。在MATLAB中,可以…

Wireshark

目录 解题思路 题目设计原理 总结 解题思路 首先下载文件,用 wireshark 打开一头雾水。 但是看看题目的提示,说管理员的密码就是 flag 的内容,我们可以知道,关键词估计是密码,passwd、password、pwd之类的。 所以我…

FreeRTOS学习13——任务相关API函数

任务相关API函数 任务相关API函数任务相关API函数介绍任务相关 API 函数详解函数 uxTaskPriorityGet()函数 vTaskPrioritySet()函数 uxTaskGetSystemState()函数 vTaskGetInfo()函数 xTaskGetApplicationTaskTag()函数 xTaskGetCurrentHandle()函数 xTaskGetHandle()函数 xTask…

使用kalibr_calibration标定相机(realsense)和imu(h7min)

vslam-evaluation/VINS/Installation documentation/4.IMU和相机联合标定kalibr_calibration.md at master DroidAITech/vslam-evaluation GitHub 目录 1.kalibr安装 1.1安装依赖项 1.2创建工作空间 1.3下载kalibr并编译 1.4设置环境变量 2.准备标定板 3.配置驱动和打…

[Docker#8] 容器配置 | Mysql | Redis | C++ | 资源控制 | 命令对比

目录 一:Mysql 容器化安装 二:Redis 容器化安装 Redis 简介 Redis 容器创建 三:C容器制作 四:容器资源更新 常见问题 一:Mysql 容器化安装 进入 mysql 的镜像网站,查找 mysql 的镜像 mysql docker…

1小时构建Vue3知识体系之vue的生命周期函数

本文转载自:https://fangcaicoding.cn/course/12/63 大家好!我是方才,目前是8人后端研发团队的负责人,拥有6年后端经验&3年团队管理经验。 系统学习践行者!近期在系统化输出前端入门相关技术文章,期望能…

数据结构-集合

一.集合的表示 一个重要的操作是查某个元素属于哪个集合,另一个操作是合并操作 从这个树的节点去找树根也就是从下往上找,要把树并起来只需把两个根并在一起就可以了 不存在已知一个节点去找孩子节点,根重要的是已知一个节点找它的父亲节点,与之前的二…

unity基础,点乘叉乘。

简单记录下点乘叉乘&#xff0c;要不每次用完就忘&#xff0c;忘了又查。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class TestCrossDot : MonoBehaviour {/// <summary>/// 原点/// </summary>public Transform t…