1 区域
2 公有属性
3 受保护属性
4 私有属性
- 注意:
1 - Python并没有真正的私有化支持,但是, 可以使用下划线完成伪私有的效果
2 - 类属性(方法)和实例属性(方法)遵循相同的规则
伪私有:即还是可以通过其他(如:_类名__属性名)
方式进行访问或修改
1 各种区域的划分及理解
2 公有属性访问权限
x:没有下划线修饰的,公有属性
- 类内部、子类内部、模块内部测试
class Animal:
x = 10
def test(self):
print(Animal.x)
print(self.x)
pass
class Dog(Animal):
def test2(self):
print(Dog.x)
print(self.x)
pass
# 测试在类的内部访问
a = Animal()
a.test()
# 测试在子类内部访问
d = Dog()
d.test2()
# 测试在模块内访问 - 类访问:通过父类(Animal) 或 派生类访问(Dog)
print(Animal.x)
print(Dog.x)
# 测试在模块内访问 - 实例访问:通过父类(a) 或 派生类访问(d)
print(a.x)
print(d.x)
- 跨模块测试 (通过公有变量测试,而公有变量与公有属性的区别是是否有数组)
# 文件1.py 代码
a = 666
# 文件2.py 代码
import 1 # 通过这样的导入方式,需要使用 模块名.xx 来访问
print(1.a) # 666
或者
from 1 import *
pirnt(a) # 666
3 受保护属性访问权限
_x:一个下划线修饰的,受保护属性
- 类内部、子类内部、模块内部测试
class Animal:
_x = 10
def test(self):
print(Animal._x)
print(self._x)
pass
class Dog(Animal):
def test2(self):
print(Dog._x)
print(self._x)
pass
# 测试在类的内部访问
a = Animal()
a.test()
# 测试在子类内部访问
d = Dog()
d.test2()
# 测试在模块内访问 - 类访问:通过父类(Animal) 或 派生类访问(Dog)
print(Animal._x)
print(Dog._x)
# 测试在模块内访问 - 实例访问:通过父类(a) 或 派生类访问(d)
print(a._x)
print(d._x)
- 跨模块测试 (通过公有变量测试,而公有变量与公有属性的区别是是否有数组)
# 文件1.py 代码
_a = 666
# 文件2.py 代码
import 1 # 通过这样的导入方式,需要使用 模块名.xx 来访问
print(1._a) # 666
或者
from 1 import *
pirnt(_a) # error
- 注意,如果_a 被 __all__修饰的话,则通过
from 模块名 import *
方式导入也是可以访问的。
# 文件1 中 模块内其他位置
__all__ = ["_a"]
_a = 666
# 文件2 模块内访问
from 1 import *
pirnt(_a) # 666
4 私有属性
__x:两个下划线修饰的,私有属性
class Animal:
__x = 10
def test(self):
print(Animal.__x)
print(self.__x)
pass
class Dog(Animal):
def test2(self):
print(Dog.__x)
print(self.__x)
pass
# 测试在类的内部访问
a = Animal()
a.test()
# 测试在子类内部访问
d = Dog()
d.test2()
# 测试在模块内访问 - 类访问:通过父类(Animal) 或 派生类访问(Dog)
print(Animal.__x)
print(Dog.__x)
# 测试在模块内访问 - 实例访问:通过父类(a) 或 派生类访问(d)
print(a.__x)
print(d.__x)
- 跨模块测试 (通过公有变量测试,而公有变量与公有属性的区别是是否有数组)
# 文件1.py 代码
__a = 666
# 文件2.py 代码
import 1 # 通过这样的导入方式,需要使用 模块名.xx 来访问
print(1.__a) # 666
或者
from 1 import *
pirnt(__a) # error
- 注意,如果__a 被 __all__修饰的话,则通过
from 模块名 import *
方式导入也是可以访问的。
# 文件1 中 模块内其他位置
__all__ = ["__a"]
__a = 666
# 文件2 模块内访问
from 1 import *
pirnt(__a) # 666
- 私有属性的跨模块访问规则,参照单下划线开头变量的访问原则
私有属性的实现机制
- 实际是通过:名字重整(Name Mangling),即重改 __x 为另外一个名称, 如
_类名__x
# 可通过__dict__类属性查看
print(Animal.__dict__)
- 使用私有属性目的:
- 防止外界直接访问
- 防止被子类同名称属性覆盖
私有属性的应用场景
- 通过Person类实例化的对象都拥有一个 age属性,且具有一个默认值
class Person:
# 主要作用, 当我们创建好一个实例对象之后, 会自动的调用这个方法, 来初始化这个对象
def __init__(self):
#此时的 age 是添加到对应的实例里面的,也就是 age 是类实例属性,Person 内没有 age 类属性
self.age = 18
p1 = Person()
p2 = Person()
p3 = Person()
print(p1.age) # 18
print(p2.age) # 18
print(p3.age) # 18
- 为避免将错误的数据赋值给实例.age
p1.age = -10
- 我们需要将 age 属性保护 起来,让实例对象不能直接访问age属性。
# 通过私有实例属性保护age -> __age
class Person:
def __init__(self):
# 此时的 __age 会被编译器执行名字重整 name mangling,通过print(p1.__dict__) 打印结果是: {'_Person__age': 18}
self.__age = 18
p1 = Person()
print(p1.age) # error,因为 age 已经被名字重整了,除非你通过 p1._Person__age进行访问,但这个访问方式不稳定,这是编译器特性
- 通过实例方法对私有实例属性 age进行访问,同时对传入的数据进行数据过滤
class Person:
def __init__(self):
self.__age = 18
def setAge(self, value):
if isinstance(value, int) and 0 < value < 200:
self.__age = value
else:
print("你输入的数据有问题, 请重新输入")
def getAge(self):
return self.__age # 这里能够通过__age访问是因为在类的内部
p1 = Person()
print(p1.getAge()) # 18
p1.setAge(10)
print(p1.getAge()) # 10
- 上述例子解决了
- 让所有实例都具有一个有默认值的"属性"(只能通过方法访问)
- 对赋值数据进行了过滤操作
实践小提示:
在不明白属性所在位置或是否有name mangling时候,多通过__dict__进行打印查证
5 添加下划线的规范
xx_
:"变量名_" 这个格式是为了与系统属性作区分
__xx__
:"两端带__" 一般为系统内置属性或方法, 所以以后命名注意避免