Python——私有变量和slots类

私有变量

在Python中,并没有传统意义上的私有变量,像其他一些面向对象编程语言(如Java或C++)那样通过访问修饰符(如private)来严格控制对成员变量的访问。但是,Python提供了一种约定俗成的方式来模拟私有变量的行为,即使用单个下划线(_)作为变量名或方法名的前缀。

约定俗成的私有变量

当你在Python类的属性名前加上单个下划线(_)时,这被视为一个约定,表明该属性是“受保护的”或“私有的”,旨在表明这个属性或方法主要是为了类的内部使用,而不是在类的外部直接访问的。然而,这仅仅是一个命名约定,它并不会阻止外部代码访问这些属性。

示例

class MyClass:  
    def __init__(self):  
        self._private_var = "这是一个私有变量"  
  
    def get_private_var(self):  
        return self._private_var  
  
    def set_private_var(self, value):  
        self._private_var = value  
  
# 创建类的实例  
obj = MyClass()  
  
# 尝试直接访问私有变量(虽然技术上可行,但违反了约定)  
# print(obj._private_var)  # 这将打印出私有变量的值,但不建议这样做  
  
# 使用提供的方法来访问和修改私有变量  
print(obj.get_private_var())  # 正确的访问方式  
obj.set_private_var("新的值")  
print(obj.get_private_var())  # 显示修改后的值

真正的私有变量

虽然Python没有直接提供像private关键字这样的机制来创建真正的私有变量,但它提供了双下划线(__)前缀的命名方式来创建“魔法”属性或方法,这些属性或方法会被Python的名称改写机制(name mangling)自动修改名称,使得它们几乎不可能从类的外部直接访问。

示例(使用双下划线)

class MyClass:  
    def __init__(self):  
        self.__private_var = "这是一个真正的私有变量"  
  
    def get_private_var(self):  
        return self.__private_var  
  
# 创建类的实例  
obj = MyClass()  
  
# 尝试直接访问双下划线开头的“私有”变量(这会失败)  
# print(obj.__private_var)  # AttributeError: 'MyClass' object has no attribute '__private_var'  
  
# 使用提供的方法来访问私有变量  
print(obj.get_private_var())  # 正确的访问方式

在上面的例子中,尝试直接访问__private_var会导致AttributeError,因为Python已经将其名称改写成了包含类名前缀的形式(例如_MyClass__private_var,这使得从类的外部直接访问变得非常困难。然而,这种机制主要用于防止子类意外地重写父类中的方法或属性,而不是为了限制对成员变量的访问。在Python中,最佳实践仍然是通过公共方法(getter和setter)来访问和修改类的内部状态。

事实上Python也正是通过修改变量的名称使其不能按照原本名称进行访问,从而达到私有变量的作用,而这种改名称也是有规律的,Python会把原名称修改为:_类名__变量名。同理私有方法也是一样的,只是把变量换成了函数名。 

slots类:时间换空间

在Python中,__slots__ 是一个类变量,用于为实例指定一个固定集合的属性名称。当你定义了一个类并为其指定了 __slots__,这个类的实例将只能有 __slots__ 中定义的属性。使用 __slots__ 可以带来几个好处,包括减少实例的内存占用和提高属性访问速度,但它也会限制类的灵活性,因为你不能动态地添加新的属性。

如何使用 __slots__

__slots__ 应该是一个字符串的迭代对象(如列表、元组或字符串,但通常使用元组或列表),其中包含了你希望实例拥有的属性名。

class MyClass:  
    __slots__ = ('name', 'age')  
  
    def __init__(self, name, age):  
        self.name = name  
        self.age = age  
  
# 使用  
obj = MyClass('Alice', 30)  
print(obj.name)  # 输出: Alice  
print(obj.age)   # 输出: 30  
  
# 尝试添加不在 __slots__ 中的属性  
# obj.city = 'New York'  # 这会抛出 AttributeError

如果你尝试为 MyClass 的实例添加一个不在 __slots__ 中的属性,Python 将抛出一个 AttributeError

__slots__ 的好处

  1. 节省内存:通常,Python中的每个实例都有一个字典来存储其属性。使用 __slots__ 可以避免这个字典的使用,从而节省内存。

  2. 速度提升:属性访问通常涉及字典查找,这相对较慢。使用 __slots__ 可以使属性访问更快,因为属性是通过固定偏移量直接访问的。

  3. 避免动态属性:在某些情况下,限制实例能够拥有的属性集合可能是有益的,以防止意外地添加或修改不期望的属性。

注意事项

  • 继承自使用 __slots__ 的类的子类也必须定义自己的 __slots__,除非子类使用 __slots__ = () 来明确表示它不使用额外的空间来存储属性(即它只继承父类的属性)。
  • 使用 __slots__ 的类不能使用 __dict__ 或 __weakref__ 作为槽名,除非它们显式地出现在 __slots__ 列表中。
  • 如果类定义了 __slots__,则不能动态地添加新的属性,这可能会限制类的灵活性。

__slots__ 是Python中一个强大的特性,但使用时需要谨慎考虑其对类设计和性能的影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hardStudy_h

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值