封装
我们在类中可以定义一些属性、方法,对于有些属性来说,我们可能不希望暴露给外界,因为在外界直接访问的时候(主要是直接修改的时候)可能设置的值并不是我们想要的一些值,可能会对我们的逻辑产生影响。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 创建Person对象
x = Person('xiaobai', 19)
# 类外直接访问属性,修改
x.age = 2000
在上述代码中可以看到,在类外访问x对象的age属性,并且将值修改为2000。这个操作并没有什么语法错误,可以成功的将age属性设置为2000。从语法的角度出发没有错,可是从我们的逻辑出发却是有问题的,因为我希望一个人类的年龄应该限制在[0, 150]的范围之内的!此时,我就非常不希望外面直接修改age属性的值
如何解决上述的问题呢?
1. 可见性
在类中定义的属性、方法都是有一定的可见性的,也就是在哪里可以看到、可以访问。在Python中,可见性分为三种: 公共的、保护的、私有的。
可见性 | 可见性描述 | 可见性修饰 |
---|---|---|
公共的 | 在任何的位置都可以访问,默认 | 默认创建的属性、方法都是公共的可见性,不需要什么操作 |
保护的 | 只能够在当前的模块中访问 | 使用一个下划线开头,例如: _age |
私有的 | 只能够在当前的类中访问 | 使用两个下划线开头,例如: __age |
class Person:
# 为了不让外界直接访问到age属性,这里将名字设置为了 __age
def __init__(self, name, age):
self.name = name
self.__age = age
x = Person('xiaobai', 18)
# print(x.__age) 这里会报错,因为找不到这个属性
这样一来,我们就可以将属性私有化起来,不让外界直接访问了!
拓展知识点:
Python中所谓的“私有化”,其实是“防君子,不防小人”。Python的研发人员遵循的原则是“大家都是成年人,知道事情的轻重,知道有些事情可以做,有些事情不能做”。**Python中并没有真正的私有化,之所以访问不到了,是因为在存储的时候修改了名字!**这些被私有化的成员的名字被定义为 _类名__特征名
# 在上述的代码中,直接访问__age是访问不到的 # 因为Python在存储这个属性的时候,并不是按照__age来存储的,而是按照 _Person__age 来存储的 # 使用下面的访问,访问属性,看看是不是成功的访问到了! print(x._Person__age)
2. 方法属性化
我们已经成功的将某些属性隐藏起来了,外界不能直接访问到了。可是别忘了我们的初衷。我们为什么要隐藏起来这个属性的?其实就是因为外界直接去修改的时候,可能会设置一些“逻辑错误”的值,给我们带来麻烦。但是私有化起来之后,外界就彻底无法访问了,这样也是不妥的。因此我们就需要提供这些私有属性的对应的访问的方法,并且在这些方法中,添加我们自己的过滤的条件。
class Person:
def __init__(self, name, age):
self.name = name
self.set_age(age)
# 提供set方法,可以让外界通过调用这个方法,来修改属性__age的值
# 在这个方法中,可以添加上自己的业务逻辑,实现对外界修改值的过滤
def set_age(self, age):
if 0 <= age <= 150:
self.__age = age
# 提供get方法,可以让外界通过调用这个方法,来获取属性__age的值
def get_age(self):
return self.__age
# 创建对象
x = Person('xiaobai', 18)
# 通过方法访问属性
x.set_age(2000)
print(x.get_age())
通过上述的方式,的确可以实现属性的私有化,并且也可以在类外通过特定的方式访问到属性。但是使用起来其实是不方便的。出于方便性的考虑,我们可以将set/get方法属性化,使得在类外使用的时候,有着跟访问属性一样的便利性,同时还能在类内保持自己的业务逻辑处理。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# get方法属性化,添加@property
@property
def age(self):
return self.__age
# set方法属性化,添加@属性.setter
# 这里需要提前给属性添加@property属性,单独写这个是会出错的
@age.setter
def age(self, age):
if 0 <= age <= 150:
self.__age = age
#
p = Person('xiaobai', 19)
# 访问“属性”
p.age = 19
print(p.age)