Skip to content

Commit 74a5e35

Browse files
committed
8.5小节完成~
1 parent 79767e9 commit 74a5e35

File tree

2 files changed

+117
-3
lines changed

2 files changed

+117
-3
lines changed

cookbook/c08/p05_encapsulate_name.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
"""
4+
Topic: 隐藏私有属性
5+
Desc :
6+
"""
7+
8+
9+
class A:
10+
def __init__(self):
11+
self._internal = 0 # An internal attribute
12+
self.public = 1 # A public attribute
13+
14+
def public_method(self):
15+
'''
16+
A public method
17+
'''
18+
pass
19+
20+
def _internal_method(self):
21+
pass
22+
23+
24+
class B:
25+
def __init__(self):
26+
self.__private = 0
27+
28+
def __private_method(self):
29+
pass
30+
31+
def public_method(self):
32+
pass
33+
self.__private_method()
34+
35+
36+
class C(B):
37+
def __init__(self):
38+
super().__init__()
39+
self.__private = 1 # Does not override B.__private
40+
41+
# Does not override B.__private_method()
42+
def __private_method(self):
43+
pass

source/c08/p05_encapsulating_names_in_class.rst

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,85 @@
55
----------
66
问题
77
----------
8-
todo...
8+
你想封装类的实例上面的“私有”数据,但是Python语言并没有访问控制。
9+
10+
|
911
1012
----------
1113
解决方案
1214
----------
13-
todo...
15+
Python程序员不去依赖语言特性去封装数据,而是通过遵循一定的属性和方法命名规约来达到这个效果。
16+
第一个约定是任何以单下划线_开头的名字都应该是内部实现。比如:
17+
18+
.. code-block:: python
19+
20+
class A:
21+
def __init__(self):
22+
self._internal = 0 # An internal attribute
23+
self.public = 1 # A public attribute
24+
25+
def public_method(self):
26+
'''
27+
A public method
28+
'''
29+
pass
30+
31+
def _internal_method(self):
32+
pass
33+
34+
Python并不会真的阻止别人访问内部名称。但是如果你这么做肯定是不好的,可能会导致脆弱的代码。
35+
同时还要注意到,使用下划线开头的约定同样适用于模块名和模块级别函数。
36+
例如,如果你看到某个模块名以单下划线开头(比如_socket),那它就是内部实现。
37+
类似的,模块级别函数比如 ``sys._getframe()`` 在使用的时候就得加倍小心了。
38+
39+
你还可能会遇到在类定义中使用两个下划线(__)开头的命名。比如:
40+
41+
.. code-block:: python
42+
43+
class B:
44+
def __init__(self):
45+
self.__private = 0
46+
47+
def __private_method(self):
48+
pass
49+
50+
def public_method(self):
51+
pass
52+
self.__private_method()
53+
54+
使用双下划线开始会导致访问名称变成其他形式。
55+
比如,在前面的类B中,私有属性会被分别重命名为 ``_B__private`` 和 ``_B__private_method`` 。
56+
这时候你可能会问这样重命名的目的是什么,答案就是继承——这种属性通过继承是无法被覆盖的。比如:
57+
58+
.. code-block:: python
59+
60+
class C(B):
61+
def __init__(self):
62+
super().__init__()
63+
self.__private = 1 # Does not override B.__private
64+
65+
# Does not override B.__private_method()
66+
def __private_method(self):
67+
pass
68+
69+
这里,私有名称 ``__private`` 和 ``__private_method``
70+
被重命名为 ``_C__private`` 和 ``_C__private_method`` ,这个跟父类B中的名称是完全不同的。
71+
72+
|
1473
1574
----------
1675
讨论
1776
----------
18-
todo...
77+
上面提到有两种不同的编码约定(单下划线和双下划线)来命名私有属性,那么问题就来了:到底哪种方式好呢?
78+
大多数而言,你应该让你的非公共名称以单下划线开头。但是,如果你清楚你的代码会涉及到子类,
79+
并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案。
80+
81+
还有一点要注意的是,有时候你定义的一个变量和某个保留关键字冲突,这时候可以使用单下划线作为后缀,例如:
82+
83+
.. code-block:: python
84+
85+
lambda_ = 2.0 # Trailing _ to avoid clash with lambda keyword
86+
87+
这里我们并不使用单下划线前缀的原因是它避免误解它的使用初衷
88+
(如使用单下划线前缀的目的是为了防止命名冲突而不是指明这个属性是私有的)。
89+
通过使用单下划线后缀可以解决这个问题。

0 commit comments

Comments
 (0)