9.4. 补充说明

如果同样的属性名称同时出现在实例和类中,则属性查找会优先选择实例:

  1. >>> class Warehouse:
  2. purpose = 'storage'
  3. region = 'west'
  4.  
  5. >>> w1 = Warehouse()
  6. >>> print(w1.purpose, w1.region)
  7. storage west
  8. >>> w2 = Warehouse()
  9. >>> w2.region = 'east'
  10. >>> print(w2.purpose, w2.region)
  11. storage east

数据属性可以被方法以及一个对象的普通用户(“客户端”)所引用。 换句话说,类不能用于实现纯抽象数据类型。 实际上,在 Python 中没有任何东西能强制隐藏数据 —- 它是完全基于约定的。 (而在另一方面,用 C 语言编写的 Python 实现则可以完全隐藏实现细节,并在必要时控制对象的访问;此特性可以通过用 C 编写 Python 扩展来使用。)

客户端应当谨慎地使用数据属性 —- 客户端可能通过直接操作数据属性的方式破坏由方法所维护的固定变量。 请注意客户端可以向一个实例对象添加他们自己的数据属性而不会影响方法的可用性,只要保证避免名称冲突 —- 再次提醒,在此使用命名约定可以省去许多令人头痛的麻烦。

在方法内部引用数据属性(或其他方法!)并没有简便方式。 我发现这实际上提升了方法的可读性:当浏览一个方法代码时,不会存在混淆局部变量和实例变量的机会。

方法的第一个参数常常被命名为 self。 这也不过就是一个约定: self 这一名称在 Python 中绝对没有特殊含义。 但是要注意,不遵循此约定会使得你的代码对其他 Python 程序员来说缺乏可读性,而且也可以想像一个 类浏览器 程序的编写可能会依赖于这样的约定。

任何一个作为类属性的函数都为该类的实例定义了一个相应方法。 函数定义的文本并非必须包含于类定义之内:将一个函数对象赋值给一个局部变量也是可以的。 例如:

  1. # Function defined outside the class
  2. def f1(self, x, y):
  3. return min(x, x+y)
  4.  
  5. class C:
  6. f = f1
  7.  
  8. def g(self):
  9. return 'hello world'
  10.  
  11. h = g

现在 f, gh 都是 C 类的引用函数对象的属性,因而它们就都是 C 的实例的方法 —- 其中 h 完全等同于 g。 但请注意,本示例的做法通常只会令程序的阅读者感到迷惑。

方法可以通过使用 self 参数的方法属性调用其他方法:

  1. class Bag:
  2. def __init__(self):
  3. self.data = []
  4.  
  5. def add(self, x):
  6. self.data.append(x)
  7.  
  8. def addtwice(self, x):
  9. self.add(x)
  10. self.add(x)

方法可以通过与普通函数相同的方式引用全局名称。 与方法相关联的全局作用域就是包含其定义的模块。 (类永远不会被作为全局作用域。) 虽然我们很少会有充分的理由在方法中使用全局作用域,但全局作用域存在许多合法的使用场景:举个例子,导入到全局作用域的函数和模块可以被方法所使用,在其中定义的函数和类也一样。 通常,包含该方法的类本身是在全局作用域中定义的,而在下一节中我们将会发现为何方法需要引用其所属类的很好的理由。

每个值都是一个对象,因此具有 (也称为 类型),并存储为 object.class