9. Classes类¶
Classes provide a means of bundling data and functionality together. 类提供了一种将数据和功能捆绑在一起的方法。Creating a new class creates a new type of object, allowing new instances of that type to be made. 创建一个新类会创建一个新类型的对象,允许创建该类型的新实例。Each class instance can have attributes attached to it for maintaining its state. 每个类实例都可以附加属性以维护其状态。Class instances can also have methods (defined by its class) for modifying its state.类实例还可以具有修改其状态的方法(由其类定义)。
Compared with other programming languages, Python’s class mechanism adds classes with a minimum of new syntax and semantics. 与其他编程语言相比,Python的类机制添加了具有最少新语法和语义的类。It is a mixture of the class mechanisms found in C++ and Modula-3. 它混合了C++和Modula-3中的类机制。Python classes provide all the standard features of Object Oriented Programming: 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. Python类提供了面向对象编程的所有标准特性:类继承机制允许多个基类,派生类可以重写其基类的任何方法,方法可以调用同名基类的方法。Objects can contain arbitrary amounts and kinds of data. 对象可以包含任意数量和种类的数据。As is true for modules, classes partake of the dynamic nature of Python: they are created at runtime, and can be modified further after creation.与模块一样,类也具有Python的动态特性:它们是在运行时创建的,创建后可以进一步修改。
In C++ terminology, normally class members (including the data members) are public (except see below Private Variables), and all member functions are virtual. 在C++术语中,通常类成员(包括数据成员)是公共的(除了下面的私有变量),所有成员函数都是虚拟的。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. 与Modula-3一样,从对象的方法中引用对象的成员并没有简写法:方法函数是用一个表示对象的显式第一个参数声明的,该参数由调用隐式提供。As in Smalltalk, classes themselves are objects. 就像在Smalltalk中一样,类本身就是对象。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. 这为导入和重命名提供了语义。与C++和Modula-3不同,内置类型可以用作用户扩展的基类。Also, like in C++, most built-in operators with special syntax (arithmetic operators, subscripting etc.) can be redefined for class instances.此外,与C++一样,大多数具有特殊语法(算术运算符、订阅等)的内置运算符都可以为类实例重新定义。
(Lacking universally accepted terminology to talk about classes, I will make occasional use of Smalltalk and C++ terms. (由于缺乏公认的术语来谈论类,我将偶尔使用Smalltalk和C++术语。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.)我会使用Modula-3术语,因为它的面向对象语义比C++更接近Python,但我希望很少有读者听说过它。)
9.1. A Word About Names and Objects一个关于名字和物体的词¶
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). 乍一看Python通常不会理解这一点,在处理不可变的基本类型(数字、字符串、元组)时,可以安全地忽略这一点。However, aliasing has a possibly surprising effect on the semantics of Python code involving mutable objects such as lists, dictionaries, and most other types. This is usually used to the benefit of the program, since aliases behave like pointers in some respects. 然而,别名可能会对涉及列表、字典和大多数其他类型的可变对象的Python代码的语义产生令人惊讶的影响。这通常有利于程序,因为别名在某些方面的行为类似于指针。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.例如,传递一个对象很便宜,因为实现只传递一个指针;如果一个函数修改了一个作为参数传递的对象,调用者就会看到变化——这就不需要像Pascal那样使用两种不同的参数传递机制。
9.2. Python Scopes and NamespacesPython作用域和名称空间¶
Before introducing classes, I first have to tell you something about Python’s scope rules. 在介绍类之前,我首先要告诉您一些Python的范围规则。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.顺便说一句,关于这个主题的知识对任何高级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. 大多数名称空间目前都是作为Python字典实现的,但这在任何方面都不明显(性能除外),将来可能会发生变化。Examples of namespaces are: the set of built-in names (containing functions such as 名称空间的示例包括:一组内置名称(包含abs()
, and built-in exception names); the global names in a module; and the local names in a function invocation. abs()
等函数和内置异常名称);模块中的全局名称;以及函数调用中的本地名称。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.maximize
,而不会产生混淆,但是模块的用户必须在其前面加上模块名。
By the way, I use the word attribute for any name following a dot — for example, in the expression 顺便说一下,我使用单词attribute来表示点后面的任何名称——例如,在表达式z.real
, real
is an attribute of the object z
. z.real
中,real
是对象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. modname.funcname
中,modname
是模块对象,funcname
是其属性。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
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
. modname.the_answer = 42
。Writable attributes may also be deleted with the 也可以使用del
statement. del
语句删除可写属性。For example, 例如,del modname.the_answer
will remove the attribute the_answer
from the object named by modname
.del modname.the_answer
将从以modname
命名的对象中删除属性the_answer
。
Namespaces 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. 包含内置名称的名称空间是在Python解释器启动时创建的,永远不会被删除。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. __main__
的模块的一部分,因此它们有自己的全局名称空间。(The built-in names actually also live in a module; this is called (内置名称实际上也存在于模块中;这称为builtins
.)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. 作用域是Python程序的一个文本区域,可以直接访问其名称空间。“Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.这里的“直接可访问”意味着对名称的非限定引用试图在名称空间中找到该名称。
Although scopes are determined statically, they are used dynamically. 虽然范围是静态确定的,但它们是动态使用的。At any time during execution, there are 3 or 4 nested scopes whose namespaces are directly accessible:在执行过程中的任何时候,都有3或4个嵌套作用域的名称空间可以直接访问:
the innermost scope, which is searched first, contains the local names最里面的范围(首先搜索)包含本地名称the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names从最近的封闭范围开始搜索的任何封闭函数的范围都包含非局部名称,但也包含非全局名称the next-to-last scope contains the current module’s global names倒数第二个作用域包含当前模块的全局名称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 variables 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
语句;如果未声明为非局部变量,则这些变量是只读的(尝试写入此类变量只会在最内部的作用域中创建一个新的局部变量,而保持同名的外部变量不变)。
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.)(事实上,局部变量已经是静态确定的。)
A special quirk of Python is that – if no Python的一个特殊怪癖是——如果没有有效的global
or nonlocal
statement is in effect – assignments to names always go into the innermost scope. global
或nonlocal
语句——对名称的赋值总是进入最内部的范围。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. del x
语句从本地作用域引用的名称空间中删除x
的绑定。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.import
语句和函数定义将模块或函数名称绑定到本地作用域中。
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:global
和nonlocal
如何影响变量绑定:
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. 请注意,local分配(默认)没有改变scope_test对spam的绑定。The nonlocal
assignment changed scope_test's binding of spam, and the global
assignment changed the module-level binding.nonlocal
赋值改变了scope_test对spam的绑定,global
赋值改变了模块级绑定。
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. def
语句))必须在生效之前执行。(You could conceivably place a class definition in a branch of an (可以想象,可以将类定义放在if
statement, or inside a function.)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: 属性引用使用Python中用于所有属性引用的标准语法:obj.name
. 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:如果类定义如下所示:
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. MyClass.i
和MyClass.f
是有效的属性引用,分别返回一个整数和一个函数对象。Class attributes can also be assigned to, so you can change the value of 类属性也可以指定给,因此可以通过指定来更改MyClass.i
by assignment. MyClass.i
的值。__doc__
is also a valid attribute, returning the docstring belonging to the class: "A simple example class"
.__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. 假设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. __init__()
方法时,类实例化会自动为新创建的类实例调用__init__()
。So in this example, a new, initialized instance can be obtained by:因此,在本例中,可以通过以下方式获得一个新的初始化实例:
x = MyClass()
Of course, the 当然,__init__()
method may have arguments for greater flexibility. __init__()
方法可能有更大灵活性的参数。In that case, arguments given to the class instantiation operator are passed on to 在这种情况下,给类实例化运算符的参数被传递到__init__()
. __init__()
上。For example,例如
>>> 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++. 数据属性对应于Smalltalk中的“实例变量”,以及C++中的“数据成员”。Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. 数据属性不需要声明;与局部变量一样,它们在第一次分配给变量时就开始存在。For example, if 例如,如果x是上面创建的x
is the instance of MyClass
created above, the following piece of code will print the value 16
, without leaving a trace: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. (在Python中,术语“方法”并不是类实例独有的:其他对象类型也可以有方法。For example, list objects have methods called append, insert, remove, sort, and so on. 例如,列表对象有append
、insert
、remove
、sort
等方法。However, in the following discussion, we’ll use the term method exclusively to mean methods of class instance objects, unless explicitly stated otherwise.)但是,在下面的讨论中,我们将使用术语“方法”专门指类实例对象的方法,除非另有明确说明。)
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. x.f
是一个有效的方法引用,因为MyClass.f
是一个函数,而x.i
不是,因为MyClass.i
不是。But 但x.f
is not the same thing as MyClass.f
— it is a method object, not a function object.x.f
与MyClass.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'
. MyClass
示例中,这将返回字符串'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. x.f
是一个方法对象,可以存储起来,以后再调用。For example:例如:
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. x.f()
时没有使用上述参数,即使f()
的函数定义指定了参数。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…当调用一个需要参数的函数时,如果没有任何参数,Python肯定会引发一个异常——即使该参数实际上没有被使用……
Actually, you may have guessed the answer: the special thing about methods is that the instance object is passed as the first argument of the function. 实际上,您可能已经猜到了答案:方法的特殊之处在于实例对象作为函数的第一个参数传递。In our example, the call 在示例中,调用x.f()
is exactly equivalent to MyClass.f(x)
. x.f()
与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 instance object before the first argument.一般来说,使用n个参数的列表调用方法相当于使用参数列表调用相应的函数,该参数列表是通过在第一个参数之前插入方法的实例对象创建的。
If you still don’t understand how methods work, a look at the implementation can perhaps clarify matters. 如果您仍然不了解这些方法是如何工作的,那么看看实现可能会澄清一些问题。When a non-data attribute of an instance is referenced, the instance’s 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, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.使用参数列表调用方法对象时,将从实例对象和参数列表构造一个新的参数列表,并使用此新参数列表调用函数对象。
9.3.5. Class and Instance Variables类和实例变量¶
Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:一般来说,实例变量用于每个实例特有的数据,类变量用于类的所有实例共享的属性和方法:
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'
As discussed in A Word About Names and Objects, shared data can have possibly surprising effects with involving mutable objects such as lists and dictionaries. 正如在一篇关于名称和对象的文章中所讨论的,共享数据可能会对列表和字典等可变对象产生令人惊讶的影响。For example, the tricks list in the following code should not be used as a class variable because just a single list would be shared by all Dog instances:例如,以下代码中的tricks列表不应用作类变量,因为所有Dog
实例只共享一个列表:
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
Correct design of the class should use an instance variable instead:正确的类设计应该使用实例变量:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # creates a new empty list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
9.4. Random Remarks随意的评论¶
If the same attribute name occurs in both an instance and in a class, then attribute lookup prioritizes the instance:如果实例和类中都出现相同的属性名,则属性查找会优先考虑该实例:
>>> class Warehouse:
purpose = 'storage'
region = 'west'
>>> w1 = Warehouse()
>>> print(w1.purpose, w1.region)
storage west
>>> w2 = Warehouse()
>>> w2.region = 'east'
>>> print(w2.purpose, w2.region)
storage east
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. 事实上,Python中的任何东西都不可能强制执行数据隐藏——这一切都是基于约定的。(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.)(另一方面,用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
. self
。This is nothing more than a convention: the name 这只不过是一种约定:self
has absolutely no special meaning to Python. self
这个名字对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.但是,请注意,如果不遵循约定,其他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 C
— h
being exactly equivalent to g
. f
,g
和h
都是C
类的属性,它们引用函数对象,因此它们都是C
-h
实例的方法,与g
完全等价。Note that this practice usually only serves to confuse the reader of a program.请注意,这种做法通常只会让程序的读者感到困惑。
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 its definition. 与方法关联的全局范围是包含其定义的模块。(A class 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__
.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. BaseClassName
。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:例如,当基类在另一个模块中定义时,这可能很有用:
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. DerivedClassName()
创建该类的新实例。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.方法引用的解析如下:搜索相应的类属性,必要时沿着基类链向下搜索,如果生成函数对象,则方法引用有效。
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 (对于C++程序员来说:Python中的所有方法都是有效的virtual
.)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)
. BaseClassName.methodname(self, arguments)
。This is occasionally useful to clients as well. 这对客户有时也很有用。(Note that this only works if the base class is accessible as (请注意,只有当基类在全局范围内可以作为BaseClassName
in the global scope.)BaseClassName
访问时,这才有效。)
Python has two built-in functions that work with inheritance:Python有两个用于继承的内置函数:
Use使用isinstance()
to check an instance’s type:isinstance(obj, int)
will beTrue
only ifobj.__class__
isint
or some class derived fromint
.isinstance()
检查实例的类型:isinstance(obj, int)
只有在obj.__class__
是int
或从int
派生的某个类时才为True
。Use使用issubclass()
to check class inheritance:issubclass(bool, int)
isTrue
sincebool
is a subclass ofint
.issubclass()
检查类继承:issubclass(bool, int)
为True
,因为bool
是int
的子类。However,然而,issubclass(float, int)
isFalse
sincefloat
is not a subclass ofint
.issubclass(float, int)
是False
,因为float
不是int
的子类。
9.5.1. Multiple Inheritance多重继承¶
Python supports a form of multiple inheritance as well. Python还支持一种形式的多重继承。A class definition with multiple base classes looks like this:具有多个基类的类定义如下所示:
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
中找不到该属性,则在Base2
中搜索该属性,依此类推。
In fact, it is slightly more complex than that; the method resolution order changes dynamically to support cooperative calls to 事实上,它比这稍微复杂一些;方法解析顺序会动态更改,以支持对super()
. 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.这种方法在其他一些多重继承语言中被称为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
. object
继承,所以任何情况下的多重继承都会提供多个到达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 https://www.python.org/download/releases/2.3/mro/.有关更多详细信息,请参阅https://www.python.org/download/releases/2.3/mro/。
9.6. Private Variables私有变量¶
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. Python中不存在只能从对象内部访问的“Private”实例变量。However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. 然而,大多数Python代码都遵循一种约定:前缀为下划线的名称(例如_spam
) should be treated as a non-public part of the API (whether it is a function, a method or a data member). _spam
)应该被视为API的非公共部分(无论是函数、方法还是数据成员)。It should be considered an implementation detail and subject to change without notice.应将其视为实施细节,如有变更,恕不另行通知。
Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. 由于类私有成员有一个有效的用例(即避免名称与子类定义的名称发生名称冲突),因此对这种称为名称篡改的机制的支持有限。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. __spam
的任何标识符(至少两个前导下划线,最多一个尾随下划线)在文本上替换为_classname__spam
,其中classname
是当前类名,前导下划线被去掉。This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.只要标识符出现在类的定义中,这种破坏就不会考虑标识符的语法位置。
Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls. 名称混乱有助于让子类重写方法,而不破坏类内方法调用。For example:例如:
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
The above example would work even if 即使MappingSubclass
were to introduce a __update
identifier since it is replaced with _Mapping__update
in the Mapping
class and _MappingSubclass__update
in the MappingSubclass
class respectively.MappingSubclass
引入了一个__update
标识符,上面的示例也会起作用,因为它在MappingSubclass
类中分别被替换为_Mapping__update
,在MappingSubclass
类中被替换为_MappingSubclass__
更新。
Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. 请注意,破损规则的设计主要是为了避免事故;仍然可以访问或修改被认为是私有的变量。This can even be useful in special circumstances, such as in the debugger.这甚至可以在特殊情况下使用,例如在调试器中。
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. exec()
或eval()
的代码并不认为调用类的类名是当前类;这与global
语句的效果类似,global
语句的效果同样仅限于字节编译在一起的代码。The same restriction applies to 同样的限制也适用于getattr()
, setattr()
and delattr()
, as well as when referencing __dict__
directly.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. 有时,使用类似于Pascal“record”或C“struct”的数据类型,将几个命名的数据项捆绑在一起是很有用的。An empty class definition will do nicely:空的类定义可以很好地实现:
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. 一段需要特定抽象数据类型的Python代码通常可以被传递给一个模拟该数据类型的方法的类。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.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. 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, end='')
This style of access is clear, concise, and convenient. 这种访问方式清晰、简洁、方便。The use of iterators pervades and unifies Python. 迭代器的使用渗透并统一了Python。Behind the scenes, the 在幕后,for
statement calls iter()
on the container object. for
语句对容器对象调用iter()
。The function returns an iterator object that defines the method 该函数返回一个迭代器对象,该对象定义了方法__next__()
which accesses elements in the container one at a time. __next__()
,该方法一次访问一个容器中的元素。When there are no more elements, 当没有更多的元素时,__next__()
raises a StopIteration
exception which tells the for
loop to terminate. __next__()
会引发StopIteration
异常,该异常会通知for
循环终止。You can call the 您可以使用__next__()
method using the next()
built-in function; this example shows how it all works:next()
内置函数调用__next__()
方法;这个例子展示了它的工作原理:
>>> s = 'abc'
>>> it = iter(s)
>>> it
<str_iterator object at 0x10c90e650>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
next(it)
StopIteration
Having seen the mechanics behind the iterator protocol, it is easy to add iterator behavior to your classes. 了解了迭代器协议背后的机制后,很容易将迭代器行为添加到类中。Define an 定义一个__iter__()
method which returns an object with a __next__()
method. __iter__()
方法,该方法返回一个带有__next__()
方法的对象。If the class defines 如果类定义了__next__()
, then __iter__()
can just return self
:__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]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
... print(char)
...
m
a
p
s
9.9. 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. yield
语句。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). next()
时,生成器都会从停止的地方恢复(它会记住所有数据值以及上次执行的语句)。An example shows that generators can be trivially easy to create:一个示例显示,生成器的创建非常简单:
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.index
和self.data
)的方法更容易编写,也更清晰。
In addition to automatic method creation and saving program state, when generators terminate, they automatically raise 除了自动创建方法和保存程序状态外,当生成器终止时,还会自动引发StopIteration
. StopIteration
。In combination, these features make it easy to create iterators with no more effort than writing a regular function.结合起来,这些特性可以轻松创建迭代器,只需编写一个常规函数。
9.10. Generator Expressions生成器表达式¶
Some simple generators can be coded succinctly as expressions using a syntax similar to list comprehensions but with parentheses instead of square 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
>>> 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.__dict__
的秘密只读属性,该属性返回用于实现模块名称空间的字典;名称__dict__
是一个属性,但不是全局名称。Obviously, using this violates the abstraction of namespace implementation, and should be restricted to things like post-mortem debuggers.显然,使用这种方法违反了名称空间实现的抽象,应该仅限于事后调试程序。