我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈
@TOC
私有化(隐藏)属性/方法
概念
- 把原本访问范围比较大的属性,设置一个权限,让其访问范围变小
用途示例
- 示例1:人的体重变化
class Person: # 体重属性 weight = 65 def eat(self): print('通过吃东西增加体重...') def exercise(self): print('通过运动减少体重.....') # 这个人的类里面有一个体重属性 weight ,这是一个类属性 # 那么我们可以在类内部拿到这个属性,也可以通过实例在类外部拿到这个属性,并进行修改 # 这个体重可以随意变化,显然不是很靠谱 # 对于体重的变化,我们希望只能通过吃东西(eat())和运动(exercise())方法来进行调整 # 所以我们需要将weight这个属性进行私有化
- 示例2:银行余额变化
class Bank: # 余额属性 surplus = 1000 def save_money(self): print('通过存钱,使余额变多......') def draw_money(self): print('通过取钱,使余额变少......') # 这个银行的类里面有一个余额属性 surplus ,这是一个类属性 # 那么我们可以在类内部拿到这个属性,也可以通过实例在类外部拿到这个属性,并进行修改 # 这余额可以随意修改,显然不是很安全 # 对于余额的变化,我们希望只能通过存款(save_money())和取款(draw_money())方法来进行修改 # 所以我们需要将 surplus 这个属性进行私有化
- 示例3:买包子
class Person: def __run(self): print('出门跑步去包子店...') def __pay(self): print('拿手机扫码付钱给老板...') def __take_bun(self): print('老板从蒸笼里面拿出包子给我...') def buy_bun(self): """买包子""" self.__run() self.__pay() self.__take_bun() # 在这个人的类里面,有一个买包子的功能,但是这个功能又被分成了无数个小功能 # 但是,我们实例化一个人的时候,只需要他执行买包子功能,其他那些小的功能就没必要都让执行者看到了 # 将其他方法私有化(隐藏)后,在实例调用方法的时候就看不到这些小功能了
注意事项
- 在其他某些语言中可以通过关键字指定属性为私有属性(
public int id
) - 但是,在Python中并没有真正的私有化支持,只能使用下划线完成伪私有的效果
- 类属性(方法)和实例属性(方法)遵循相同的规则
不同属性各区域访问权限
区域划分
-
分区
- 类内部区域
- 子类内部区域
- 模块内部其他区域
- 模块外部区域(跨模块)
-
分区图解
-
访问权限
公有属性(x)
- 访问权限
- 类内部访问
- 可以访问模块公有属性
- 可以访问类公有属性
- 子类内部访问
- 可以访问模块公有属性
- 可以访问类公有属性
- 模块内其他位置访问
- 可以访问模块公有属性
- 可以访问类公有属性
- 跨模块访问
from 模块 import 类
形式导入- 不可以访问模块公有属性
- 可以访问类公有属性
import 模块
形式导入- 可以访问模块公有属性
- 可以访问类公有属性
from 模块 import *
形式导入- 可以访问模块公有属性
- 可以访问类公有属性
- 类内部访问
- 代码示例
- super_class模块代码(super_class.py)
m = '模块公有属性m' n = '模块公有属性n' class Animal: x = '类公有属性x' y = '类公有属性y' def A_test(self): print('类内部访问类属性:', Animal.x) print('类内部访问类属性:', self.y) print('类内部访问模块属性:', m) print('类内部访问模块属性:', n) print('') class Dog(Animal): def D_test(self): print('子类内部访问类属性:', Dog.x) print('子类内部访问类属性:', self.y) print('子类内部访问模块属性:', m) print('子类内部访问模块属性:', n) print('') # =========访问权限测试======== if __name__ == '__main__': # # 类内部访问 a = Animal() a.A_test() # # 子类内部访问 d =Dog() d.D_test() # 模块内其他位置访问 print('模块内其他位置访问类属性,父类.类属性:', Animal.x) print('模块内其他位置访问类属性,派生类.类属性:', Dog.y) print('') print('模块内其他位置访问类属性,父类实例.类属性:', a.x) print('模块内其他位置访问类属性,派生类实例.类属性:', d.y) print('') print('模块内其他位置访问,模块属性:', m) print('模块内其他位置访问,模块属性:', n) # ========== 输出结果 ========== # 类内部访问类属性: 类公有属性x # 类内部访问类属性: 类公有属性y # 类内部访问模块属性: 模块公有属性m # 类内部访问模块属性: 模块公有属性n # # 子类内部访问类属性: 类公有属性x # 子类内部访问类属性: 类公有属性y # 子类内部访问模块属性: 模块公有属性m # 子类内部访问模块属性: 模块公有属性n # # 模块内其他位置访问类属性,父类.类属性: 类公有属性x # 模块内其他位置访问类属性,派生类.类属性: 类公有属性y # # 模块内其他位置访问类属性,父类实例.类属性: 类公有属性x # 模块内其他位置访问类属性,派生类实例.类属性: 类公有属性y # # 模块内其他位置访问,模块属性: 模块公有属性m # 模块内其他位置访问,模块属性: 模块公有属性n
- sub_class模块代码(sub_class.py)
'<!--导入方式1-->' # from super_class import Animal # """导入了一个模块中的module(模块)、class(类)、function(函数)或是variable(变量)""" # # print('跨模块访问模块公有属性:', m) # 报错: NameError: name 'm' is not defined # # print('跨模块访问模块公有属性:', super_class.n) # 报错: NameError: name 'super_class' is not defined # # print('跨模块访问类公有属性:', x) # 报错: NameError: name 'x' is not defined # print('跨模块访问类公有属性:', Animal.y) # 正常访问: 跨模块访问类公有属性: 类公有属性y '<!--导入方式2-->' # import super_class # """导入一个模块(module)""" # # print('跨模块访问模块公有属性:', m) # 报错: NameError: name 'm' is not defined # print('跨模块访问模块公有属性:', super_class.n) # 正常访问: 跨模块访问模块公有属性: 模块公有属性n # # print('跨模块访问类公有属性:', super_class.x) # 报错: AttributeError: module 'super_class' has no attribute 'x' # print('跨模块访问类公有属性:', super_class.Animal.y) # 正常访问: 跨模块访问类公有属性: 类公有属性y # print('跨模块访问父类公有属性:', super_class.Dog.x) # 正常访问: 跨模块访问父类公有属性: 类公有属性x '<!--导入方式3-->' from super_class import * """"是把一个模块中所有函数都导入进来""" print('跨模块访问模块公有属性:', m) # 正常访问: 跨模块访问模块公有属性: 模块公有属性m print('跨模块访问模块公有属性:', super_class.n) # 正常访问: 跨模块访问模块公有属性: 模块公有属性n # print('跨模块访问类公有属性:', x) # 报错: NameError: name 'x' is not defined print('跨模块访问类公有属性:', Animal.x) # 正常访问: 跨模块访问类公有属性: 类公有属性x print('跨模块访问类公有属性:', super_class.Animal.x) # 正常访问: 跨模块访问类公有属性: 类公有属性x print('跨模块访问父类公有属性:', Dog.x) # 正常访问: 跨模块访问父类公有属性: 类公有属性x print('跨模块访问父类公有属性:', super_class.Dog.x) # 正常访问: 跨模块访问父类公有属性: 类公有属性x
受保护类属性(_x)
-
访问权限
- 类内部访问
- 可以访问模块受保护属性
- 可以访问类受保护属性
- 子类内部访问
- 可以访问模块受保护属性
- 可以访问类受保护属性
- 模块内其他位置访问
- 可以访问模块受保护属性
- 可以访问类受保护属性,但是有警告
- 可以强行访问受保护类属性,但是PyCharm会有警告提示
Access to a protected member _x of a class (对类的受保护成员x的访问)
- 跨模块访问
from 模块 import 类
形式导入- 不可以访问模块受保护属性
- 可以访问类受保护属性,但是有警告
import 模块
形式导入- 可以访问模块受保护属性,但是有警告
- 可以访问类受保护属性,但是有警告
- 可以强行访问受保护视图,但是PyCharm会有报警提示
Access to a protected member _x of a class (对类的受保护成员x的访问)
from 模块 import 类
形式导入- 可以访问模块受保护属性,但是需要通过
__all__
属性指定属性 - 可以访问类受保护属性,但是有警告
- 可以强行访问受保护视图,但是PyCharm会有报警提示
Access to a protected member _x of a class (对类的受保护成员x的访问)
- 可以访问模块受保护属性,但是需要通过
- 类内部访问
-
代码示例
-
super_class模块代码(super_class.py)
_m = '模块公有属性m' _n = '模块公有属性n' __all__ = ['_n'] class Animal: _x = '类公有属性x' _y = '类公有属性y' def A_test(self): print('类内部访问类属性:', Animal._x) print('类内部访问类属性:', self._y) print('类内部访问模块属性:', _m) print('类内部访问模块属性:', _n) print('') class Dog(Animal): def D_test(self): print('子类内部访问类属性:', Dog._x) print('子类内部访问类属性:', self._y) print('子类内部访问模块属性:', _m) print('子类内部访问模块属性:', _n) print('') # =========访问权限测试======== if __name__ == '__main__': # # 类内部访问 a = Animal() a.A_test() # # 子类内部访问 d =Dog() d.D_test() # 模块内其他位置访问 print('模块内其他位置访问类属性,父类.类属性:', Animal._x) # 警告:Access to a protected member _x of a class(对类的受保护成员x的访问) print('模块内其他位置访问类属性,派生类.类属性:', Dog._y) # 警告:Access to a protected member _y of a class(对类的受保护成员y的访问) print('') print('模块内其他位置访问类属性,父类实例.类属性:', a._x) # 警告:Access to a protected member _x of a class(对类的受保护成员x的访问) print('模块内其他位置访问类属性,派生类实例.类属性:', d._y) # 警告:Access to a protected member _y of a class(对类的受保护成员y的访问) print('') print('模块内其他位置访问,模块属性:', _m) print('模块内其他位置访问,模块属性:', _n) # ========== 输出结果 ========== # 类内部访问类属性: 类公有属性x # 类内部访问类属性: 类公有属性y # 类内部访问模块属性: 模块公有属性m # 类内部访问模块属性: 模块公有属性n # # 子类内部访问类属性: 类公有属性x # 子类内部访问类属性: 类公有属性y # 子类内部访问模块属性: 模块公有属性m # 子类内部访问模块属性: 模块公有属性n # # 模块内其他位置访问类属性,父类.类属性: 类公有属性x # 模块内其他位置访问类属性,派生类.类属性: 类公有属性y # # 模块内其他位置访问类属性,父类实例.类属性: 类公有属性x # 模块内其他位置访问类属性,派生类实例.类属性: 类公有属性y # # 模块内其他位置访问,模块属性: 模块公有属性m # 模块内其他位置访问,模块属性: 模块公有属性n
-
sub_class模块代码(sub_class.py)
'<!--导入方式1-->' # from super_class import Animal # """导入了一个模块中的module(模块)、class(类)、function(函数)或是variable(变量)""" # print('跨模块访问模块公有属性:', _m) # 报错: NameError: name '_m' is not defined # print('跨模块访问模块公有属性:', super_class._n) # 报错: NameError: name 'super_class' is not defined # print('跨模块访问类公有属性:', _x) # 报错: NameError: name '_x' is not defined # print('跨模块访问类公有属性:', Animal._y) # 能访问: 跨模块访问类公有属性: 类公有属性_y,但是有警告Access to a protected member _y of a class '<!--导入方式2-->' # import super_class # """导入一个模块(module)""" # print('跨模块访问模块公有属性:', _m) # 报错: NameError: name '_m' is not defined # print('跨模块访问模块公有属性:', super_class._n) # 能访问: 跨模块访问模块公有属性: 模块受保护属性_n,但有警告Access to a protected member _n of a module # print('跨模块访问类公有属性:', super_class._x) # 报错: AttributeError: module 'super_class' has no attribute '_x' # print('跨模块访问类公有属性:', super_class.Animal._y) # 能访问: 跨模块访问类公有属性: 类受保护属性_y,但有警告Access to a protected member _y of a class # print('跨模块访问父类公有属性:', super_class.Dog._x) # 能访问: 跨模块访问父类公有属性: 类受保护属性_x,但有警告Access to a protected member _x of a class '<!--导入方式3-->' from super_class import * """"是把一个模块中所有函数都导入进来""" # print('跨模块访问模块公有属性:', _m) # 报错: NameError: name '_m' is not defined # print('跨模块访问模块公有属性:', super_class._m) # 报错:NameError: name 'super_class' is not defined # print('跨模块访问模块公有属性:', _n) # 通过__all__指定可以访问: 跨模块访问模块公有属性: 模块受保护属性_n # print('跨模块访问模块公有属性:', super_class._n) # 报错: NameError: name 'super_class' is not defined # print('跨模块访问类公有属性:', _x) # 报错: NameError: name '_x' is not defined # print('跨模块访问类公有属性:', Animal._x) # 能访问: 跨模块访问类公有属性: 类受保护属性_x,但有警告Access to a protected member _x of a class # print('跨模块访问类公有属性:', super_class.Animal._x) # 报错: NameError: name 'super_class' is not defined # print('跨模块访问父类公有属性:', Dog._x) # 能访问: 跨模块访问父类公有属性: 类受保护属性_x,但有警告Access to a protected member _x of a class # print('跨模块访问父类公有属性:', super_class.Dog._x) # 报错: NameError: name 'super_class' is not defined
私有化类属性(__x)
- 访问权限
- 类内部访问
- 不可以访问模块私有属性
- 可以访问类私有属性
- 子类内部访问
- 不可以访问模块私有属性
- 不可以访问类私有属性
- 模块内其他范围访问
- 可以访问模块私有属性
- 不可以访问类私有属性
- 跨模块访问
from 模块 import 类
形式导入- 不可以访问模块私有属性
- 不可以访问类私有属性
import 模块
形式导入- 可以访问模块私有属性
- 不可以访问类私有属性
from 模块 import *
形式导入- 可以访问模块私有属性,但是需要通过
__all__
指定属性 - 不可以访问类私有属性
- 可以访问模块私有属性,但是需要通过
- 类内部访问
- 代码示例
- super_class模块代码(super_class.py)
__m = '模块私有化属性__m' __n = '模块私有化属性__n' __all__ = ['__n'] class Animal: __x = '类私有化属性__x' __y = '类私有化属性__y' def A_test(self): print('类内部访问类属性:', Animal.__x) # 正常访问:类内部访问类属性: 类私有化属性__x print('类内部访问类属性:', self.__y) # 正常访问:类内部访问类属性: 类私有化属性__y # print('类内部访问模块属性:', __m) # 报错:NameError: name '_Animal__m' is not defined # print('类内部访问模块属性:', __n) # 报错:NameError: name '_Animal__n' is not defined print('') class Dog(Animal): def D_test(self): # print('子类内部访问类属性:', Dog.__x) # 报错:AttributeError: type object 'Dog' has no attribute '_Dog__x' # print('子类内部访问类属性:', self.__y) # 报错:AttributeError: 'Dog' object has no attribute '_Dog__y' # print('子类内部访问模块属性:', __m) # 报错:NameError: name '_Dog__m' is not defined # print('子类内部访问模块属性:', __n) # # 报错:NameError: name '_Dog__n' is not defined print('') # =========访问权限测试======== if __name__ == '__main__': # # 类内部访问 a = Animal() # a.A_test() # # 子类内部访问 d = Dog() # d.D_test() # # 模块内其他位置访问 # print('模块内其他位置访问类属性,父类.类属性:', Animal.__x) # 报错:AttributeError: type object 'Animal' has no attribute '__x' # print('模块内其他位置访问类属性,派生类.类属性:', Dog.__y) # 报错:AttributeError: type object 'Dog' has no attribute '__y' # print('') # # print('模块内其他位置访问类属性,父类实例.类属性:', a.__x) # 报错:AttributeError: 'Animal' object has no attribute '__x' # print('模块内其他位置访问类属性,派生类实例.类属性:', d.__y) # 报错:AttributeError: 'Dog' object has no attribute '__y' # print('') # print('模块内其他位置访问,模块属性:', __m) # 正常访问:模块内其他位置访问,模块属性: 模块私有化属性__m print('模块内其他位置访问,模块属性:', __n) # 正常访问:模块内其他位置访问,模块属性: 模块私有化属性__n
- sub_class模块代码(sub_class.py)
'<!--导入方式1-->' # from super_class import Animal # """导入了一个模块中的module(模块)、class(类)、function(函数)或是variable(变量)""" # print('跨模块访问模块公有属性:', __m) # 报错: NameError: name '__m' is not defined # print('跨模块访问模块公有属性:', super_class.__n) # 报错: NameError: name 'super_class' is not defined # print('跨模块访问类公有属性:', __x) # 报错: NameError: name '__x' is not defined # print('跨模块访问类公有属性:', Animal.__y) # 报错: AttributeError: type object 'Animal' has no attribute '__y' '<!--导入方式2-->' # import super_class """导入一个模块(module)""" # print('跨模块访问模块公有属性:', __m) # 报错: NameError: name '__m' is not defined # print('跨模块访问模块公有属性:', super_class.__n) # 正常访问: 跨模块访问模块公有属性: 模块私有化属性__n # print('跨模块访问类公有属性:', super_class.__x) # 报错: AttributeError: module 'super_class' has no attribute '__x' # print('跨模块访问类公有属性:', super_class.Animal.__y) # 报错: AttributeError: type object 'Animal' has no attribute '__y' # print('跨模块访问父类公有属性:', super_class.Dog.__x) # 报错: AttributeError: type object 'Dog' has no attribute '__x' '<!--导入方式3-->' from super_class import * """"是把一个模块中所有函数都导入进来""" # print('跨模块访问模块公有属性:', __m) # 报错: NameError: name '__m' is not defined # print('跨模块访问模块公有属性:', super_class.__m) # 报错:NameError: name 'super_class' is not defined # print('跨模块访问模块公有属性:', __n) # 通过__all__属性指定,可以正常访问:跨模块访问模块公有属性: 模块私有化属性__n # print('跨模块访问模块公有属性:', super_class.__n) # 报错: NameError: name 'super_class' is not defined # print('跨模块访问类公有属性:', __x) # 报错: NameError: name '__x' is not defined # print('跨模块访问类公有属性:', Animal.__x) # 报错: NameError: name 'Animal' is not defined # print('跨模块访问类公有属性:', super_class.Animal.__x) # 报错: NameError: name 'super_class' is not defined # print('跨模块访问父类公有属性:', Dog.__x) # 报错: NameError: name 'Dog' is not defined # print('跨模块访问父类公有属性:', super_class.Dog.__x) # 报错: NameError: name 'super_class' is not defined
- 访问情况
私有化(隐藏)属性/方法实现机制
- 名字重整(Name Mangling)机制:重改__x为另一个名称。如:_classname__x
- 目的:
- 防止被外界直接访问
- 防止被子类同名称属性覆盖
- 名字重整(Name Mangling)机制
-
1、本质只是一种改名操作
-
2、隐藏是对外不对内(在类外部无法调用,在类内部可以直接调用)
- 改名字在类定义阶段,检查类子代码语法的时候发生,在类调用阶段,用
__x
去调用就无法调用到了
- 改名字在类定义阶段,检查类子代码语法的时候发生,在类调用阶段,用
-
3、改名操作,只会在类的定义阶段检查子代码语法的时候执行一次,之后定义的
__
开头的属性,都不会改名
-
私有化属性应用场景
- 场景1:防止外部直接访问和修改属性值
class Person: # 实例方法,创建实例对象的时候会自动调用这个方法,初始化实例对象 def __init__(self): self.test = '测试' self.__age = 18 def setAge(self, num): """设置__age属性""" self.__age = num def getAge(self): """获取__age属性""" return self.__age p = Person() # print(p.test) # 公有属性,可以直接访问 # print(p.__age) # 私有属性,不能直接访问,会报错。AttributeError: 'Person' object has no attribute '__age' p.__age = 36 # 这并不是修改了实例对象原本的__age属性,而是给实例对象新增了一个__age属性 # print(p.__dict__) # {'test': '测试', '_Person__age': 18, '__age': 36},原本的__age属性通过名称重整机制修改为了_Person__age print(p.getAge()) # 通过指定的方法获取age属性:18 p.setAge(33) # 通过指定的方法设置age属性 print(p.getAge()) # 通过指定的方法获取age属性:33
- 场景2:对数据进行有效性验证
class Person: # 实例方法,创建实例对象的时候会自动调用这个方法,初始化实例对象 def __init__(self): self.test = '测试' self.__age = 18 def setAge(self, num): """设置__age属性""" if not isinstance(num, int): print('请输入一个数字!') return if not 0 < num < 200: print('请输入一个【0-200】之间的数字!') return self.__age = num def getAge(self): """获取__age属性""" return self.__age p = Person() # print(p.test) # 公有属性,可以直接访问 # print(p.__age) # 私有属性,不能直接访问,会报错。AttributeError: 'Person' object has no attribute '__age' # p.__age = 36 # 这并不是修改了实例对象原本的__age属性,而是给实例对象新增了一个__age属性 # print(p.__dict__) # {'test': '测试', '_Person__age': 18, '__age': 36},原本的__age属性通过名称重整机制修改为了_Person__age print(p.getAge()) # 通过指定的方法获取age属性:18 p.setAge(33) # 通过指定的方法设置age属性 print(p.getAge()) # 通过指定的方法获取age属性:33 p.setAge('aaa') # 请输入一个数字! p.setAge(333) # 请输入一个【0-200】之间的数字! print(p.getAge()) # 33
变量添加下划线规范
_x
:受保护属性__x
:私有属性x_
:区别系统关键字(如:class_)__x__
:系统内置的,自己定义的时候不要这样写(如:__dict__)
只读属性
概念
- 一个属性(一般指实例属性),只能读取,不能写入
应用场景
- 有些属性,只限在内部根据不同场景进行修改,对外界来说,不能修改,只能读取
实现方法
方式一
- 步骤
- 1、通过私有化全部隐藏(既不能读,也不能写)
- 2、通过方法部分公开(公开读的操作)
- 示例
class Person: def __init__(self): self.__age = 18 def getAge(self): return self.__age p = Person() # print(p.__age) # 报错,通过属性私有化,隐藏了读操作 # p.__age = 22 # 并不是修改的私有属性,而是新增了__age属性。通过属性私有化,隐藏了写操作 print(p.getAge()) # 通过指定方法,公开读操作
- 存在问题
- 1、获取属性的时候需要通过调用方法获取
p.getAge()
,而不是通过属性获取方式获取p.age
- 2、在外部直接使用赋值
p.age = 22
并不会报错(虽然没有修改内部的私有属性,而是给实例添加了一个age属性,但是给人的感觉就是修改了)
- 1、获取属性的时候需要通过调用方法获取
- 优化
class Person: def __init__(self): self.__age = 18 # @property的作用:装饰器,可以使用属性的方式调用这个方法 @property def age(self): return self.__age p = Person() print(p.age) # 18 # p.age = 22 # 报错,AttributeError: can't set attribute
- 通过优化之后,就可以通过调用属性的方式调用读属性方法了。并且,外部也不能对这个属性方法进行赋值