9. Classes 类

Python’s class mechanism adds classes to the language with a minimum of new syntax and semantics. It is a mixture of the class mechanisms found in C++ and Modula-3. As is true for modules, classes in Python do not put an absolute barrier between definition and user, but rather rely on the politeness of the user not to “break into the definition.” The most important features of classes are retained with full power, however: the class inheritance mechanism allows multiple base classes, a derived class can override any methods of its base class or classes, and a method can call the method of a base class with the same name. Objects can contain an arbitrary amount of private data.

Python的类机制通过最小的新语法和语义在语言中实现了类。 它是C++何Modula-3语言中类机制的混合。 就像模块一样,Python的类并没有在用户和定义之间设立绝对的屏障,而是依赖于用户不去“强行闯入定义”的优雅。 另一方面,类的大多数重要特性都被完整的保留下来:类继承机制允许多重继承,派生类可以覆盖(override)基类中的任何方法或类,可以使用相同的方法名称调用基类的方法。 对象可以包含任意数量的私有数据。

In C++ terminology, normally class members (including the data members) are public (except see below Private Variables 私有变量), and all member functions are virtual. There are no special constructors or destructors. As in Modula-3, there are no shorthands for referencing the object’s members from its methods: the method function is declared with an explicit first argument representing the object, which is provided implicitly by the call. As in Smalltalk, classes themselves are objects, albeit in the wider sense of the word: in Python, all data types are objects. This provides semantics for importing and renaming. Unlike C++ and Modula-3, built-in types can be used as base classes for extension by the user. Also, like in C++ but unlike in Modula-3, most built-in operators with special syntax (arithmetic operators, subscripting etc.) can be redefined for class instances.

用在C++中的术语讲,普通的类成员(包括数据成员)都是 公有 的(public)(除了下面提到的 Private Variables 私有变量 ),并且所有的成员函数都是 的(virtual)。 类并没有特殊的构造器和析构器。 和在Modula-3中一样,从方法中没有什么简洁的方式可以引用其对象成员,函数方法必须以代表对象的标识符(self)作为第一个明确的参数,在调用时被隐式的提供。 和在Smalltalk中一样,类本身就是对象,从更广发的意义上讲:在Python中,所有的数据类型都是对象。 这就为导入和重命名提供了支持。 不似C++和Modula-3那样,内置类型可以被用户用作基类进行扩展。 并且像在C++中一样,而不是Modula-3,所有内置带有特殊语法的操作符(算术操作符,下标操作符等)都可以针对类的实例进行重定义。

9.1. A Word About Terminology 术语相关

Lacking universally accepted terminology to talk about classes, I will make occasional use of Smalltalk and C++ terms. (I would use Modula-3 terms, since its object-oriented semantics are closer to those of Python than C++, but I expect that few readers have heard of it.)

关于类因为缺少普遍的可以接受的术语,我暂时借用Smalltalk和C++中的术语。 (我更想使用Modula-3的术语,因为它的面向对象机制比C++更接近Python,但我想几乎没人听说过它)。

Objects have individuality, and multiple names (in multiple scopes) can be bound to the same object. This is known as aliasing in other languages. This is usually not appreciated on a first glance at Python, and can be safely ignored when dealing with immutable basic types (numbers, strings, tuples). However, aliasing has an (intended!) effect on the semantics of Python code involving mutable objects such as lists, dictionaries, and most types representing entities outside the program (files, windows, etc.). This is usually used to the benefit of the program, since aliases behave like pointers in some respects. For example, passing an object is cheap since only a pointer is passed by the implementation; and if a function modifies an object passed as an argument, the caller will see the change — this eliminates the need for two different argument passing mechanisms as in Pascal.

对象具有特性,并且多个名称(在多个作用于中)可以绑定在同一个对象上。 这在其它语言中被称为别名。 在对Python的第一印象中这通常会被忽略,并且当处理不可变基础类型(数字,字符串,元组)时可以被放心的忽略。 但是,在调用列表、字典这类可变对象,或者大多数程序外部类型(文件,窗体等)描述实体时,别名对Python代码的语义便具有(有意而为!)影响。 这通常有助于程序的优化,因为在某些方面别名表现的就像是指针。 例如,你可以轻易的传递一个对象,因为通过继承只是传递一个指针。 并且如果一个方法修改了一个作为参数传递的对象,调用者可以接收这一变化——这消除了两种不同的参数传递机制的需要,像Pascal语言。

9.2. Python Scopes and Name Spaces Python作用域和命名空间

Before introducing classes, I first have to tell you something about Python’s scope rules. Class definitions play some neat tricks with namespaces, and you need to know how scopes and namespaces work to fully understand what’s going on. Incidentally, knowledge about this subject is useful for any advanced Python programmer.

在介绍类(class)之前,我必须先告诉你一些Python作用域的规则。 类定义非常巧妙的运用了命名空间,并且要想探求究竟你需要知道作用域和命名空间是如何工作的。 顺便说一句,这个主题的相关知识对任何高级Python程序员非常重要。

Let’s begin with some definitions.

让我们从一些定义说起。

