@@ -164,11 +164,11 @@ True
164
164
165
165
## 2.5.3 消息传递和点表达式
166
166
167
- 方法定义在类中,而实例属性通常在狗在其中赋值 ,二者都是面向对象编程的基本元素。这两个概念很大程序上类似于数据值的消息传递实现中的分发字典 。对象使用点运算符接受消息,但是消息并不是任意的字符串的键,而是类局部的名称 。对象也拥有具名的局部状态值(实例属性),但是这个状态可以使用点运算符访问和操作,并不需要在实现中使用` nonlocal ` 语句。
167
+ 方法定义在类中,而实例属性通常在构造器中赋值 ,二者都是面向对象编程的基本元素。这两个概念很大程度上类似于数据值的消息传递实现中的分发字典 。对象使用点运算符接受消息,但是消息并不是任意的、值为字符串的键,而是类的局部名称 。对象也拥有具名的局部状态值(实例属性),但是这个状态可以使用点运算符访问和操作,并不需要在实现中使用` nonlocal ` 语句。
168
168
169
- 消息传递的核心概念,就是数据值应该通过响应消息而拥有行为,这些消息和它们所表示的抽象类型相关。点运算符是 Python 的语法特征,它形成了消息传递的隐喻。使用带有内建对象系统语言的优点是,消息传递能够和其它语言特征无缝对接,就像赋值语句那样 。我们并不需要不同的消息来“获取”和“设置”关联到局部属性名称的值;语言的语法允许我们直接使用消息名称。
169
+ 消息传递的核心概念,就是数据值应该通过响应消息而拥有行为,这些消息和它们所表示的抽象类型相关。点运算符是 Python 的语法特征,它形成了消息传递的隐喻。使用带有内建对象系统语言的优点是,消息传递能够和其它语言特性,例如赋值语句无缝对接 。我们并不需要不同的消息来“获取”和“设置”关联到局部属性名称的值;语言的语法允许我们直接使用消息名称。
170
170
171
- ** 点表达式。** ` tom_account.deposit ` 的代码片段叫做点表达式。点表达式包含一个表达式,一个点和一个名称:
171
+ ** 点表达式。** 类似 ` tom_account.deposit ` 的代码片段叫做点表达式。点表达式包含一个表达式,一个点和一个名称:
172
172
173
173
```
174
174
<expression> . <name>
192
192
193
193
对象的属性包含所有实例属性,以及所有定义在类中的属性(包括方法)。方法是需要特别处理的类的属性。
194
194
195
- ** 方法和函数。** 当一个方法在对象上调用时,对象隐式地作为第一个参数传递给方法。也就是说,点运算符左边值为` <expression> ` 的对象是会自动传给点运算符右边的方法 ,作为第一个参数。所以,对象绑定到了参数` self ` 上。
195
+ ** 方法和函数。** 当一个方法在对象上调用时,对象隐式地作为第一个参数传递给方法。也就是说,点运算符左边值为` <expression> ` 的对象,会自动传给点运算符右边的方法 ,作为第一个参数。所以,对象绑定到了参数` self ` 上。
196
196
197
- 为了自动实现` self ` 的绑定,Python 区分函数和绑定方法。我们已经在这门课的开始创建了前者,而后者在方法调用时将对象和函数组合到一起。绑定方法的值已经关联了第一个函数,即所调用的实例 ,当方法调用时实例会被命名为` self ` 。
197
+ 为了自动实现` self ` 的绑定,Python 区分函数和绑定方法。我们已经在这门课的开始创建了前者,而后者在方法调用时将对象和函数组合到一起。绑定方法的值已经将第一个函数关联到所调用的实例 ,当方法调用时实例会被命名为` self ` 。
198
198
199
199
通过在点运算符的返回值上调用` type ` ,我们可以在交互式解释器中看到它们的差异。作为类的属性,方法只是个函数,但是作为实例属性,它是绑定方法:
200
200
@@ -216,15 +216,15 @@ True
216
216
2011
217
217
```
218
218
219
- 函数` getattr ` 的表现就像运算符那样:它的第一个参数是对象,而名称是定义在类中的方法 。之后,` getattr ` 返回绑定方法的值。另一方面,如果第一个参数是个类,` getattr ` 会直接返回属性值,它仅仅是个函数。
219
+ 函数` getattr ` 的表现就像运算符那样:它的第一个参数是对象,而第二个参数(名称)是定义在类中的方法 。之后,` getattr ` 返回绑定方法的值。另一方面,如果第一个参数是个类,` getattr ` 会直接返回属性值,它仅仅是个函数。
220
220
221
221
** 实践指南:命名惯例。** 类名称通常以首字母大写来编写(也叫作驼峰拼写法,因为名称中间的大写字母像驼峰)。方法名称遵循函数命名的惯例,使用以下划线分隔的小写字母。
222
222
223
- 有的时候,有些实例变量和方法和对象的维护和一致性相关 ,我们不想让用户看到或使用它们。它们并不是由类定义的抽象的一部分,而是实现的一部分 。Python 的惯例规定,如果属性名称以下划线开始,它只能在方法或类中访问,而不是类的用户 。
223
+ 有的时候,有些实例变量和方法的维护和对象的一致性相关 ,我们不想让用户看到或使用它们。它们并不是由类定义的一部分抽象,而是一部分实现 。Python 的惯例规定,如果属性名称以下划线开始,它只能在方法或类中访问,而不能被类的用户访问 。
224
224
225
225
## 2.5.4 类属性
226
226
227
- 有些属性值在指定类的所有对象之间共享 。这样的属性关联到类本身,而不是类的任何独立实例。例如,让我们假设银行以固定的利率对余额支付利息。这个利率可能会改变,但是它是在所有账户中共享的单一值。
227
+ 有些属性值在特定类的所有对象之间共享 。这样的属性关联到类本身,而不是类的任何独立实例。例如,让我们假设银行以固定的利率对余额支付利息。这个利率可能会改变,但是它是在所有账户中共享的单一值。
228
228
229
229
类属性由` class ` 语句组中的赋值语句创建,位于任何方法定义之外。在更宽泛的开发者社群中,类属性也被叫做类变量或静态变量。下面的类语句以名称` interest ` 为` Account ` 创建了类属性。
230
230
248
248
0.02
249
249
```
250
250
251
- 但是,单一的对雷属性的赋值语句会改变所有该类实例上的属性值 。
251
+ 但是,对类属性的单一赋值语句会改变所有该类实例上的属性值 。
252
252
253
253
``` py
254
254
>> > Account.interest = 0.04
260
260
261
261
** 属性名称。** 我们已经在我们的对象系统中引入了足够的复杂性,我们需要规定名称如何解析为特定的属性。毕竟,我们可以轻易拥有同名的类属性和实例属性。
262
262
263
- 像我们看到的那样,点运算符,由表达式 、点和名称组成:
263
+ 像我们看到的那样,点运算符由表达式 、点和名称组成:
264
264
265
265
```
266
266
<expression> . <name>
273
273
3 . 如果` <name> ` 不存在于实例属性,那么会在类中查找` <name> ` ,这会产生类的属性值。
274
274
4 . 这个值会被返回,如果它是个函数,则会返回绑定方法。
275
275
276
- 在这个求值过程中,实例属性在类的属性之前查找,就像局部名称具有高于全局的优先级。定义在类中的方法,在求值过程的第三部绑定到了点运算符的对象上 。在类中查找名称的过程有额外的差异,在我们引入类继承的时候就会出现。
276
+ 在这个求值过程中,实例属性在类的属性之前查找,就像局部名称具有高于全局的优先级。定义在类中的方法,在求值过程的第三步绑定到了点运算符的对象上 。在类中查找名称的过程有额外的差异,在我们引入类继承的时候就会出现。
277
277
278
278
** 赋值。** 所有包含点运算符的赋值语句都会作用于右边的对象。如果对象是个实例,那么赋值就会设置实例属性。如果对象是个类,那么赋值会设置类属性。作为这条规则的结果,对对象属性的赋值不能影响类的属性。下面的例子展示了这个区别。
279
279
@@ -327,11 +327,11 @@ True
327
327
328
328
子类继承了基类的属性,但是可能覆盖特定属性,包括特定的方法。使用继承,我们只需要关注基类和子类之间有什么不同。任何我们在子类未指定的东西会自动假设和基类中相同。
329
329
330
- 继承也在对象隐喻中有重要作用,不仅仅是一种实用的组织特性 。继承意味着在类之间表达“is-a”关系,它和“has-a”关系相反。活期账户是(is-a)一种特殊类型的账户,所以让` CheckingAccount ` 继承` Account ` 是继承的合理使用。另一方面,银行拥有(has-a)所管理的银行账户的列表,所以二者都不应继承另一个。反之,账户对象的列表应该自然地表现为银行账户的实例属性。
330
+ 继承也在对象隐喻中有重要作用,不仅仅是一种实用的组织方式 。继承意味着在类之间表达“is-a”关系,它和“has-a”关系相反。活期账户是(is-a)一种特殊类型的账户,所以让` CheckingAccount ` 继承` Account ` 是继承的合理使用。另一方面,银行拥有(has-a)所管理的银行账户的列表,所以二者都不应继承另一个。反之,账户对象的列表应该自然地表现为银行账户的实例属性。
331
331
332
332
## 2.5.6 使用继承
333
333
334
- 我们通过将放置到类名称后面的圆括号内来指定继承 。首先,我们提供` Account ` 类的特定完整实现 ,也包含类和方法的文档字符串。
334
+ 我们通过将基类放置到类名称后面的圆括号内来指定继承 。首先,我们提供` Account ` 类的完整实现 ,也包含类和方法的文档字符串。
335
335
336
336
``` py
337
337
>> > class Account (object ):
363
363
return Account.withdraw(self , amount + self .withdraw_charge)
364
364
```
365
365
366
- 这里,我们引入了类属性` withdraw_charge ` ,它特定于` CheckingAccount ` 类。我们将一个更低的值赋给` interest ` 属性。我们也定义了新的` withdraw ` 方法来覆盖定义在` Account ` 对象中国的行为 。类语句组中没有更多的语句,所有其它行为都从基类` Account ` 中继承。
366
+ 这里,我们引入了类属性` withdraw_charge ` ,它特定于` CheckingAccount ` 类。我们将一个更低的值赋给` interest ` 属性。我们也定义了新的` withdraw ` 方法来覆盖定义在` Account ` 对象中的行为 。类语句组中没有更多的语句,所有其它行为都从基类` Account ` 中继承。
367
367
368
368
``` py
369
369
>> > checking = CheckingAccount(' Sam' )
375
375
0.01
376
376
```
377
377
378
- ` checking.deposit ` 表达式为用于存款的绑定方法 ,它定义在` Account ` 类中,当 Python 解析点表达式中的名称时,实例上并没有这个属性,它会在类中查找该名称。实际上,在类中“查找名称”会在原始对象的类的继承链中的每个基类中查找 。我们可以递归定义这个过程,为了在类中查找名称:
378
+ ` checking.deposit ` 表达式是用于存款的绑定方法 ,它定义在` Account ` 类中,当 Python 解析点表达式中的名称时,实例上并没有这个属性,它会在类中查找该名称。实际上,在类中“查找名称”的行为会在原始对象的类的继承链中的每个基类中查找 。我们可以递归定义这个过程,为了在类中查找名称:
379
379
380
380
1 . 如果类中有带有这个名称的属性,返回属性值。
381
381
2 . 否则,如果有基类的话,在基类中查找该名称。
@@ -412,7 +412,7 @@ Python 支持子类从多个基类继承属性的概念,这是一种叫做多
412
412
self .balance = 1 # A free dollar!
413
413
```
414
414
415
- 实际上,这个实现就完整了。存款和取款都需要费用,使用定义在 ` CheckingAccount ` 和` SavingsAccount ` 中的相应函数。
415
+ 实际上,这个实现就完整了。存款和取款都需要费用,使用了定义在 ` CheckingAccount ` 和` SavingsAccount ` 中的相应函数。
416
416
417
417
``` py
418
418
>> > such_a_deal = AsSeenOnTVAccount(" John" )
@@ -433,17 +433,17 @@ Python 支持子类从多个基类继承属性的概念,这是一种叫做多
433
433
1
434
434
```
435
435
436
- 但是如果引用有歧义呢,比如` withdraw ` 方法的引用定义在 ` Account ` 和` CheckingAccount ` 中?下面的图展示了` AsSeenOnTVAccount ` 类的继承图。每个箭头都从子类指向基类。
436
+ 但是如果引用有歧义呢,比如` withdraw ` 方法的引用,它定义在 ` Account ` 和` CheckingAccount ` 中?下面的图展示了` AsSeenOnTVAccount ` 类的继承图。每个箭头都从子类指向基类。
437
437
438
438
![ ] ( img/multiple_inheritance.png )
439
439
440
- 对于像这样的简单“菱形”,Python 从做到右解析名称 ,之后向上。这个例子中,Python 按下列顺序检查名称,直到找到了具有该名称的属性:
440
+ 对于像这样的简单“菱形”,Python 从左到右解析名称 ,之后向上。这个例子中,Python 按下列顺序检查名称,直到找到了具有该名称的属性:
441
441
442
442
```
443
443
AsSeenOnTVAccount, CheckingAccount, SavingsAccount, Account, object
444
444
```
445
445
446
- 对于继承顺序的问题没有正确的解法 ,因为我们可能会给某个派生类高于其他类的优先级。但是,任何致辞多重继承的编程语言必须始终选择同一个顺序 ,便于语言的用户预测程序的行为。
446
+ 继承顺序的问题没有正确的解法 ,因为我们可能会给某个派生类高于其他类的优先级。但是,任何支持多重继承的编程语言必须始终选择同一个顺序 ,便于语言的用户预测程序的行为。
447
447
448
448
** 扩展阅读。** Python 使用一种叫做 C3 Method Resolution Ordering 的递归算法来解析名称。任何类的方法解析顺序都使用所有类上的` mro ` 方法来查询。
449
449
@@ -452,7 +452,7 @@ AsSeenOnTVAccount, CheckingAccount, SavingsAccount, Account, object
452
452
[' AsSeenOnTVAccount' , ' CheckingAccount' , ' SavingsAccount' , ' Account' , ' object' ]
453
453
```
454
454
455
- 这个用于发现方法解析顺序的算法并不是这门课的主题 ,但是 Python 的原作者使用一篇[ 原文章的引用] ( http://python-history.blogspot.com/2010/06/method-resolution-order.html ) 来描述它。
455
+ 这个用于查询方法解析顺序的算法并不是这门课的主题 ,但是 Python 的原作者使用一篇[ 原文章的引用] ( http://python-history.blogspot.com/2010/06/method-resolution-order.html ) 来描述它。
456
456
457
457
## 2.5.8 对象的作用
458
458
@@ -465,4 +465,3 @@ Python 对象系统为使数据抽象和消息传递更加便捷和灵活而设
465
465
另一方面,类可能不会提供用于实现特定的抽象的最佳机制。函数式抽象提供了更加自然的隐喻,用于表现输入和输出的关系。一个人不应该强迫自己把程序中的每个细微的逻辑都塞到类里面,尤其是当定义独立函数来操作数据变得十分自然的时候。函数也强制了关注分离。
466
466
467
467
类似 Python 的多范式语言允许程序员为合适的问题匹配合适的范式。为了简化程序,或使程序模块化,确定何时引入新的类,而不是新的函数,是软件工程中的重要设计技巧,这需要仔细关注。
468
-
0 commit comments