Py003-01-07封装

封装

属性和方法的隐藏,外部不能直接通过属性和方法名访问

  • 双下划线前缀的属性和方法,在类外部无法直接这样访问
  • 但在类的内部可以访问这些“双下划线前缀的属性和方法”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

class A:
__x = 1

def __init__(self,name):
self.__name = name

def __foo(self):
print('run foo')

def bar(self):
self.__foo()
print('from bar')

# print(A.__x) 报错
# print(A.__foo()) 报错
# print(A.bar()) 不报错

# 即使创建该类对象 也无法访问双下划线的属性和方法
a = A('hjx')
# print(a.__name) 报错
# print(a.__foo()) 报错

# 可以通过 __dict__ 访问A类的相关属性信息
print(A.__dict__)
'''
{
'__module__': '__main__',
'_A__x': 1,
'__init__': <function A.__init__ at 0x00000187A059AF28>,
'_A__foo': <function A.__foo at 0x00000187A059AEA0>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': None
}
'''

我们发现 dict信息里属性通过一种方式——变形

1
2
3
4
5
6
7
8
9
class A:
__x = 1 # _A__x = 1
def __init__(self,name):
self.__name = name # _A__name = name
def __foo(self): # def _A__foo(self)
print('run foo')

类中的 __x 会变为 _A__x
类中的 __foo 会变为 _A__foo

这种变形的特点

  • 在类外部无法直接obj.__属性名
  • 类内部可以通过:obj.__属性名
  • 子类无法覆盖父类双下划线前缀的属性和方法
    (因为在类执行前 双下划线前缀的属性和方
    法会产生变形)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A:
    __x = 1 # 变形为 _A__x
    def __foo(self): # 变形为 def _A__foo(self):
    print('from foo A')

    class B(A):
    __x = 2 # 变形为 _B__x
    def __foo(self): # 变形为 def _B__foo(self):
    print('from foo B')

知道变形规则后你可以这样投机的访问这些双下划线前缀的属性和方法

1
2
3
4
# 这样就不报错了
a = A('hjx')
print(a._A__x)
a._A__foo()

这种变形需要注意的问题:

  • 如果想隐藏属性和方法你就添加双下划线前缀,你知道了变形规则和访问方式还想直接访问那索性不如不加双下划线前缀
1
2
3
4
5
6
class B:
__x = 1
y = 2

print(B._B__x) # 你都隐藏了还这样访问? 是不是傻?
print(B.y) # 既然想访问就别费劲了 还加双下划线前缀
  • 双下划线前缀的属性和方法只在类定义的时候变形,后续添加的双下划线前缀属性或方法不会进行——变形
1
2
3
4
5
6
7
class B:
__x = 1

B.__y = 2
print(B.__dict__)
print(B.__y) # 像普通属性一样访问
# 自己看吧!

所以说你要隐藏属性或方法,要在类定义的时候进行

  • 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

先看我们之前的学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A:
def foo(self):
print('from a foo')
def bar(self):
print('from a bar')
self.foo()

class B(A):
def foo(self):
print('from b foo')

b = B()
b.bar()
'''
执行结果:
from a bar
from b foo
1. b本身没有bar方法
2. 于是从父类A查找 bar方法 找到了
3. 执行 bar的时候 调用 self.foo() 此时的self是 b对象 所以 self.foo() 相当于 b.foo()
'''

实现我们的需求 子类调用父类的同名方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A:
def __foo(self): # _A__foo
print('from a foo')
def bar(self):
print('from a bar')
self.__foo() # self._A_foo

class B(A):
def __foo(self):
print('from b foo')

b = B()
b.bar()

'''
执行结果
from a bar
from a foo
'''