Python 属性封装(Attribute Encapsulation)
封装是面向对象编程中的基本概念之一(与继承、多态和抽象并列)。封装将属性隐藏在类内部,就像胶囊一样,防止未经授权的直接访问。同时类会提供一些方法来访问和修改属性值,这为属性提供了一定程度的隐私保护——不能直接访问对象属性,但可以通过调用方法(代理)对属性执行操作。
文章目录
- 一、类变量命名约定
- 1.1 "私有"变量
- 二、通过@property对属性进行封装
- 2.1 定义属性访问
- 2.2 定义属性修改
- 2.3 定义属性删除
一、类变量命名约定
1.1 "私有"变量
在 Python 中,如果想要命名“私有变量”,通常有以下两种约定方式:
_variable
,前置单下划线,这是一种命名约定,表示“别随便用”,提示该变量为内部使用。__variable
,前置双下划线,Python 解释器会进行名称改写(Name Mangling),将其转换为_类名__variable
的形式,以减少被子类或外部意外修改的可能性。
要注意的是,这两种方式只是一种希望大家遵守的"约定",并不是真正的私有变量,即使是双下划线命名,仍然可以通过改写后的变量名进行访问。
下面定义一个类,包含2个"私有"变量:
class Attr:def __init__(self, var1, var2):self._var1 = var1self.__var2 = var2 # 变量名会被改写成_Attr__var2
实例化之后,通过_var1和改写后的a._attr__var2依然可以访问变量内容:
a = Attr(1,2)
a._var1
a._Attr__var2
二、通过@property对属性进行封装
如要要对属性进行更复杂的控制,Python提供了内置装饰器@property,它可以控制属性的访问,对外部调用者来说,可以像正常一样访问属性,但内部可以对属性增加校验、计算等逻辑。
2.1 定义属性访问
下面的类Acct,定义了一个"私有"变量__balance,并通过@property装饰器定义了一个方法来对它进行访问
class Acct:def __init__(self, balance):self.__balance = balance@propertydef balance(self):print('通过方法访问balance:')return self.__balance
正常访问balance属性,Python会自动调用@property修饰的balance方法(方法名作为属性名):
a = Acct(100)
a.balance
属性封装对调用者是透明的,调用者无法意识到他是直接访问了属性,还是在和控制属性访问的方法对话,这里我们故意加了一个print语句,告诉调用者通过方法访问了属性,实际应用中可以根据需要进行校验、日志记录等操作。
2.2 定义属性修改
@property修饰的是属性读取方法,这种方法称为getter,通过"@变量名.setter"装饰器可以指定属性的修改方法。
在上面代码基础上新增一个修改方法,并在修改方法中定义了一个校验逻辑:不能将balance修改为负值:
class Acct:def __init__(self, balance):self.__balance = balance@propertydef balance(self):print('通过方法访问balance:')return self.__balance@balance.setterdef balance(self, amt):if amt < 0:raise ValueError('余额不能设置为负')print('通过方法设置balance为:', amt)self.__balance = amt
实例化后,尝试将balance修改为正值和负值,观察效果:
a = Acct(100)
a.balance = 200 # 修改成功
a.balance = -200 # 修改失败,禁止修改为负值
这里可以看到校验逻辑生效了,成功阻止了将balance修改为负值,这种方式可以有效阻止在程序中输入非法值。
2.3 定义属性删除
和修改方法类似,通过"@变量名.deleter"可以指定属性删除方法,在上面的代码后新增删除方法,在删除属性的时候提示"不能删除balance属性",并将其设置为空:
class Acct:def __init__(self, balance):self.__balance = balance@propertydef balance(self):print('通过方法访问balance:')return self.__balance@balance.setterdef balance(self, amt):if amt < 0:raise ValueError('余额不能设置为负')print('通过方法设置balance为:', amt)self.__balance = amt@balance.deleterdef balance(self):print('不能删除balance属性,设置为空')self.__balance = None
实例化后,尝试删除balance属性,删除方法成功执行,提示禁止删除,并将变量设置为空:
a = Acct(100)
del a.balance
以上即是使用@property进行属性封装的用法,通过封装可以在给属性赋值或获取值时加入条件判断、类型检查、逻辑计算等操作,从而保证对象的完整性和数据有效性。