1. 封装
1.1 定义
广义上的封装:把方法和变量都封装在类中,在类的外部就不能直接调用了
狭义上的封装:在类的外部直接就不能调用了
定义总结:隐藏对象的属性和实现细节,仅对外提供公共访问方式
1.2 封闭原则
- 将不需要对外提供的内容都隐藏起来
- 把属性都隐藏,提供公共方法对其访问
1.3 优点
- 将变化隔离;
- 便于使用;
- 提高复用性;
- 提高安全性;
2. 私有变量和私有方法
在python中用双下划线开头的方式将类变量、实例变量(对象属性)、实例方法隐藏起来(即设置成私有的);一旦变成私有的,就只能在类的内部使用,而不能在类的外部使用了
2.1 私有变量
例1:正常情况下定义/获取类的属性
class Student:
N=11
def __init__(self,name):
self.name = name
def showAge(self):
print('I am 18 years old')
tom = Student('tom')
print(tom.N) # 11
print(tom.name) # tom
tom.showAge() # I am 18 years old
例2:__X定义私有变量、对象属性
class Student:
__N = 11
def __init__(self,name):
self.__name = name
def __showAge(self):
print('I am 18 years old')
tom = Student('tom')
# print(tom.N) # AttributeError: 'Student' object has no attribute 'N'
# print(tom.__N) # AttributeError: 'Student' object has no attribute '__N'
# print(tom.name) # AttributeError: 'Student' object has no attribute 'name'
# print(tom.__name) # AttributeError: 'Student' object has no attribute '__name'
# tom.showAge() # AttributeError: 'Student' object has no attribute 'showAge'
# tom.__showAge() # AttributeError: 'Student' object has no attribute '__showAge'
print(tom._Student__N) # 11
print(tom._Student__name) # tom
tom._Student__showAge() # I am 18 years old
例3:外部获取私有变量的常规手段
class Student:
__N = 11
def __init__(self,name):
self.__name = name
def __showAge(self):
print('I am 18 years old')
def getN(self):
print(self.__N)
def getAge(self):
self.__showAge()
def getName(self):
return self.__name
tom = Student('tom')
tom.getN()
tom.getAge()
print(tom.getName())
'''输出
11
I am 18 years old
tom
'''
为什么把一些属性设置成私有变量
主要是为了不想外部直接访问这些私有的变量和属性,一方面把不想暴露的变量隐藏起来,另一方面某些变量可以提供给外部统一的接口来对私有变量进行访问
举例说上面tom对象的name属性,如果按照正常的方法定义属性,那么这个tom的方法是随便可改的,且无法直接对新更改的名称做校验;
如果我们把原来的name属性改成私有变量,然后再在类内定义一个方法专门用来名字的修改,而在这个改名的方法中我们可以加入各种判断和校验逻辑(比如先判断修改的新名字是否是字符串格式的)
私有化实现的原理
# 查看上述Student类的__dict__方法
print(Student.__dict__)
'''输出
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '_Student__N': 11, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__init__': <function Student.__init__ at 0x102e42950>, '__doc__': None, 'getAge': <function Student.getAge at 0x102e42bf8>, 'getName': <function Student.getName at 0x102e42c80>, 'getN': <function Student.getN at 0x102e42b70>, '_Student__showAge': <function Student.__showAge at 0x102e42a60>}
'''
# 关注'_Student__N': 11,
类中所有双下划线开头的名称如__x
都会自动变形成:_类名__x
的形式,这样,就无法直接通过原来的变量名进行调用了;
当然,这只是一种变形操作,还是可以通过tom._Student__name
的方法来访问到的,即这种操作并不是严格意义伤的限制外部访问,仅仅是一种语法意义上的变形
为什么在类的内部能直接调用私有变量/属性
- 在内部调用私有变量/属性的时候对名字进行了包装,都会自动转变成
self._类名__变量名
的方式来调用; - 外部直接调用私有变量是不会自动转换的,但是如果像上述例2中,直接自己通过
tom._Student__N
来调用也是能得到结果的;但是强烈不建议这么调用
私有变量不能被继承
# 常规的继承
class A:
def func(self):
print('in A')
class B(A):
def test(self):
self.func()
b = B()
b.test() # in A
# 定义成私有方法之后再继承
class A:
def __func(self): # _A__func
print('in A')
class B(A):
def test(self):
self.__func() # _B__func
b = B()
b.test()
'''报错
AttributeError: 'B' object has no attribute '_B__func'
'''
由此可见,私有变量是无法被继承的,只能在自己本类中被使用
2.2 私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
正常情况下
class A:
def func(self):
print('in A')
def test(self):
self.func()
class B(A):
def func(self):
print('in B')
b = B()
b.test() # in B
把func定义为私有方法,__func
class A:
def __func(self):
print('in A')
def test(self):
self.__func()
class B(A):
def func(self):
print('in B')
b = B()
b.test() # in A