访问控制:私有属性和私有方法、保护变量
面向对象三要素之一:封装
属性装饰器,@property(getter),setter,deleter
property(fget=None, fset=None, fdel=None, doc=None)
私有属性
有些属性不希望外部可以访问或者修改,python 提供私有属性解决这个问题。
- 私有属性:使用双下划线开头的属性名
- 私有属性的本质:如果声明一个实例变量使用双下划线开头,python 解释器会将其改名,转换名称为
_类名__变量名
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
def getage(self):
return self.__age
j = Person('jerry',18)
# print(j.__age) # AttributeError: 'Person' object has no attribute '__age'
j.__age = 19 # 动态增加__age 属性
print(j.getage())
print(j.__dict__) # 可以看出__age私有属性被修改成了_Person__age
j._Person__age = 19 # 虽然知道修改了名称,但一般不建议这么做
print(j.getage())
print(j.__dict__)
-------------------------------------------------
18
{'name': 'jerry', '_Person__age': 18, '__age': 19}
19
{'name': 'jerry', '_Person__age': 19, '__age': 19}
保护属性
class Person:
def __init__(self,name,age):
self.name = name
self._age = age
j = Person('jerry',18)
print(j.__dict__)
-----------------------
{'name': 'jerry', '_age': 18}
- 保护属性不做改名,和普通属性一样,解释器不做任何处理
- 只是开发者共同的约定,这种变量等同私有变量,最好不要直接使用
私有方法
- 同私有属性,使用双下划线命名的方法
- 通过改名隐藏,改成
_类名__方法名
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
def __getage(self):
return self.__age
j = Person('jerry',18)
# print(j.__getage) # AttributeError: 'Person' object has no attribute '__getage'
print(j.__dict__)
print(j.__class__.__dict__.keys())
print(Person.__dict__.keys())
print(j._Person__getage())
---------------------------------
{'name': 'jerry', '_Person__age': 18}
dict_keys(['__module__', '__init__', '_Person__getage', '__dict__', '__weakref__', '__doc__'])
dict_keys(['__module__', '__init__', '_Person__getage', '__dict__', '__weakref__', '__doc__'])
18
综上,在 Python 中使用 单下划线或者双下划线来标识一个成员被保护或者被私有化隐藏起来。但是,不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员
Python 中没有绝对的安全的保护成员或者私有成员。因此,下划线只是一种警告或者提醒,请遵守这个约定。除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们
补丁
通过替换修改来增强扩展原有代码的能力。可用于调试 mock 数据等。
不做深入解释,可参考其他资料。
封装
- 面向对象三要素之一:封装(encapsulation)
- 将数据和操作组织到类中,即属性和方法
- 通过访问控制,将数据隐藏起来,给使用者提供操作(方法),使用者通过操作就可以获取或者修改数据如 getter 和 setter
属性装饰器
属性保护起来了,不直接从外部访问,那需要提供外部访问的接口,如 getter 读取属性和 setter 设置属性
@property
# 可以这样,提供getage和setage方法
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def getage(self):
return self.__age
def setage(self, age):
self.__age = age
j = Person('jerry')
j.setage(20)
j.getage()
----------------------------------
20
其实我们有时候希望有更简单的操作方式,如j.age
来获取, j.age = 20
来设置。
python 提供了 property 装饰器,property 装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏效果
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
self.__age = age
@age.deleter
def age(self):
del self.__age
j = Person('jerry')
print(j.age)
j.age = 20
print(j.age)
--------------
18
20
- 使用 property 装饰器的时三个方法 (getter,setter,deleter) 同名
- property 装饰器必须在前,setter、deleter 装饰器在后
@property
后面跟的函数名就是以后的属性名,他就是 getter,这个必须有,此时保证属性可读@属性名.setter
接收两个参数,第一个是 self,第二个是将要赋值的值,此时属性可写@属性名.deleter
控制属性是否删除,很少用
property(fget=None, fset=None, fdel=None, doc=None)
- property 的另一个表示方式,使用 property 类
- 至少要包含 fget
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
def getage(self):
return self.__age
def setage(self,age):
self.__age = age
def delage(self):
del self.__age
age = property(getage,setage,delage,'age property') #
j = Person('jerry')
print(j.age)
j.age = 20
print(j.age)
# 至少包含fget,且可用lambda表达式简化
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
#def getage(self):
# return self.__age
# age = property(getage)
age = property(lambda self:self.__age)
j = Person('jerry')
print(j.age)
参考
- magedu