A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that’s normally not noticeable in any way (except for performance), and it may change in the future. Examples of namespaces are: the set of built-in names (functions such as abs(), and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is absolutely no relation between names in different namespaces; for instance, two different modules may both define a function “maximize” without confusion — users of the modules must prefix it with the module name.

命名空间 就是一个从名称到对象的映射。 大多数命名空间目前都被实现为Python字典,但那通常不会被注意(除非为了性能考虑),并且在将来它可能会被改变。 命名空间的一些实例:内置名称集(函数,像 abs() ,和内置异常名称),一个模块中的全局名称,函数调用时的局部名称。 某种意义上讲,一个对象的属性集合也构成了一个命名空间。 关于命名空间需要了解的一件重要的事情就是在不同命名空间中的名称之间没有任何的关系。 比如,两个不同的模块可能都定义了一个“maximize”函数而不会混淆——用户必须使用模块名为前缀(来使用模块)。

By the way, I use the word attribute for any name following a dot — for example, in the expression z.real, real is an attribute of the object z. Strictly speaking, references to names in modules are attribute references: in the expression modname.funcname, modname is a module object and funcname is an attribute of it. In this case there happens to be a straightforward mapping between the module’s attributes and the global names defined in the module: they share the same namespace! [1]

顺便说一下,在Python中我习惯将任何跟在一个点(.)后的名称叫作 属性 。 列如,在 z.real 表达式中, real 就是对象 z 的一个属性。 严格来讲,从模块中引用名称就是引用其属性:在 modname.funcname 表达式中, modname 是一个模块对象,同时 funcname 就是它的一个属性。 以此而论,在模块属性和模块内部定义的全局名称之间恰好是直接的映射:它们共享相同的命名空间![#]_

Attributes may be read-only or writable. In the latter case, assignment to attributes is possible. Module attributes are writable: you can write modname.the_answer = 42. Writable attributes may also be deleted with the del statement. For example, del modname.the_answer will remove the attribute the_answer from the object named by modname.

属性可以是只读的或可写的。 在后一种情况下(指可写),允许对属性赋值。 模块属性都是可写的:你可以这样使用 modname.the_answer = 42 。 可写属性也可以使用 del 语句删除。 例如, del modname.the_answer 就会从名为 modname 的对象中删除 the_answer 属性。

Name spaces are created at different moments and have different lifetimes. The namespace containing the built-in names is created when the Python interpreter starts up, and is never deleted. The global namespace for a module is created when the module definition is read in; normally, module namespaces also last until the interpreter quits. The statements executed by the top-level invocation of the interpreter, either read from a script file or interactively, are considered part of a module called __main__, so they have their own global namespace. (The built-in names actually also live in a module; this is called builtins.)

命名空间是在不同的时刻创建的,并且具有不同的生命周期。 包含内置名称的命名空间在Python解释器启动时即被创建,并且从不会被删除。 模块的全局命名空间在模块定义被读取时即被创建,通常模块的命名空间会一直保存到解释器退出。 通过解释器顶层调用执行的语句,不论是交互的还是从脚本文件读取的,都被认为是 __main__ 模块的一部分,因此他们也有自己的全局命名空间。 (内置名称实际上也存在于一个模块,称为: builtins 。)

The local namespace for a function is created when the function is called, and deleted when the function returns or raises an exception that is not handled within the function. (Actually, forgetting would be a better way to describe what actually happens.) Of course, recursive invocations each have their own local namespace.

当调用函数时,就会为它创建一个局部命名空间,并且在函数返回或抛出一个并没有在函数内部处理的异常时被删除。 (实际上,用遗忘来形容到底发生了什么更为贴切。) 当然,每个递归调用都有自己的局部命名空间。

A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

作用域 就是一个Python程序可以直接访问命名空间的正文区域。 这里的 直接访问 意思是一个对名称的错误引用会尝试在命名空间内查找。

Although scopes are determined statically, they are used dynamically. At any time during execution, there are at least three nested scopes whose namespaces are directly accessible: the innermost scope, which is searched first, contains the local names; the namespaces of any enclosing functions, which are searched starting with the nearest enclosing scope; the middle scope, searched next, contains the current module’s global names; and the outermost scope (searched last) is the namespace containing built-in names.

尽管作用域都是静态定义,但它们都被动态的使用。 在执行的任何时刻,至少有三个命名空间可以直接访问的嵌套作用域: 最先被查找的最内层作用域,包含局部名称; 所有封闭函数的命名空间被从最近的封闭作用域内开始搜索; 其次被查找的中间层作用域,包含当前模块的全局名称; 最后被查找的最外层作用域,包含内建名称的命名空间。

If a name is declared global, then all references and assignments go directly to the middle scope containing the module’s global names. To rebind variables found outside of the innermost scope, the nonlocal statement can be used; if not declared nonlocal, those variable are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

如果一个名称被声明为全局的,那么所有的赋值和引用都会直接从包含模块全局名称的中层作用域开始。 那些在最内层作用域以外的变量,可以使用 nonlocal 语句重新绑定,如果没有声明为 nonlocal 它们则是只读的。 (试图改变这样的变量只会在最内层作用域中简单的创建一个 的局部变量,而外部那个相同标识符的变量不会改变)

Usually, the local scope references the local names of the (textually) current function. Outside functions, the local scope references the same namespace as the global scope: the module’s namespace. Class definitions place yet another namespace in the local scope.

通常,局部作用域引用当前函数(正文的)的局部名称。 在函数外部,局部作用域将这一命名空间看做全局作用域:模块命名空间。 类定义也会在局部作用域引入另一个命名空间。

It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)

重要的是要理解作用域是由正文确定: 在模块中定义的函数的全局作用域是该模块的命名空间,而不论从何处或者通过什么别名调用函数。 换句话说,对名称的实际搜索是在运行时动态完成的。 然而,Python语言的定义正朝着“编译”时静态名称确定进化,因此不要依赖动态名称确定! (事实上,局部变量已经是静态确定的。)

A special quirk of Python is that – if no global or nonlocal statement is in effect – assignments to names always go into the innermost scope. Assignments do not copy data — they just bind names to objects. The same is true for deletions: the statement del x removes the binding of x from the namespace referenced by the local scope. In fact, all operations that introduce new names use the local scope: in particular, import statements and function definitions bind the module or function name in the local scope. (The global statement can be used to indicate that particular variables live in the global scope.)

Python的一个特别之处就是对名称的赋值总是在最内层作用域内,当然含有 globalnonlocal 语句的除外。 赋值操作并不会拷贝数据——他们只是将名称绑定到对象。 删除操作亦是如此:语句 del x 从局部作用域引用的命名空间中删除名称``x``的绑定。 实际上,所有引入新名称的操作都是用局部作用域:特别是 import 语句和函数定义语句,它们将模块或函数名称绑定在局部作用域。 (可以用 global 语句指明某个特定的变量为全局作用域。)

The global statement can be used to indicate that particular variables live in the global scope and should be rebound there; the nonlocal statement indicates that particular variables live in an enclosing scope and should be rebound there.

global 语句用以指明某个特定的变量为全局作用域,并重新绑定它。 nonlocal 语句用以指明某个特定的变量为封闭作用域,并重新绑定它。

9.2.1. Scopes and Namespaces Example 作用域和命名空间示例

This is an example demonstrating how to reference the different scopes and namespaces, and how global and nonlocal affect variable binding:

以下是一个示例,演示了如何引用不同作用域和命名空间,以及 globalnonlocal 如何影响变量绑定:

def scope_test():
    def do_local():
        spam = "local spam"
    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"
    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

The output of the example code is:

以上示例代码的输出为:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

Note how the local assignment (which is default) didn’t change scope_test‘s binding of spam. The nonlocal assignment changed scope_test‘s binding of spam, and the global assignment changed the module-level binding.

注意: local 赋值语句是无法改变 scope_testspam 绑定。 nonlocal 赋值语句改变了 scope_testspam 绑定,并且 global 赋值语句从模块级改变了 spam 绑定。

You can also see that there was no previous binding for spam before the global assignment.

你也可以看到在 global 赋值语句之前对 spam 是没有预先绑定的。

9.3. A First Look at Classes 初识类

Classes introduce a little bit of new syntax, three new object types, and some new semantics.

类引入了一些新语法:三种新的对象类型和一些新的语义。

9.3.1. Class Definition Syntax 类定义语法

The simplest form of class definition looks like this:

类定义最简单的形式如下:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

Class definitions, like function definitions (def statements) must be executed before they have any effect. (You could conceivably place a class definition in a branch of an if statement, or inside a function.)

类定义,就像函数定义( def 语句)一样,必须先执行才能生效。 (你当然可以在一条 if 语句分支或一个函数内部定义一个类。)

In practice, the statements inside a class definition will usually be function definitions, but other statements are allowed, and sometimes useful — we’ll come back to this later. The function definitions inside a class normally have a peculiar form of argument list, dictated by the calling conventions for methods — again, this is explained later.

实践中,类定义内部的语句通常都是函数定义,但也允许包含其他语句,有时这非常有用——稍后我们将对此介绍。 类内部的函数定义通常有一个特殊形式的参数列表,用于方法调用约定——稍后我们也将对此介绍。

When a class definition is entered, a new namespace is created, and used as the local scope — thus, all assignments to local variables go into this new namespace. In particular, function definitions bind the name of the new function here.

当进入类定义时,就会创建一个新的命名空间,并且用作局部作用域。 因此,所有对局部变量的赋值都会在这个新命名空间内进行。 特别的,函数定义就是将新函数的名称绑定在此。

When a class definition is left normally (via the end), a class object is created. This is basically a wrapper around the contents of the namespace created by the class definition; we’ll learn more about class objects in the next section. The original local scope (the one in effect just before the class definition was entered) is reinstated, and the class object is bound here to the class name given in the class definition header (ClassName in the example).

当类定义完成时(正常结束),就创建了一个 类对象 。 这是一个在类定义创建的命名空间内容周围的基本包装,我们将在下一节中学习更多关于类对象的知识。 原来的局部(在进入类定义之前生效的那个)作用域得以恢复,并且类对象在这被绑定到类定义头部指定的类名称(参考 ClassName 示例)。

9.3.2. Class Objects 类对象

Class objects support two kinds of operations: attribute references and instantiation.

类对象支持两种操作:属性引用和实例化。

Attribute references use the standard syntax used for all attribute references in Python: obj.name. Valid attribute names are all the names that were in the class’s namespace when the class object was created. So, if the class definition looked like this:

属性引用 使用在Python中所有属性引用一样的标准语法: obj.name 。 当创建类对象时,所有在类命名空间中的名称都是有效的属性名。 因此,如果定义一个这样的类:

class MyClass:
    """A simple example class"""
    i = 12345
    def f(self):
        return 'hello world'

then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively. Class attributes can also be assigned to, so you can change the value of MyClass.i by assignment. __doc__ is also a valid attribute, returning the docstring belonging to the class: "A simple example class".

那么, MyClass.iMyClass.f 都是有效的属性引用,分别反馈一个整数和一个函数对象。 也可以对类属性进行赋值,所以你可以通过给 MyClass.i 赋值来修改它。 __doc__ 也是一个有效的属性,返回类的 docstring (文档字符串): "A simple example class"

Class instantiation uses function notation. Just pretend that the class object is a parameterless function that returns a new instance of the class. For example (assuming the above class):

类的 实例化 使用函数(调用)表示法。 只要将类对象看作是一个返回新的类实例的无参函数。 例如(假设实例化上面的类)

x = MyClass()

creates a new instance of the class and assigns this object to the local variable x.

创建一个类的新 实例 ,并将其赋值给一个局部变量 x

The instantiation operation (“calling” a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named __init__(), like this:

这个实例化操作(“调用”一个类对象)会将建一个空对象。 很多类都喜欢创建含有特别的自定义初始化状态的实例对象。 因而,你可以定义一个包含特殊方法 __init__() 的类,像下面这样:

def __init__(self):
    self.data = []

When a class defines an __init__() method, class instantiation automatically invokes __init__() for the newly-created class instance. So in this example, a new, initialized instance can be obtained by:

当一个类定义了 __init__() 方法时,类实例化会为新创建的类实例自动调用 __init__() 方法。 所以,在这个例子中,可以获得一个初始化的新实例:

x = MyClass()

Of course, the __init__() method may have arguments for greater flexibility. In that case, arguments given to the class instantiation operator are passed on to __init__(). For example,

当然,为了更好的灵活性 __init__() 方法可以包含参数。 如果那样的话,类实例化操作时给出的参数都会传递给 __init__() 方法。例如:

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

9.3.3. Instance Objects 实例对象

Now what can we do with instance objects? The only operations understood by instance objects are attribute references. There are two kinds of valid attribute names, data attributes and methods.

现在,我们可以用实例对象做什么呢? 实例对象唯一可用的操作就是属性引用。 这里有两种有效的属性名称:数据属性(字段)和方法。

data attributes correspond to “instance variables” in Smalltalk, and to “data members” in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. For example, if x is the instance of MyClass created above, the following piece of code will print the value 16, without leaving a trace.

数据属性 相当于Smalltalk中的“实例变量”,或者C++中的“数据成员”。 数据属性无需声明。 和局部变量一样,它们在第一次赋值时就会产生。 例如:如果 x 是上面创建的类 MyClass 的实例,那么下面的代码片段将会打印出 16 这个值而没有任何错误。

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter)
del x.counter

The other kind of instance attribute reference is a method. A method is a function that “belongs to” an object. (In Python, the term method is not unique to class instances: other object types can have methods as well. For example, list objects have methods called append, insert, remove, sort, and so on. However, in the following discussion, we’ll use the term method exclusively to mean methods of class instance objects, unless explicitly stated otherwise.)

另外一种实例属性引用就是 方法 。 方法即使“属于”某个对象的函数。 (在Python中,术语“方法”不仅存在于类实例,其他对象类型也包含方法。 例如,列表对象含有append,insert,remove,sort等方法。 然而,在下面的讨论中,除非特别说明,我们所用得术语“方法”专指类实例对象的方法。)

Valid method names of an instance object depend on its class. By definition, all attributes of a class that are function objects define corresponding methods of its instances. So in our example, x.f is a valid method reference, since MyClass.f is a function, but x.i is not, since MyClass.i is not. But x.f is not the same thing as MyClass.f — it is a method object, not a function object.

一个实例对象的方法名是否有效取决于它的类。 按照定义,一个类中所有函数对象定义与它的实例方法是相对应的。 因此在我们的例子中, x.f 是一个有效的方法引用,因为 MyClass.f 是一个方法; 但是 x.i 则不是,因为 MyClass.i 不是一个方法。 然而 x.fMyClass.f 并不是相同的东西——它是一个 方法对象 ,而非函数对象。

9.3.4. Method Objects 方法对象

Usually, a method is called right after it is bound.

通常,方法通过右绑定方式调用。

x.f()

In the MyClass example, this will return the string 'hello world'. However, it is not necessary to call a method right away: x.f is a method object, and can be stored away and called at a later time. For example:

MyClass 示例中,这将会返回 'hello world' 字符串。 然而,你无需立刻就调用一个方法: x.f 是一个方法对象,并且它可以被保存起来以便稍后调用。 比如:

xf = x.f
while True:
    print(xf())

will continue to print hello world until the end of time.

会不断打印 hello world 字符串,直到程序终止。

What exactly happens when a method is called? You may have noticed that x.f() was called without an argument above, even though the function definition for f() specified an argument. What happened to the argument? Surely Python raises an exception when a function that requires an argument is called without any — even if the argument isn’t actually used...

当调用一个方法是具体做了什么呢? 你可能已经注意到上面我们调用 x.f() 时并没有使用参数,尽管在 f() 的定义中指定了一个参数。 这个参数怎么了呢? 当然,当不使用任何参数调用需要一个参数的函数时,Python就会抛出一个异常——即使这个参数没有实际使用…

Actually, you may have guessed the answer: the special thing about methods is that the object is passed as the first argument of the function. In our example, the call x.f() is exactly equivalent to MyClass.f(x). In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method’s object before the first argument.

实际上,你可能已经猜到了答案:方法有一个特性就是实例对象被当做第一个参数传递给了函数。 在我们的例子中, x.f() 调用实际上等价于 MyClass.f(x) 调用。 通常,使用包含 n 个参数的列表调用一个方法,相当于使用通过将方法的对象插入到参数列表第一个参数前面后创建的参数列表调用相应的函数。

If you still don’t understand how methods work, a look at the implementation can perhaps clarify matters. When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, it is unpacked again, a new argument list is constructed from the instance object and the original argument list, and the function object is called with this new argument list.

如果你还不理解方法是如何工作的,了解一下它的实现可能会明白真相。 当引用一个非数据属性的实例属性时,就会搜索它的类。 如果这个名称表示一个有效的函数对象类属性,就会将实例对象和函数对象封装(用指针指向)进一个抽象对象,从而创建一个方法对象:这就是方法对象。 当使用一个参数列表调用方法对象时,它会被重新拆封,用实例对象和原始参数列表构造一个新的参数列表,然后用这个新的参数列表调用函数对象。

9.4. Random Remarks 一些说明

Data attributes override method attributes with the same name; to avoid accidental name conflicts, which may cause hard-to-find bugs in large programs, it is wise to use some kind of convention that minimizes the chance of conflicts. Possible conventions include capitalizing method names, prefixing data attribute names with a small unique string (perhaps just an underscore), or using verbs for methods and nouns for data attributes.

数据属性会覆盖同名的方法属性。 为了避免意外的名称冲突,这在大型程序中是极难发现的Bug,使用一些约定来减少冲突的机会是明智的。 可能的约定包括:大写方法名称的首字母,使用一个唯一的小字符串(也许只是一个下划线)作为数据属性名称的前缀,或者方法使用动词而数据属性使用名词。

Data attributes may be referenced by methods as well as by ordinary users (“clients”) of an object. In other words, classes are not usable to implement pure abstract data types. In fact, nothing in Python makes it possible to enforce data hiding — it is all based upon convention. (On the other hand, the Python implementation, written in C, can completely hide implementation details and control access to an object if necessary; this can be used by extensions to Python written in C.)

数据属性可以被方法引用,也可以由一个对象的普通用户(客户)使用。 换句话说,类不能用来实现纯净的数据类型。 事实上,Python中不可能强制隐藏数据——一切基于约定。 (如果需要,使用C编写的Python实现可以完全隐藏实现细节并控制对象的访问。这可以用来通过C语言扩展Python。)

Clients should use data attributes with care — clients may mess up invariants maintained by the methods by stamping on their data attributes. Note that clients may add data attributes of their own to an instance object without affecting the validity of the methods, as long as name conflicts are avoided — again, a naming convention can save a lot of headaches here.

客户应该谨慎的使用数据属性——客户可能通过践踏他们的数据属性而使那些由方法维护的常量变得混乱。 注意:只要能避免冲突,客户可以向一个实例对象添加他们自己的数据属性,而不会影响方法的正确性——再次强调,命名约定可以避免很多麻烦。

There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.

从方法内部引用数据属性(或其他方法)并没有快捷方式。 我觉得这实际上增加了方法的可读性:当浏览一个方法时,在局部变量和实例变量之间不会出现令人费解的情况。

Often, the first argument of a method is called self. This is nothing more than a convention: the name self has absolutely no special meaning to Python. (Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.)

一般,方法的第一个参数被命名为 self 。 这仅仅是一个约定:对Python而言,名称 self 绝对没有任何特殊含义。 (但是请注意:如果不遵循这个约定,对其他的Python程序员而言你的代码可读性就会变差,而且有些 类查看器 程序也可能是遵循此约定编写的。)

Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok. For example:

类属性的任何函数对象都为那个类的实例定义了一个方法。 函数定义代码不一定非得定义在类中:也可以将一个函数对象赋值给类中的一个局部变量。 例如:

# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'hello world'
    h = g

Now f, g and h are all attributes of class C that refer to function objects, and consequently they are all methods of instances of Ch being exactly equivalent to g. Note that this practice usually only serves to confuse the reader of a program.

现在, fgh 都是指向函数对象的类 C 的属性,因此它们都是 C 实例的方法—— h 严格等于 g 。 注意:这种习惯通常只会让程序的读者迷惑。

Methods may call other methods by using method attributes of the self argument.

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

class Bag:
    def __init__(self):
        self.data = []
    def add(self, x):
        self.data.append(x)
    def addtwice(self, x):
        self.add(x)
        self.add(x)

Methods may reference global names in the same way as ordinary functions. The global scope associated with a method is the module containing the class definition. (The class itself is never used as a global scope!) While one rarely encounters a good reason for using global data in a method, there are many legitimate uses of the global scope: for one thing, functions and modules imported into the global scope can be used by methods, as well as functions and classes defined in it. Usually, the class containing the method is itself defined in this global scope, and in the next section we’ll find some good reasons why a method would want to reference its own class!

方法可以像普通函数一样引用全局名称。 方法可见的全局作用域是包含此类定义的模块。(类自身永远不会作为全局作用域!) 虽然在方法中使用全局数据只有极少好的理由,还是有很多合法的用法使用全局作用域: 首先,在全局作用域中导入的模块和方法可以被方法使用,也可以调用其中定义的类和函数。 通常,包含此方法的类也会在这个全局作用域中被定义。 在下一节中,我们会了解为何一个方法想要引用自己的类!

Each value is an object, and therefore has a class (also called its type). It is stored as object.__class__.

Python中,每个值都是一个对象,并具有一个*类*(称作它的*类型*)。 这被存储为``object.__class__``。

9.5. Inheritance 继承

Of course, a language feature would not be worthy of the name “class” without supporting inheritance. The syntax for a derived class definition looks like this:

当然,如果一种语言不支持继承的特性,那么 就没有什么意义。 派生类的定义语法如下所示:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>

The name BaseClassName must be defined in a scope containing the derived class definition. In place of a base class name, other arbitrary expressions are also allowed. This can be useful, for example, when the base class is defined in another module.

名称 BaseClassName 必须定义在包含派生类定义的作用域内。 在基类名称的位置,允许出现任何表达式。 当基类在另一个模块中定义时,这种做法非常有用。比如:

class DerivedClassName(modname.BaseClassName):

Execution of a derived class definition proceeds the same as for a base class. When the class object is constructed, the base class is remembered. This is used for resolving attribute references: if a requested attribute is not found in the class, the search proceeds to look in the base class. This rule is applied recursively if the base class itself is derived from some other class.

派生类定义的执行过程和基类是相同的。 当构造类对象时,基类就会被记住。 这在解析属性引用时被使用:如果一个请求的属性在类中没有找到,就会继续在基类中查找。 如果基类自身也是从其他类派生而来,这个规则就会被递归的应用。

There’s nothing special about instantiation of derived classes: DerivedClassName() creates a new instance of the class. Method references are resolved as follows: the corresponding class attribute is searched, descending down the chain of base classes if necessary, and the method reference is valid if this yields a function object.

派生类的实例化并没有什么特别之处: DerivedClassName() 调用会为类创建一个新的实例。 方法引用按照如下决定:搜索相应的类属性,必要时沿着基类链逐层搜索,如果发现一个函数对象这个方法引用就是有效的。

Derived classes may override methods of their base classes. Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class may end up calling a method of a derived class that overrides it. (For C++ programmers: all methods in Python are effectively virtual.)

派生类可能会覆盖它们基类的方法。 因为方法没有任何特权,所以本意想要调用基类中的另一个方法时,可能会被派生类中覆盖它的方法终止。 (对于C++程序员:Python中所有的方法实际上都是 virtual 的。)

An overriding method in a derived class may in fact want to extend rather than simply replace the base class method of the same name. There is a simple way to call the base class method directly: just call BaseClassName.methodname(self, arguments). This is occasionally useful to clients as well. (Note that this only works if the base class is defined or imported directly in the global scope.)

在派生类中覆盖方法,实际上可能是想要扩展基类中同名的方法,而非简单的替代。 有一个简单的方法可以直接调用基类中的方法:只需调用 BaseClassName.methodname(self, arguments) 。 偶尔,这也会对客户有所帮助。 (注意:只有基类在同一作用域内定义或导入时才可以这样使用。)

Python has two built-in functions that work with inheritance:

Python有两个和集成相关的内置函数:

  • Use isinstance() to check an object’s type: isinstance(obj, int) will be True only if obj.__class__ is int or some class derived from int.

    使用 isinstance() 函数可以检测一个对象的类型: 仅当 obj.__class__int 或者从 int 中派生的类时, isinstance(obj, int) 才返回 True

  • Use issubclass() to check class inheritance: issubclass(bool, int) is True since bool is a subclass of int. However, issubclass(float, int) is False since float is not a subclass of int.

    使用 issubclass() 函数可以检查类的继承关系:因为 boolint 的子类,所以 issubclass(bool, int) 返回 True 。 但是, issubclass(float, int)False ,因为 float 不是 int 的子类。

9.5.1. Multiple Inheritance 多重继承

Python supports a form of multiple inheritance as well. A class definition with multiple base classes looks like this:

Python也支持多重继承的形式:使用多个基类进行类定义。 如下所示:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

For most purposes, in the simplest cases, you can think of the search for attributes inherited from a parent class as depth-first, left-to-right, not searching twice in the same class where there is an overlap in the hierarchy. Thus, if an attribute is not found in DerivedClassName, it is searched for in Base1, then (recursively) in the base classes of Base1, and if it was not found there, it was searched for in Base2, and so on.

大多数情况下,最简单来讲,你可以认为对从父类继承的属性搜索遵循深度优先,从左向右,不会对重叠的相同层次的同一个类搜索两次。 因此,如果一个属性在 DerivedClassName 中找不到,就会继续在 Base1 中查找,然后(递归的)在 Base1 的基类中查找。 如果还没有找到,就会继续从 Base2 类中查找,依次类推。

In fact, it is slightly more complex than that; the method resolution order changes dynamically to support cooperative calls to super(). This approach is known in some other multiple-inheritance languages as call-next-method and is more powerful than the super call found in single-inheritance languages.

实际上,它比以上要稍微复杂一些,解决方法就是为支持协同调用 super() 而动态改变排序。 这个方法在一些其他多重继承的语言中称作 call-next-method ,并且要比单继承语言中的 super 调用更强大。

Dynamic ordering is necessary because all cases of multiple inheritance exhibit one or more diamond relationships (where at least one of the parent classes can be accessed through multiple paths from the bottommost class). For example, all classes inherit from object, so any case of multiple inheritance provides more than one path to reach object. To keep the base classes from being accessed more than once, the dynamic algorithm linearizes the search order in a way that preserves the left-to-right ordering specified in each class, that calls each parent only once, and that is monotonic (meaning that a class can be subclassed without affecting the precedence order of its parents). Taken together, these properties make it possible to design reliable and extensible classes with multiple inheritance. For more detail, see http://www.python.org/download/releases/2.3/mro/.

动态排序是必须的,因为多重继承中所有的情况都会呈现为一个或多个菱形关系(从最低层的类开始,至少存在一个父类可以通过多条路径访问)。 比如,所有的类继承自 oject ,索引任何情况下的多重继承都存在不止一条路径可以访问 object 。 想要避免基类被多次访问,动态算法在每个类中通过维护一个从左向右的特殊顺序的方式将搜索顺序线性化,从而每个父类支部调用一次,并且那是不变的(即继承一个类不会一项它父类的优先级)。 总之,这些属性让使用多重继承设计可靠的和可扩展的类成为可能。 更多信息请参考: http://www.python.org/download/releases/2.3/mro/

9.6. Private Variables 私有变量

There is limited support for class-private identifiers. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, so it can be used to define class-private instance and class variables, methods, variables stored in globals, and even variables stored in instances. private to this class on instances of other classes. Truncation may occur when the mangled name would be longer than 255 characters. Outside classes, or when the class name consists of only underscores, no mangling occurs.

Python对类私有标识符提供了有限的支持。 形如 __spam (至少两个下划线前缀,至多一个下划线后缀)的任何标识符都会被原文的替换成 _classname__spam 形式,这里 classname 就是当前的类名。 这种变换不会关注标识符的语法位置,因此可以用来定义类私有实例和类变量、方法、全局变量,甚至把*其他*类实例变量保存为私有实例变量。 当变换的名字超过255个字符时就会被截短,在类外部或者类名只有一个下划线组成时则不会被截短。

Name mangling is intended to give classes an easy way to define “private” instance variables and methods, without having to worry about instance variables defined by derived classes, or mucking with instance variables by code outside the class. Note that the mangling rules are designed mostly to avoid accidents; it still is possible for a determined soul to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger, and that’s one reason why this loophole is not closed. (Buglet: derivation of a class with the same name as the base class makes use of private variables of the base class possible.)

名字变换是一种实现类定义“私有”实例变量和方法的简单的途径,无需担心与派生类定义的实例变量混淆,或与类外部代码的实例变量混淆。 注意:变换规则的设计主要是用来避免冲突,执意访问或修改被认为是私有的变量仍然是可行的。 这在特殊环境下尤为有用,比如调试的时候,这也是为何一直没有堵上这个漏洞的原因之一。 (号外:派生类和基类具有相同的名字即可使用基类的私有变量。)

Notice that code passed to exec() or eval() does not consider the classname of the invoking class to be the current class; this is similar to the effect of the global statement, the effect of which is likewise restricted to code that is byte-compiled together. The same restriction applies to getattr(), setattr() and delattr(), as well as when referencing __dict__ directly.

注意:传递给 exec()eval() 函数的代码不会将调用类的类名作为当前类。 这和 global 语句的情况相似,它的作用限制于一起进行字节码编译的代码。 同样的限制也适用于 getattr()setattr()delattr() 函数,以及直接引用 __dict__ 时。

9.7. Odds and Ends 备注

Sometimes it is useful to have a data type similar to the Pascal “record” or C “struct”, bundling together a few named data items. An empty class definition will do nicely:

有时拥有一种类似Pascal的“记录(record)”或C的“结构(struct)”的数据类型是非常有用的,可以将一些已命名的数据项绑定在一起。 定义一个空的类便可以很好的做到这点:

class Employee:
    pass

john = Employee() # Create an empty employee record

# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

A piece of Python code that expects a particular abstract data type can often be passed a class that emulates the methods of that data type instead. For instance, if you have a function that formats some data from a file object, you can define a class with methods read() and readline() that get the data from a string buffer instead, and pass it as an argument.

一段需要一个特别的抽象数据类型的Python代码通常可以传入一个模仿那种数据类型方法的类来代替。 例如,如果你有一个格式化文件对象数据的函数,你可以定义一个包含 read()readline() 方法的类,它可以替代从字符串缓冲中获取数据并作为参数传递。

Instance method objects have attributes, too: m.__self__ is the instance object with the method m(), and m.__func__ is the function object corresponding to the method.

实例方法对象也包含属性: m.__self__ 就是方法 m() 的实例对象,并且 m.__func__ 就是方法对应的函数对象。

9.8. Exceptions Are Classes Too 异常也是类

User-defined exceptions are identified by classes as well. Using this mechanism it is possible to create extensible hierarchies of exceptions.

用户自定义的异常也被当做类。 利用这一原理便可以创建可扩展的异常分类。

There are two valid (semantic) forms for the raise statement:

以下是两种有效的(语义上)异常抛出形式:

raise Class

raise Instance

In the first form, Class must be an instance of type or of a class derived from it. The first form is a shorthand for:

在第一种方式中, Class 必须是一个 type 或其派生类的实例。 第一种方式为以下形式的简写:

raise Class()

A class in an except clause is compatible with an exception if it is the same class or a base class thereof (but not the other way around — an except clause listing a derived class is not compatible with a base class). For example, the following code will print B, C, D in that order:

except 从句中的类是与异常相兼容的,这里的异常是指同一个类或者是一个基类(但是不能反过来说—— except 从句中列出的派生类与基类是不兼容的)。 例如,下面的代码将依次输出B,C,D:

class B(Exception):
    pass
class C(B):
    pass
class D(C):
    pass

for c in [B, C, D]:
    try:
        raise c()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

Note that if the except clauses were reversed (with except B first), it would have printed B, B, B — the first matching except clause is triggered.

注意:如果 except 从句被颠倒了(最先使用 except B ),它只会打印B,B,B——首先被匹配的异常被触发。

When an error message is printed for an unhandled exception, the exception’s class name is printed, then a colon and a space, and finally the instance converted to a string using the built-in function str().

当打印一个未处理异常的错误信息时,异常的类名也会被打印出来,然后紧跟一个冒号和一个空格,最后使用内置函数 str() 将实例转换为字符串。

9.9. Iterators 迭代器

By now you have probably noticed that most container objects can be looped over using a for statement:

到现在,你可能已经注意到大多数容器对象都可以使用 for 语句遍历:

for element in [1, 2, 3]:
    print(element)
for element in (1, 2, 3):
    print(element)
for key in {'one':1, 'two':2}:
    print(key)
for char in "123":
    print(char)
for line in open("myfile.txt"):
    print(line)

This style of access is clear, concise, and convenient. The use of iterators pervades and unifies Python. Behind the scenes, the for statement calls iter() on the container object. The function returns an iterator object that defines the method __next__() which accesses elements in the container one at a time. When there are no more elements, __next__() raises a StopIteration exception which tells the for loop to terminate. You can call the __next__() method using the next() builtin; this example shows how it all works:

这种访问风格清晰、简洁并且方便。 迭代器的使用在Python中非常普遍而且统一。 在幕后, for 语句会对容器对象调用 iter() 函数。 这个函数返回一个定义了 __next__() 方法的迭代对象,每次访问容器中的一个元素。 当没有可以继续访问的元素时, :meth: __next__ 方法会跑出一个 StopIteration 异常,这将会通知 for 语句结束循环。 你可以使用内置 next() 函数调用 __next__() 方法。以下是一个完整的示例:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
    next(it)
StopIteration

Having seen the mechanics behind the iterator protocol, it is easy to add iterator behavior to your classes. Define a __iter__() method which returns an object with a __next__() method. If the class defines __next__(), then __iter__() can just return self.

了解了迭代器协议背后的机制,你可以轻松的给类添加迭代器行为。 定义一个 __iter__() 方法,使其返回一个带有 __next__() 方法的对象。 如果这个类定义了 __next__() 方法,那么 __iter__() 方法只需要返回 self 即可。

class Reverse:
    "Iterator for looping over a sequence backwards"
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

>>> for char in Reverse('spam'):
...     print(char)
...
m
a
p
s

9.10. Generators 生成器

Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. Each time next() is called on it, the generator resumes where it left-off (it remembers all the data values and which statement was last executed). An example shows that generators can be trivially easy to create:

Generators 是创建迭代器简单而又强大的工具。 它们写起来就像正式的函数,但是在需要返回数据时使用 yield 语句。 每次对其调用 next() 函数,生成器就会从上次脱离的位置继续(它记忆所有的数据值和最后执行的语句)。 下例说明可以辩解的创建一个生成器:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

>>> for char in reverse('golf'):
...     print(char)
...
f
l
o
g

Anything that can be done with generators can also be done with class based iterators as described in the previous section. What makes generators so compact is that the __iter__() and __next__() methods are created automatically.

任何可以使用生成器做的事情,也可以使用前一节介绍的基于迭代器的类来完成。 生成器之所以如此简单是因为在生成器中 __iter__()__next__() 方法是自动创建的。

Another key feature is that the local variables and execution state are automatically saved between calls. This made the function easier to write and much more clear than an approach using instance variables like self.index and self.data.

另一个关键特性是在调用之间局部变量和执行状态都被自动的保存。 这就使得函数编写更容易,并且比像 self.indexself.data 形式这样手动调用示例变量更清晰。

In addition to automatic method creation and saving program state, when generators terminate, they automatically raise StopIteration. In combination, these features make it easy to create iterators with no more effort than writing a regular function.

除自动创建方法和保存程序状态外,当生成器到达结尾时,它们会自动抛出 StopIteration 异常。 综合来说,这些特性使得创建迭代器就像书写一个普通函数一般简单。

9.11. Generator Expressions 生成器表达式

Some simple generators can be coded succinctly as expressions using a syntax similar to list comprehensions but with parentheses instead of brackets. These expressions are designed for situations where the generator is used right away by an enclosing function. Generator expressions are more compact but less versatile than full generator definitions and tend to be more memory friendly than equivalent list comprehensions.

有些简单的生成器可以使用类似列表推导式的符号简单编码为表达式,但无需带有中括号。 这些表达式是为某种情景而设计的,在那里生成器被一个封闭函数使用。 生成器表达式并完整的生成器定义更为简洁,但没有那么通用,而且比等价的列表推导式更容易记住。

Examples:

>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260

>>> from math import pi, sin
>>> sine_table = {x: sin(x*pi/180) for x in range(0, 91)}

>>> unique_words = set(word  for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']

Footnotes

[1]

Except for one thing. Module objects have a secret read-only attribute called __dict__ which returns the dictionary used to implement the module’s namespace; the name __dict__ is an attribute but not a global name. Obviously, using this violates the abstraction of namespace implementation, and should be restricted to things like post-mortem debuggers.

有个例外:模块对象有一个隐秘的只读属性 __dict__ ,返回一个作为模块命名空间的字典。 名称 __dict__ 是一个属性,但不是全局名称。 显然,这样使用违法了命名空间实现的抽象概念,并且应该严格限制在希望在测试之后进一步研究的场合。