Object-Oriented Programming
面向对象编程
Chapter Topics
本章主题
Introduction
引言
Object-Oriented Programming
面向对象编程
Classes
类
Instances
实例
Binding and Method Invocation
绑定与方法调用
Subclassing, Derivation, and Inheritance
子类,派生和继承
Built-in Functions
内建函数
Customizing Classes
定制类
Privacy
私有性
Delegation and Wrapping
授权与包装
Advanced Features of New-Style Classes
新式类的高级特性
Related Modules
相关模块
Classes finally introduce the notion of object-oriented programming(OOP) to our picture. We will first present a high-level overview, covering all the main aspects of using classes and OOP in Python. The remainder of the chapter examines details on classes, class instances, and methods. We will also describe derivation or subclassing in Python and what its inheritance model is. Finally, Python gives programmers the ability to cus- tomize classes with special functionality, including those that overload operators and emulate Python types. We will show you how to implement some of these special methods to customize your classes so that they behave more like Python’s built-in types.
在我们的描绘中,类最终解释了面向对象编程思想(OOP)。本章中,我们首先将给出一个总体上的概述,涵盖了Python中使用类和OOP的所有主要方面。其余部分针对类,类实例和方法进行详细探讨。我们还将描述Python中有关派生或子类化及继承机理。最后,Python可以在特定功能方面定制类,例如重载操作符,模拟Python类型等。我们将展示如何实现这些特殊的方法来自定义你的类,以让它们表现得更像Python的内建类型。
With this said, however, we would like to add that there have been some exciting changes with regard to Python OOP. In version 2.2, the Python com- munity finally saw the unification of types and classes, and with the new-style classes come much more advanced OOP features. New-style classes represent a superset of features from classic (or old-style) classes, the original class objects since Python was born.
然而,除了这些外,Python的面向对象编程(00P)还有一些令人兴奋的变动。在版本2.2中,Python社区最终统一了类型(types)和类(classes),新式类具备更多高级的OOP特性,扮演了一个经典类(或者说旧式类)超集的角色,后者是Python诞生时所创造的类对象。
We will first present the core features common to both types of classes, and then introduce those more advanced features found only in Python’s new-style classes.
下面,我们首先介绍在两种风格的类(译者注:新式类和旧式类)中都存在的核心特性,然后讲解那些只有新式类才拥有的的高级特性。
13.1 Introduction
13.1 介绍
Before we get into the nitty-gritty of OOP and classes, we begin with a high-level overview, then present some simple examples to get you warmed up. If you are new to object-oriented programming, you may wish to skim this sec- tion first, then begin the formal reading in Section 13.2. If you are already familiar with object-oriented programming and want to see how it is done in Python, finish this section and go straight to Section 13.3 for more details.
The main two entities in Python object-oriented programming are classes and class instances (see Figure 13?1).
在摸清OOP和类的本质之前,我们首先讲一些高级主题,然后通过几个简单的例子热一热身。如果你刚学习面向对象编程,你可以先跳过这部分内容,直接进入第13.2节。如果你对有关面向对象编程已经熟悉了,并且想了解它在Python中是怎样表现的,那么先看一下这部分内容,然后再进入13.3节,看个究竟!
在Python中,面向对象编程主要有两个主题,就是类和类实例(见图13-1)
Classes and Instances
类与实例
Classes and instances are related to each other: classes provide the definition
of an object, and instances are “the real McCoy,” the objects specified in the class definition brought to life.
类与实例相互关联着:类是对象的定义,而实例是"真正的实物",它存放了类中所定义的对象的具体信息。
Here is an example of how to create a class:
下面的示例展示了如何创建一个类:
class MyNewObjectType(bases):
'define MyNewObjectType class'
class_suite #类体
The keyword is class, followed by the class name. What follows is the suite of code that defines the class. This usually consists of various definitions and declarations. The biggest difference between declaring new-style classes and classic classes is that all new-style classes must inherit from at least one parent class. The bases argument is one (single inheritance) or more (multi- ple inheritance) parent classes to derive from.
关键字是class,紧接着是一个类名。随后是定义类的类体代码。这里通常由各种各样的定义和声明组成。新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类,参数bases可以是一个(单继承)或多个(多重继承)用于继承的父类。
The “mother of all classes” is object. If you do not have any ancestor classes to inherit from, use object as your default. It must exist at the top of every class hierarchy. If you do not subclass object or a class that subclasses object, then you have defined a classic class:
object是“所有类之母”。如果你的类没有继承任何其他父类,object将作为默认的父类。它位于所有类继承结构的最上层。如果你没有直接或间接的子类化一个对象,那么你就定义了一个经典类:
class MyNewObjectType:
'define MyNewObjectType classic class'
class_suite
Conversely, if you do not specify a parent class, or if you subclass a base class without a parent class, you have created a classic class. Most Python classes are still classic classes. There really is no problem with using them until they become obsolete in some future version of Python. We do recom- mend that you use new-style classes whenever possible, but for learning purposes, either type will suffice.
如果你没有指定一个父类,或者如果所子类化的基本类没有父类,你这样就是创建了一个经典类。很多Python类都还是经典类。即使经典类已经过时了,在以后的Python版本中,仍然可以使用它们。不过我们强烈推荐你尽可能使用新式类,尽管对于学习来说,两者都行。
Figure 13?1
The factory manufacturing machines on the left are analogous to classes,
while the toys produced are instances of their respective classes. Although each instance has the basic underlying structure, individual attributes like color or feet can be changed― these are similar to instance attributes.
左边的工厂制造机器相当于类,而生产出来的玩具就是它们各个类的实例。尽管每个实例都有一个基本的结构,但各自的属性像颜色或尺寸可以改变-----这就好比实例的属性。
The process of creating an instance is called instantiation, and it is carried out like this (note the conspicuous absence of a new keyword):
创建一个实例的过程称作实例化,过程如下(注意:没有使用new关键字):
myFirstObject = MyNewObjectType()
The class name is given as an “invocation,” using the familiar function operators ( ( ) ). You then typically assign that newly created instance to a variable. The assignment is not required syntactically, but if you do not save your instance to a variable, it will be of no use and will be automatically garbage- collected because there would no references to that instance. What you would be doing is allocating memory, then immediately deallocating it.
类名使用我们所熟悉的函数操作符(()),以“函数调用”的形式出现。然后你通常会把这个新建的实例赋给一个变量。赋值在语法上不是必须的,但如果你没有把这个实例保存到一个变量中,它就没用了,会被自动垃圾收集器回收,因为任何引用指向这个实例。这样,你刚刚所做的一切,就是为那个实例分配了一块内存,随即又释放了它。
Classes can be as simple or as complicated as you wish them to be. At a very minimum, classes can be used as namespaces (see Chapter 11 for more on these). By this, we mean that you can store data into variables and group them in such a way that they all share the same relationship―a named rela- tionship using the standard Python dotted-attribute notation. For example, you may have a class without any inherent attributes and merely use such a class to provide a namespace for data, giving your class characteristics similar to records in Pascal or structures in C, or, in other words, use the class simply as a container object with shared naming.
只要你需要,类可以很简单,也可以很复杂。最简单的情况,类仅用作名称空间(namespaces)(参见第11章)。这意味着你把数据保存在变量中,对他们按名称空间进行分组,使得他们处于同样的关系空间中-----所谓的关系是使用标准Python句点属性标识。比如,你有一个本身没有任何属性的类,使用它仅对数据提供一个名字空间,让你的类拥有像Pascal中的记录集(records)和C语言中的结构体(structures)一样的特性,或者换句话说,这样的类仅作为容器对象来共享名字空间。
Here is an example:
示例如下:
class MyData(object):
pass
Recall that the pass statement is used where code is required syntactically, but no operation is desired. In this case, the required code is the class suite, but we do not wish to provide one. The class we just defined has no methods or any other attributes. We will now create an instance to use the class simply as a namespace container.
注意有的地方在语法构成上需要有一行语句,但实际上不需要做任何操作,这时候可以使用pass语句。这种情况,必要的代码就是类体,但我们暂不想提供这些。上面定义的类没有任何方法或属性。下面我们创建一个实例,它只使用类作为名称空间容器。
>>> mathObj = MyData()
>>> mathObj.x = 4
>>> mathObj.y = 5
>>> mathObj.x + mathObj.y
9
>>> mathObj.x * mathObj.y
20
We could have used variables “x” and “y” to accomplish the same thing, but
in our case, mathObj.x and mathObj.y are related by the instance name, mathObj. This is what we mean by using classes as namespace containers. mathObj.x and mathObj.y are known as instance attributes because they are only attributes of their instance object (mathObj), not of the class(MyData). As we will see later on in this chapter, these attributes really are
dynamic in nature: you do not need to pre-declare or pre-assign them in the
constructor or anywhere else.
我们当然也可以使用变量“x”,“y”来完成同样的事情,但本例中,实例名字mathObj将mathObj.x和mathObj.y关联起来。这就是我们所说的使用类作为名字空间容器。mathObj.x 和 mathObj.y 是实例属性,因为它们不是类MyData的属性,而是实例对象(mathObj)的独有属性。本章后面,我们将看到这些属性实质上是动态的:你不需要在构造器中,或其它任何地方为它们预先声明或者赋值。
Methods
方法
One way we can improve our use of classes is to add functions to them. These class functions are known by their more common name, methods. In Python, methods are defined as part of the class definition, but can be invoked only on an instance. In other words, the path one must take to finally be able to call a method goes like this: (1) define the class (and the methods), (2) create an instance, and finally, (3) invoke the method on that instance. Here is an example class with a method:
我们改进类的方式之一就是给类添加功能。类的功能有一个更通俗的名字叫方法。在Python中,方法定义在类定义中,但只能被实例所调用。也就是说,调用一个方法的最终途径必须是这样的:(1)定义类(和方法),(2)创建一个实例(3)最后一步,用这个实例调用方法。例如:
class MyDataWithMethod(object): # 定义类
def printFoo(self): # 定义方法
print 'You invoked printFoo()!'
You will notice the self argument, which must be present in all method declarations. That argument, representing the instance object, is passed to the method implicitly by the interpreter when you invoke a method on an instance, so you, yourself, do not have to worry about passing anything in (specifically self, which is automatically passed in for you).
For example, if you have a method that takes two arguments, all of your calls should only pass in the second argument. Python passes in self for you as the first. If you make a mistake, do not worry about it. When an error occurs, Python will tell you that you have passed in the wrong number of arguments. You may make this mistake only once anyway … you’ll certainly remember each time after that!
你可能注意到了self参数,它在所有的方法声明中都存在。这个参数代表实例对象本身,当你用实例调用方法时,由解释器悄悄地传递给方法的,所以,你不需要自己传递self进来,因为它是自动传入的。举例说明一下,假如你有一个带两参数的方法,所有你的调用只需要传递第二个参数,Python把self作为第一个参数传递进来,如果你犯错的话,也不要紧。Python将告诉你传入的参数个数有误。总之,你只会犯一次错,下一次———你当然就记得了!
The requirement of the instance (self) in each method’s signature will be something new to those of you coming from C++ or Java, so be aware of that.
这种需要在每个方法中给出实例(self)的要求对于那些使用C++或Java的人,可能是一种新的体验,所以请意识到这点。
It is all part of Python’s philosophy of being explicitly clear. In those other lan- guages, self is called "this." You can find out more about self in the Core Note in Section 13.7 on page 540. Requiring the instance only applies to regu- lar methods and not static or class methods, although the latter requires the class rather than the instance. You can find out more about static and class methods in Section 13.8 on page 542.
Python的哲学本质上就是要明白清晰。在其它语言中,self称为“this”。可以在13.7节的“核心笔记”中找到有关self更多内容。一般的方法会需要这个实例(self),而静态方法或类方法不会,其中类方法需要类而不是实例。在第13.8节中可以看到有关静态方法和类方法的更多内容。
Now we will instantiate the class and invoke the method once we have an instance:
现在我们来实例化这个类,然后调用那个方法:
>>> myObj = MyDataWithMethod() # 创建实例
>>> myObj.printFoo() # 现在调用方法
You invoked printFoo()!
We conclude this introductory section by giving you a slightly more complex example of what you can do with classes (and instances) and also introduc- ing you to the special method __init__() as well as subclassing and inheritance.
在本节结束时,我们用一个稍复杂的例子来总结一下这部分内容,这个例子给出如何处理类(和实例),还介绍了一个特殊的方法__init__(),子类化及继承。
For those of you who are already familiar with object-oriented program- ming, __init__() is similar to the class constructor. If you are new to the world of OOP, a constructor is simply a special method that is typically called
to create a new object. In Python, __init__() is not really the constructor. You do not call “new” to create a new object. (Python does not even have a keyword called “new” anyway.) Instead, Python creates the instance for you and then calls __init__() during instantiation to define additional behavior that should occur when a class is instantiated, i.e., setting up initial values or running some preliminary diagnostic code―basically performing any special tasks or setup after the instance is created but before the new instance is returned from the instantiation call.
对于已熟悉面向对象编程的人来说,__init__()类似于类构造器。如果你初涉OOP世界,可以认为一个构造器仅是一个特殊的方法,它在创建一个新的对象时被调用。在Python中,__init__()实际上不是一个构造器。你没有调用“new”来创建一个新对象。(Python根本就没有“new”关键字)。取而代之,Python创建实例后,在实例化过程中,调用__init__()方法,当一个类被实例化时,就可以定义额外的行为,比如,设定初始值或者运行一些初步诊断代码———主要是在实例被创建后,实例化调用返回这个实例之前,去执行某些特定的任务或设置。
(We will add print statements to our methods to better illustrate when certain methods are called. It is generally not typical to have input or output statements in functions unless output is a predetermined characteristic of the body of code.)
(我们将把print语句添加到方法中,这样我们就清楚什么时候方法被调用了。通常,我们不把输入或输出语句放入函数中,除非预期代码体具有输出的特性。)
Creating a Class (Class Definition)
创建一个类(类定义)
class AddrBookEntry(object): # 类定义
'address book entry class'
def __init__(self, nm, ph): # 定义构造器
self.name = nm # 设置 name
self.phone = ph # 设置 phone
print 'Created instance for:', self.name
def updatePhone(self, newph): # 定义方法
self.phone = newph
print 'Updated phone# for:', self.name
In the definition for the AddrBookEntry class, we define two methods:
__init__() and updatePhone(). __init__() is called when instantia-
tion occurs, that is, when AddrBookEntry() is invoked. You can think of
instantiation as an implicit call to __init__() because the arguments
given to AddrBookEntry() are exactly the same as those that are
received by __init__() (except for self, which is passed automatically
for you).
在AddrBookEntry类的定义中,定义了两个方法:__init__()和updatePhone()。__init__()在实例化时被调用,即,在AddrBookEntry()被调用时。你可以认为实例化是对__init__()的一种隐式的调用,因为传给AddrBookEntry()的参数完全与__init__()接收到的参数是一样的(除了self,它是自动传递的)。
Recall that the self (instance object) argument is passed in automatically by the interpreter when the method is invoked on an instance, so in our __init__() above, the only required arguments are nm and ph, represent- ing the name and telephone number, respectively. __init__() sets these two instance attributes on instantiation so that they are available to the pro-
grammer by the time the instance is returned from the instantiation call.
As you may have surmised, the purpose of the updatePhone() method is
to replace an address book entry’s telephone number attribute.
回忆一下,当方法在实例中被调用时,self(实例对象)参数自动由解释器传递,所以在上面的__init__()中,需要的参数是nm和ph,它们分别表示名字和电话号码。__init__()在实例化时,设置这两个属性,以便,在实例从实例化调用中返回时,这两个属性对程序员是可见的了。你可能已猜到,updatePhone()方法的目的是替换地址本条目的电话号码属性。
Creating Instances (Instantiation)
创建实例(实例化)
>>> john = AddrBookEntry('John Doe', '408-555-1212') #为John Doe创建实例
>>> jane = AddrBookEntry('Jane Doe', '650-555-1212') #为Jane Doe创建实例
These are our instantiation calls, which, in turn, invoke __init__(). Recall that an instance object is passed in automatically as self. So, in your head, you can replace self in methods with the name of the instance. In the first case, when object john is instantiated, it is john.name that is set, as you can confirm below.
这就是实例化调用,它会自动调用__init__()。self把实例对象自动传入__init__()。你可以在脑子里把方法中的self用实例名替换掉。在上面第一个例子中,当对象john被实例化后,它的john.name就被设置了,你可在下面得到证实。
Also, without the presence of default arguments, both parameters to
__init__() are required as part of the instantiation.
另外,如果不存在默认的参数,那么传给__init__()的两个参数在实例化时是必须的。
Accessing Instance Attributes
访问实例属性
>>> john
<__main__.AddrBookEntry instance at 80ee610>
>>> john.name
'John Doe'
>>> john.phone
'408-555-1212'
>>> jane.name
'Jane Doe'
>>> jane.phone
'650-555-1212'
Once our instance was created, we can confirm that our instance attributes were indeed set by __init__() during instantiation. “Dumping” the instance within the interpreter tells us what kind of object it is. (We will discover later how we can customize our class so that rather than seeing the default Python object string, a more desired output can be customized.)
一旦实例被创建后,就可以证实一下,在实例化过程中,我们的实例属性是否确实被__init__()设置了。我们可以通过解释器“转储”实例来查看它是什么类型的对象。(我们以后将学到如何定制类来获得想要的Python对象字符串的输出形式,而不是现在看到的默认的Python对象字符串())
Method Invocation (via Instance)
方法调用(通过实例)
>>> john.updatePhone('415-555-1212') #更新John Doe的电话
>>> john.phone
'415-555-1212'
The updatePhone() method requires one argument (in addition to self): the new phone number. We check our instance attribute right after the call to updatePhone(), making sure that it did what was advertised.
updatePhone()方法需要一个参数(不计self在内):新的电话号码。在updatePhone()之后,立即检查实例属性,可以证实已生效。
Creating a Subclass
创建子类
Subclassing with inheritance is a way to create and customize a new class type with all the features of an existing class but without modifying the original class definition. The new subclass can be customized with special functionality unique only to that new class type. Aside from its relationship to its parent or base class, a subclass has all the same features as any regular class and is instantiated in the same way as all other classes. Note below that a parent class
is part of the subclass declaration:
靠继承来进行子类化是创建和定制新类类型的一种方式,新的类将保持已存在类所有的特性,而不会改动原来类的定义(指对新类的改动不会影响到原来的类,译者注)。对于新的类类型来说,这个新的子类可以定制只属于它的特定功能。除了与父类或基类的关系外,子类与通常的类没有什么区别,也像一般类一样进行实例化。注意下面,子类声明中提到了父类:
class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class'#员工地址本类
def __init__(self, nm, ph, id, em):
AddrBookEntry.__init__(self, nm, ph)
self.empid = id
self.email = em
def updateEmail(self, newem):
self.email = newem
print 'Updated e-mail address for:', self.name
We will now create our first subclass, EmplAddrBookEntry. In Python, when classes are derived, subclasses inherit the base class attributes, so in our case, we will not only define the methods __init__() and update- Email(), but EmplAddrBookEntry will also inherit the updatePhone() method from AddrBookEntry.
现在我们创建了第一个子类,EmplAddrBookEntry。Python中,当一个类被派生出来,子类继承了基类的属性,所以,在上面的类中,我们不仅定义了__init__(),updatEmail()方法,而且EmplAddrBookEntry还从AddrBookEntry中继承了updatePhone()方法。
Each subclass must define its own constructor if desired, otherwise the base class constructor will be called. However, if a subclass overrides a base class constructor, the base class constructor will not be called automatically―such a request must be made explicitly as we have above. For our subclass, we make an initial call to the base class constructor before performing any “local” tasks, hence the call to AddrBookEntry.__init__() to set the name and phone number. Our subclass sets two additional instance attributes, the employee ID
and e-mail address, which are set by the remaining lines of our constructor.
如果需要,每个子类最好定义它自己的构造器,不然,基类的构造器会被调用。然而,如果子类重写基类的构造器,基类的构造器就不会被自动调用了--这样,基类的构造器就必须显式写出才会被执行,像我们上面那样,用AddrBookEntry.__init__()设置名字和电话号码。我们的子类在构造器后面几行还设置了另外两个实例属性:员工ID和E-mail地址。
Note how we have to explicitly pass the self instance object to the base class constructor because we are not invoking that method on an instance. We are invoking that method on an instance of a subclass. Because we are not invoking it via an instance, this unbound method call requires us to pass an acceptable instance (self) to the method.
注意,这里我们要显式传递self实例对象给基类构造器,因为我们不是在其实例中调用那个方法而是在一个子类实例中调用那个方法。因为我们不是通过实例来调用它,这种未绑定的方法调用需要传递一个适当的实例(self)给方法。
We close this section with examples of how to create an instance of the
subclass, accessing its attributes and invoking its methods, including those inherited from the parent class.
本小节后面的例子,告诉我们如何创建子类的实例,访问它们的属性及调用它的方法,包括从父类继承而来的方法。
Using a Subclass
使用子类
>>> john = EmplAddrBookEntry('John Doe', '408-555-1212',42, 'john@spam.doe')
Created instance for: John Doe #给John Doe创建实例
>>> john
<__main__.EmplAddrBookEntry object at 0x62030>
>>> john.name
'John Doe'
>>> john.phone
'408-555-1212'
>>> john.email
'john@spam.doe'
>>> john.updatePhone('415-555-1212') Updated phone# for: John Doe
>>> john.phone
'415-555-1212'
>>> john.updateEmail('john@doe.spam') Updated e-mail address for: John Doe
>>> john.email
'john@doe.spam'
CORE STYLE: Naming classes, attributes, and methods
核心笔记:命名类、属性和方法
Class names traditionally begin with a capital letter. This is the standard
convention that will help you identify classes, especially during instantiation
(which would look like a function call otherwise). In particular, data attributes should sound like data value names, and method names should indicate
action toward a specific object or value. Another way to phrase this is: Use nouns for data value names and predicates (verbs plus direct objects) for methods.The data items are the objects acted upon, and the methods should indicate what action the programmer wants to perform on the object.
In the classes we defined above, we attempted to follow this guideline, with data values such as “name,” “phone,” and “email,” and actions such as
“updatePhone” and “updateEmail.” This is known as “mixedCase” or
“camelCase.” The Python Style Guide favors using underscores over camelCase, i.e,. “update_phone,” “update_email.” Classes should also be well named; some of those good names include “AddrBookEntry,”
“RepairShop,” etc.
类名通常由大写字母打头。这是标准惯例,可以帮助你识别类,特别是在实例化过程中(有时看起来像函数调用)。还有,数据属性(译者注:变量或常量)听起来应当是数据值的名字,方法名应当指出对应对象或值的行为。另一种表达方式是:数据值应该使用名词作为名字,方法使用谓词(动词加对象)。数据项是操作的对象、方法应当表明程序员想要在对象进行什么操作。在上面我们定义的类中,遵循了这样的方针,数据值像“name”,“phone”和“email”,行为如“updatePhone”,“updateEmail”。这就是常说的“混合记法(mixedCase)”或“骆驼记法(camelCase)”。Python规范推荐使用骆驼记法的下划线方式,比如,“update_phone”,“update_email”。类也要细致命名,像“AddrBookEntry”,“RepairShop”等等就是很好的名字。
We hope that you now have some understanding of how object-oriented programming is accomplished using Python. The remaining sections of this chapter will take you deeper into all the facets of object-oriented program- ming and Python classes and instances.
我希望你已初步理解如何在Python中进行面向对象编程了。本章其它小节将带你深入面向对象编程,Python类及实例的方方面面。
13.2 Object-Oriented Programming
面向对象编程
The evolution of programming has taken us from a sequence of step-by-step instructions in a single flow of control to a more organized approach whereby blocks of code could be cordoned off into named subroutines and defined functionality. Structured or procedural programming lets us organize our programs into logical blocks, often repeated or reused. Creating applications becomes a more logical process; actions are chosen which meet the specifications, then data are created to be subjected to those actions. Deitel and Deitel refer to structured programming as “action-oriented” due to the fact that logic must be “enacted” on data that have no associated behaviors.
编程的发展已经从简单控制流中按步的指令序列进入到更有组织的方式中,依靠代码块可以形成命名子程序和完成既定的功能。结构化的或过程性编程可以让我们把程序组织成逻辑块,以便重复或重用。创建程序的过程变得更具逻辑性;选出的行为要符合规范,才可以约束创建的数据。迪特尔父子(这里指DEITEL系列书籍作者Harvey M.Deitel和Paul James Deitel父子,译者注)认为结构化编程是“面向行为”的,因为事实上,即使没有任何行为的数据也必须“规定”逻辑性。
However, what if we could impose behavior on data? What if we were able
to create or program a piece of data modeled after real-life entities that embody both data characteristics along with behaviors? If we were then able
to access the data attributes via a set of defined interfaces (aka a set of accessor functions), such as an automated teller machine (ATM) card or a personal check to access your bank account, then we would have a system of “objects” where each could interact not only with itself, but also with other objects in a larger picture.
然而,如果我们能对数据加上动作呢?如果我们所创建和编写的数据片段,是真实生活中实体的模型,内嵌数据体和动作呢?如果我们能通过一系列已定义的接口(又称存取函数集合)访问数据属性,像自动取款机卡或能访问你的银行帐号的个人支票,我们就有了一个“对象”系统,从大的方面来看,每一个对象既可以与自身进行交互,也可以与其它对象进行交互。
Object-oriented programming takes this evolutionary step by enhancing structured programming to enable a data/behavior relationship: data and logic are now described by a single abstraction with which to create these objects. Real-world problems and entities are stripped down to their bare essentials, providing an abstraction from which they can be coded similarly or into objects that can interact with objects in the system. Classes provide the definitions of such objects, and instances are realizations of such definitions. Both are vital components for object-oriented design (OOD), which simply means to build your system architected in an object-oriented fashion.
面向对象编程踩上了进化的步伐,增强了结构化编程,实现了数据与动作的融合:数据层和逻辑层现在由一个可用以创建这些对象的简单抽象层来描述。现实世界中的问题和实体完全暴露了本质,从中提供的一种抽象,可以用来进行相似编码,或者编入能与系统中对象进行交互的对象中。类提供了这样一些对象的定义,实例即是这些定义的实现。二者对面向对象设计(object-oriented design,OOD)来说都是重要的,OOD仅意味来创建你采用面向对象方式架构来创建系统。
13.2.1 Relationship between OOD and OOP
面向对象设计与面向对象编程的关系
Object-oriented design does not specifically require an object-oriented pro- gramming language. Indeed, OOD can be performed in purely structural lan- guages such as C, but this requires more effort on the part of the programmer who must build data types with object qualities and characteristics. Naturally, OOP is simplified when a language has built-in OO properties that enable smoother and more rapid development of OO programs.
面向对象设计(OOD)不会特别要求面向对象编程语言。事实上,OOD可以由纯结构化语言来实现,比如C,但如果想要构造具备对象性质和特点的数据类型,就需要在程序上作更多的努力。当一门语言内建OO特性,OO编程开发就会更加方便高效。
Conversely, an object-oriented language may not necessarily force one to write OO programs. C++ can be used simply as a “better C.” Java, on the other hand, requires everything to be a class, and further dictates only one class definition per source file. In Python, however, neither classes nor OOP are required for everyday programming. Even though it is a language that is object-oriented by design and that has constructs to support OOP, Python does not restrict or require you to write OO code for your application. Rather, OOP is a powerful tool that is at your disposal when you are ready to evolve, learn, transition, or otherwise move toward OOP. The creator of Python often refers to this phenomenon as being able to “see the forest through the trees.”
另一方面,一门面向对象的语言不一定会强制你写OO方面的程序。例如C++可以被认为“更好的C”;而Java,则要求万物皆类,此外还规定,一个源文件对应一个类定义。然而,在Python中,类和OOP都不是日常编程所必需的。尽管它从一开始设计就是面向对象的,并且结构上支持OOP,但Python没有限定或要求你在你的应用中写OO的代码。OOP是一门强大的工具,不管你是准备进入,学习,过渡,或是转向OOP,都可以任意支配。
13.2.2 Real-World Problems
现实世界中的问题
One of the most important reasons to consider working in OOD is that it provides a direct approach to modeling and solving real-world problems and situations. For example, let us attempt to model an automobile mechanic shop where you would take your car in for repair. There are two general entities we would have to create: humans who interact with and in such a “system,” and a physical location for the activities that define a mechanic shop. Since there are more and different types of the former, we will describe them first, then conclude with the latter.
A class called Person would be created to represent all humans involved in such an activity. Instances of Person would include the Customer, the
Mechanic, and perhaps the Cashier. Each of these instances would have similar as well as unique behaviors. For example, all would have the talk()
method as a means of vocal communication as well as a drive_car() method. Only the Mechanic would have the repair_car() method and
only the Cashier would have a ring_sale() method. The Mechanic will have a repair_certification attribute while all Persons would have a
drivers_license attribute.
考虑用OOD来工作的一个最重要的原因,在于它直接提供建模和解决现实世界问题和情形的途径。比如,让你来试着模拟一台汽车维修店,可以让你停车进行维修。我们需要建两个一般实体:处在一个“系统”中并与其交互的人类,和一个修理店,它定义了物理位置,用于人类活动。因为前者有更多不同的类型,我将首先对它进行描述,然后描述后者。在此类活动中,一个名为Person的类被创建以用来表示所有的人。Person的实例可以包括消费者(Customer),技工(Mechanic),还可能是出纳员(Cashier)。这些实例具有相似的行为,也有独一无二的行为。比如,他们能用声音进行交流,都有talk()方法,还有drive_car()方法。不同的是,技工有repair_car()方法,而出纳有ring_sale()方法。技工有一个repair_certification属性,而所有人都有一个drivers_license属性。
Finally, all of these instances would be participants in one overseeing class,
called the RepairShop, which would have operating_hours, a data attribute that accesses time functionality to determine when Customers can bring in their vehicles and when Employees such as Mechanics and Cashiers show up for work. The RepairShop might also have a AutoBay class that would have instances such as SmogZone, TireBrakeZone, and perhaps one called GeneralRepair.
最后,所有这些实例都是一个检查(overseeing)类RepairShop的参与者,后者具有一个叫operating_hours的数据属性,它通过时间函数来确定何时顾客来修车,何时职员技工和出纳员来上班。RepairShop可能还有一个AutoBay类,拥有SmogZone,TireBrakeZone等实例,也许还有一个叫GeneralRepair的实例。
The point of our fictitious RepairShop is to show one example of how classes and instances plus their behaviors can be used to model a true-to- life scenario. You can probably also imagine classes such as an Airport, a Restaurant, a ChipFabPlant, a Hospital, or even a MailOrderMusic business, all complete with their own participants and functionality.
我们所编的RepairShop的一个关键点是要展示类和实例加上它们的行为是如何用来对现实生活场景建模的。同样,你可以把诸如机场,餐厅,晶蕊,医院,其至一个邮订音乐公司想像为类,它们完全具备各自的参与者和功能性。
13.2.3 *Buzzword-Compliance
13.2.3*常用术语
For those of you who are already familiar with all the lingo associated with
OOP, here is how Python stacks up:
对于已熟悉有关OOP术语的朋友来说,看Python中是怎么称呼的:
Abstraction/Implementation
抽象/实现
Abstraction refers to the modeling of essential aspects, behavior, and charac- teristics of real-world problems and entities, providing a relevant subset as the definition of a programmatic structure that can realize such models. Abstrac- tions not only contain the data attributes of such a model, but also define inter- faces with that data. An implementation of such an abstraction is the realization
of that data and the interfaces that go along with it. Such a realization should remain hidden from and irrelevant to the client programmer.
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于描绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户程序应当是透明而且无关的。
Encapsulation/Interfaces
封装/接口
Encapsulation describes the concept of data/information hiding and provid- ing interfaces or accessor functions to the data attributes. Direct access to data by any client, bypassing the interfaces, goes against the principles of encapsulation, but the programmer is free to allow such access. As part of the implementation, the client should not even know how the data attributes are architected within the abstraction. In Python, all class attributes are public, but names may be “mangled” to discourage unauthorized access, but other- wise not prevented. It is up to the designer to provide the appropriate inter- faces to the data so that the client programmer does not have to resort to manipulating the encapsulated data attributes.
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
Composition
合成
Composition extends our description of classes, enabling multiple yet distinct classes to be combined into a larger entity to solve a real-world problem. Composition describes a singular, complex system such as a class made up of other, smaller components such as other classes, data attributes, and behaviors, all of which are combined, embodying “has-a” relationships. For example, the RepairShop “has a” Mechanic (hopefully at least one) and also “has a” Customer (again, hopefully at least one).
合成扩充了对类的描述,使得多个不同的类合成为一个大的类,来解决现实问题。合成描述了一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为,所有这些合在一起,彼此是“有一个”的关系。比如,RepairShop“有一个”技工(应该至少有一个吧),还“有一个”顾客(至少一个)。
These components are composed either via association, meaning that access to subcomponents is granted (for the RepairShop, a customer may enter and request a SmogCheck, the client programmer interfacing with components of the RepairShop), or aggregation, encapsulating components that are then accessed only via defined interfaces, and again, hidden from the client programmer. Continuing our example, the client programmer may be able to make a SmogCheck request on behalf of the Customer, but has no ability to interact with the SmogZone part of the RepairShop, which is accessed only via internal controls of the RepairShop when the smogCheckCar() method is called. Both forms of composition are supported in Python.
这些组件要么通过联合关系组在一块,意思是说,对子组件的访问是允许的(对RepairShop来说,顾客可能请求一个SmogCheck,客户程序这时就是与RepairShop的组件进行交互),要么是聚合在一起,封装的组件仅能通过定义好的接口来访问,对于客户程序来说是透明的。继续我的例子,客户程序可能会建立一个SmogCheck请求来代表顾客,但不能够同RepairShop的SmogZone部分进行交互,因为SmogZone是由RepairShop内部控制的,只能通过smogCheckCar()方法调用。Python支持上述两种形式的合成。
Derivation/Inheritance/Hierarchy
派生/继承/继承结构
Derivation describes the creation of subclasses, new classes that retain all desired data and behavior of the existing class type but permit modification or other customization, all without having to modify the original class definition. Inheritance describes the means by which attributes of a subclass are “bequeathed from” an ancestor class. From our earlier example, a Mechanic may have more car skill attributes than a Customer, but individually, each “is a” Person, so it is valid to invoke the talk() method, which is common to all instances of Person, for either of them. Hierarchy describes multiple “generations” of derivation which can be depicted graphically as a “family tree,” with successive subclasses having relationships with ancestor classes.
派生描述了子类的创建,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。继承描述了子类属性从祖先类继承这样一种方式。从前面的例子中,技工可能比顾客多个汽车技能属性,但单独的来看,每个都“是一个”人,所以,不管对谁而言调用talk()都是合法得,因为它是人的所有实例共有的。继承结构表示多“代”派生,可以描述成一个“族谱”,连续的子类,与祖先类都有关系。
Generalization/Specialization
泛化/特化
Generalization describes all the traits a subclass has with its parent and ancestor classes, so subclasses are considered to have an “is-a” relationship with ancestor classes because a derived object (instance) is an “example” of an ancestor class. For example, a Mechanic “is a” Person, a Car “is a” Vehicle, etc. In the family tree diagram we alluded to above, we can draw lines from subclasses to ancestors indicating “is-a” relationships. Specialization is the term that describes all the customization of a subclass, i.e., what
attributes make it differ from its ancestor classes.
泛化表示所有子类与其父类及祖先类有一样的特点,所以子类可以认为同祖先类是“是一个”的关系,因为一个派生对象(实例)是祖先类的一个“例子”。比如,技工“是一个”人,车“是一个”交通工具,等等。在上面我们间接提到的族谱图中,我们可以从子类到祖先类画一条线,表示“是一个”的关系。特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
Polymorphism
多态
The concept of polymorphism describes how objects can be manipulated and accessed using attributes and behaviors they have in common without regard to their specific class. Polymorphism indicates the presence of dynamic (aka late, runtime) binding, allowing for overriding and runtime type determination and verification.
多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。多态表明了动态(又名,运行时)绑定的存在,允计重载及运行时类型确定和验证。
Introspection/Reflection
自省/反射
Introspection is what gives you, the programmer, the ability to perform an activity such as “manual type checking.” Also called reflection, this property describes how information about a particular object can be accessed by itself during runtime. Would it not be great to have the ability to take an object passed to you and be able to find out what it is capable of ? This is a powerful feature that you will encounter frequently in this chapter. The dir() and type() built-in functions would have a very difficult time working if Python did not support some sort of introspection capability. Keep an eye out for these calls as well as for special attributes like __dict__, __name__, and
__doc__. You may even be familiar with some of them already!
自省表示给予你,程序员,某种能力来进行像“手工类型检查”的工作,它也被称为反射。这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这样的功能不是很好吗?这是一项强大的特性,在本章中,你会时常遇到。如果Python不支持某种形式的自省功能,dir()和type()内建函数,将很难正常工作。请密切关注这些调用,还有那些特殊属性,像__dict__,__name__及__doc__。可能你对其中一些已经很熟悉了!
13.3 Classes
类
Recall that a class is a data structure that we can use to define objects that hold together data values and behavioral characteristics. Classes are entities that are the programmatic form of an abstraction for a real-world problem, and instances are realizations of such objects. One analogy is to liken classes to blueprints or molds with which to make real objects (instances). So why the term “class”? The term most likely originates from using classes to identify and categorize biological families of species to which specific creatures belong and can be derived into similar yet distinct subclasses. Many of these features apply to the concept of classes in programming.
回想一下,类是一种数据结构,我们可以用它来定义对象,后者把数据值和行为特性融合在一起。类是现实世界的抽象的实体以编程形式出现。实例是这些对象的具体化。可以类比一下,类是蓝图或者模型,用来产生真实的物体(实例)。因此为什么是术语“class”?这个术语很可能起源于使用类来识别和归类特定生物所属的生物种族,类还可以派生出相似但有差异的子类。编程中类的概念就应用了很多这样的特征。
In Python, class declarations are very similar to function declarations, a header line with the appropriate keyword followed by a suite as its definition,as indicated below:
在Python中,类声明与函数声明很相似,头一行用一个相应的关键字,接下来是一个作为它的定义的代码体,如下所示:
def functionName(args):
'function documentation string' #函数文档字符串
function_suite #函数体
class ClassName(object):
'class documentation string' #类文档字符串
class_suite #类体
Both allow you to create functions within their declaration, closures or inner functions for functions within functions, and methods for functions defined in classes. The biggest difference is that you run functions but create objects with classes. A class is like a Python container type on steroids. In this section, we will take a close look at classes and what types of attributes they have. Just remember to keep in mind that even though classes are objects (everything in Python is an object), they are not realizations of the objects they are defining. We will look at instances in the next section, so stay tuned for that. For now, the limelight is beamed strictly on class objects.
二者都允许你在他们的声明中创建函数,闭包或者内部函数(即函数内的函数),还有在类中定义的方法。最大的不同在于你运行函数,而类会创建一个对象。类就像一个Python容器类型。在这部分,我们将特别留意类及它们有什么类型的属性。这只要记住,尽管类是对象(在Python中,一切皆对象),但正被定义时,它们还不是对象的实现。在下节中会讲到实例,所以拭目以待吧。不过现在,我们集中讲解类对象。
When you create a class, you are practically creating your own kind of data type. All instances of that class are similar, but classes differ from one another (and so will instances of different classes by nature). Rather than playing with toys that came from the manufacturer and were bestowed upon you as gifts, why not design and build your own toys to play with?
当你创建一个类,你就实际创建了一个你自己的数据类型。所以这个类的实例都是相似的,但类之间彼此是有区别的(因此,不同类的实例自然也不可能相同了)。与其玩那些从玩具商那买来的玩具礼物,为什么不设计并创造你自己的玩具来玩呢?
Classes also allow for derivation. You can create subclasses that are classes but inherit all of the features and attributes of the “parent” class. Starting in Python 2.2, you can subclass built-in types instead of just other classes.
类还允许派生。你可以创建一个子类,它也是类,而且继续了父类所有的特征和属性。从Python2.2开始,你也可以从内建类型中派生子类,而不是仅仅从其它类。
13.3.1 Creating Classes
创建类
Python classes are created using the class keyword. In the simple form of class declarations, the name of the class immediately follows the keyword:
Python类使用class关键字来创建。简单的类的声明可以是关键字后紧跟类名:
class ClassName(bases):
'class documentation string' #'类文档字符串'
class_suite #类体
As outlined briefly earlier in this chapter, bases is the set of one or more parent classes from which to derive; and class_suite consists of all the component statements, defining class members, data attributes, and func- tions. Classes are generally defined at the top-level of a module so that instances of a class can be created anywhere in a piece of source code where the class is defined.
本章前面的概述中提到,基类是一个或多个用于继承的父类的集合;类体由所有声明语句,类成员定义,数据属性和函数组成。类通常在一个模块的顶层进行定义,以便类实例能够在类所定义的源代码文件中的任何地方被创建。
13.3.2 Declaration versus Definition
13.3.2 声明与定义
As with Python functions, there is no distinction between declaring and defining classes because they occur simultaneously, i.e., the definition (the class suite) immediately follows the declaration (header line with the class keyword) and the always recommended, but optional, documenta- tion string. Likewise, all methods must also be defined at this time. If you are familiar with the OOP terms, Python does not support pure virtual functions (à la C++) or abstract methods (as in Java), which coerce the programmer to define a method in a subclass. As a proxy, you can simply raise the NotImplementedError exception in the base class method to get the same effect.
对于Python函数来说,声明与定义类没什么区别,因为他们是同时进行的,定义(类体)紧跟在声明(含class关键字的头行[header line])和可选(但总是推荐使用)的文档字符串后面。同时,所有的方法也必须同时被定义。如果对OOP很熟悉,请注意Python并不支持纯虚函数(像C++)或者抽象方法(如在JAVA中),这些都强制程序员在子类中定义方法。作为替代方法,你可以简单地在基类方法中引发NotImplementedError异常,这样可以获得类似的效果。
13.4 Class Attributes
类属性
What is an attribute? An attribute is a data or functional element that belongs to another object and is accessed via the familiar dotted-attribute notation. Some Python types such as complex numbers have data attributes (real and imag), while others such as lists and dictionaries have methods (functional attributes).
什么是属性呢?属性就是属于另一个对象的数据或者函数元素,可以通过我们熟悉的句点属性标识法来访问。一些Python类型比如复数有数据属性(实部和虚部),而另外一些,像列表和字典,拥有方法(函数属性)。
One interesting side note about attributes is that when you are accessing an attribute, it is also an object and may have attributes of its own which you can then access, leading to a chain of attributes, i.e., myThing.subThing.sub- SubThing, etc. Some familiar examples are:
有关属性的一个有趣的地方是,当你正访问一个属性时,它同时也是一个对象,拥有它自己的属性,可以访问,这导致了一个属性链,比如,myThing,subThing,subSubThing.等等。常见例子如下:
? sys.stdout.write('foo')
? print myModule.myClass.__doc__
? myList.extend(map(upper, open('x').readlines()))
Class attributes are tied only to the classes in which they are defined, and since instance objects are the most commonly used objects in everyday OOP, instance data attributes are the primary data attributes you will be using. Class data attributes are useful only when a more “static” data type is required which is independent of any instances, hence the reason we are making the next section advanced, optional reading. (If you are unfamiliar with static, it just means a value that hangs around a function for each call, or a piece of data in a class that is the same across all instances. More about static data in the next subsection.)
类属性仅与其被定义的类相绑定,并且因为实例对象在日常OOP中用得最多,实例数据属性是你将会一直用到的主要数据属性。类数据属性仅当需要有更加“静态”数据类型时才变得有用,它和任何实例都无关,因此,这也是为什么下一节被表为高级主题,你可以选读。(如果你对静态不熟,它表示一个值,不会因为函数调用完毕而消失,它在每两个函数调用的间隙都存在。或者说,一个类中的一些数据对所有的实例来说,都是固定的。有关静态数据详细内容,见下一小节.)
In the succeeding subsection, we will briefly describe how methods in Python are implemented and invoked. In general, all methods in Python have the same restriction: they require an instance before they can be called.
接下来的一小节中,我们将简要描述,在Python中,方法是如何实现及调用的。通常,Python中的所有方法都有一个限制:在调用前,需要创建一个实例。
13.4.1 Class Data Attributes
类的数据属性
Data attributes are simply variables of the class we are defining. They can be used like any other variable in that they are set when the class is created and can be updated either by methods within the class or elsewhere in the main part of the program.
数据属性仅仅是所定义的类的变量。它们可以像任何其它变量一样在类创建后被使用,并且,要么是由类中的方法来更新,要么是在主程序其它什么地方被更新。
Such attributes are better known to OO programmers as static members, class variables, or static data. They represent data that is tied to the class object they belong to and are independent of any class instances. If you are a Java or C++ programmer, this type of data is the same as placing the static keyword in front of a variable declaration.
这种属性已为OO程序员所熟悉,即静态变量,或者是静态数据。它们表示这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字。
Static members are generally used only to track values associated with classes. In most circumstances, you would be using instance attributes rather than class attributes. We will compare the differences between class and instance attributes when we formally introduce instances.
静态成员通常仅用来跟踪与类相关的值。大多数情况下,你会考虑用实例属性,而不是类属性。在后面,我们正式介绍实例时,将会对类属性及实例属性进行比较。
Here is an example of using a class data attribute (foo):
看下面的例子,使用类数据属性(foo):
>>> class C(object):
... foo = 100
>>> print C.foo
100
>>> C.foo = C.foo + 1
>>> print C.foo
101
Note that nowhere in the code above do you see any references to class instances.
注意,上面的代码中,看不到任何类实例的引用。
13.4.2 Methods
方法
A method, such as the myNoActionMethod method of the MyClass class in the example below, is simply a function defined as part of a class definition
(thus making methods class attributes). This means that myNoActionMethod applies only to objects (instances) of MyClass type. Note how myNoActionMethod is tied to its instance because invocation requires both names in the dotted attribute notation:
方法,比如下面,类MyClass中的myNoActionMethod方法,仅仅是一个作为类定义一部分定义的函数.(这使得方法成为类属性)。这表示myNoActionMethod仅应用在MyClass类型的对象(实例)上。这里,myNoActionMethod是通过句点属性标识法与它的实例绑定的。
>>> class MyClass(object):
def myNoActionMethod(self):
pass
>>> mc = MyClass()
>>> mc.myNoActionMethod()
Any call to myNoActionMethod by itself as a function fails:
任何像函数一样对myNoActionMethod自身的调用都将失败:
>>> myNoActionMethod() Traceback (innermost last):
File "<stdin>", line 1, in ?
myNoActionMethod() NameError: myNoActionMethod
A NameError exception is raised because there is no such function in the global namespace. The point is to show you that myNoActionMethod is a method, meaning that it belongs to the class and is not a name in the global namespace. If myNoActionMethod was defined as a function at the top-level, then our call would have succeeded.
引发了NameError异常,因为在全局名字空间中,没有这样的函数存在。这就告诉你myNoActionMethod是一个方法,表示它属于一个类,而不是全局空间中的名字。如果myNoActionMethod是在顶层作为函数被定义的,那么我们的调用则会成功。
We show you below that even calling the method with the class object fails.
下面展示的是,甚至由类对象调用此方法也失败了。
>>> MyClass.myNoActionMethod() Traceback (innermost last):
File "<stdin>", line 1, in ? MyClass.myNoActionMethod()
TypeError: unbound method must be called with class instance 1st argument
This TypeError exception may seem perplexing at first because you know that the method is an attribute of the class and so are wondering why there is
a failure. We will explain this next.
TypeError异常初看起来很让人困惑,因为你知道这种方法是类的一个属性,因此,一定很想知道为何为失败吧?接下来将会解释这个问题。
Binding (Bound and Unbound Methods)
绑定(绑定及非绑定方法)
In keeping with OOP tradition, Python imposes the restriction that methods cannot be invoked without instances. An instance must be used to perform method calls. This restriction describes Python’s concept of binding, where methods must be bound (to an instance) in order to be invoked directly. Unbound methods may also be called, but an instance object must be provided explicitly in order for the invocation to succeed. However, regardless of binding, methods are inherently attributes of the class they are defined in, even if they are almost always invoked via an instance. We will further explore bound and unbound methods later in Section 13.7.
为与OOP惯例保持一致,Python严格要求,没有实例,方法是不能被调用的。这种限制即Python所描述的绑定概念(binding),在此,方法必须绑定(到一个实例)才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能确保调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。在13.7节中,我们会更深入地探索本主题。
13.4.2 Determining Class Attributes
决定类的属性
There are two ways to determine what attributes a class has. The simplest way is to use the dir() built-in function. An alternative is to access the class dictionary attribute __dict__, one of a number of special attributes that is common to all classes. Let us take a look at an example:
要知道一个类有哪些属性,有两种方法。最简单的是使用dir()内建函数。另外是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。看一下下面的例子:
>>> class MyClass(object):
... 'MyClass class definition' #MyClass类定义
... myVersion = '1.1' # static data 静态数据
... def showMyVersion(self): # method 方法
... print MyClass.myVersion
...
Using the class defined above, let us use dir() and the special class attribute __dict__ to see this class’s attributes:
根据上面定义的类,让我们使用dir()和特殊类属性__dict__来查看一下类的属性:
>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__', '__weakref__', 'myVersion',
'showMyVersion']
>>> MyClass.__dict__
<dictproxy object at 0x62090>
>>> print MyClass.__dict__
{'showMyVersion': ,
'__dict__': ,
'myVersion': '1.1', '__weakref__': <attribute
'__weakref__' of 'MyClass' objects>, '__doc__':
'MyClass class definition'}
There are a few more attributes added for new-style classes as well as a more robust dir() function. Just for comparison, here is what you would see for classic classes:
在新式类中,还新增加了一些属性,dir()也变得更健壮。作为比较,可以看下经典类是什么样的:
>>> dir(MyClass)
['__doc__', '__module__', 'showMyVersion', 'myVersion']
>>>
>>> MyClass.__dict__
{'__doc__': None, 'myVersion': 1, 'showMyVersion':
<function showMyVersion at 950ed0>, '__module__':
'__main__'}
As you can tell, dir() returns a list of (just the) names of an object’s attributes while __dict__ is a dictionary whose attribute names are the keys and whose values are the data values of the corresponding attribute objects.
从上面可以看到,dir()返回的仅是对象的属性的一个名字列表,而__dict__返回的是一个字典,它的键(keys)是属性名,键值(values)是相应的属性对象的数据值。
The output also reveals two familiar attributes of our class MyClass, showMyVersion and myVersion, as well as a couple of new ones. These attributes, __doc__ and __module__, are special class attributes that all classes have (in addition to __dict__). The vars() built-in function returns the contents of a class’s __dict__ attribute when passed the class object as its argument.
结果还显示了MyClass类中两个熟悉的属性,showMyVersion 和 myVersion,以及一些新的属性。这些属性,__doc__及__module__,是所有类都具备的特殊类属性(另外还有__dict__)。。内建的vars()函数接受类对象作为参数,返回类的__dict__属性的内容。
13.4.3 Special Class Attributes
特殊的类属性
For any class C, Table 13.1 represents a list of all the special attributes of C:
对任何类C,表13.1显示了类C的所有特殊属性:
Table 13.1 Special Class Attributes
表13.1 特殊类属性
C.__name__ String name of class C 类C的名字(字符串)
C.__doc__ Documentation string for class C 类C的文档字符串
C.__bases__ Tuple of class C’s parent classes 类C的所有父类构成的元组
C.__dict__ Attributes of C 类C的属性
C.__module__ Module where C is defined (new in 1.5) 类C定义所在的模块(1.5版本新增)
C.__class__ Class of which C is an instance (new-style classes only) 实例C对应的类(仅新式类中)
Using the class MyClass we just defined above, we have the following:
根据上面定义的类MyClass,有如下结果:
>>> MyClass.__name__
'MyClass'
>>> MyClass.__doc__
'MyClass class definition'
>>> MyClass.__bases__
(<type 'object'>,)
>>> print MyClass.__dict__
{'__doc__': None, 'myVersion': 1, 'showMyVersion':
<function showMyVersion at 950ed0>, '__module__': '__main__'}
>>> MyClass.__module__
'__main__'
>>> MyClass.__class__
<type 'type'>
__name__ is the string name for a given class. This may come in handy in cases where a string is desired rather than a class object. Even some built-in types have this attribute, and we will use one of them to showcase the usefulness of the __name__ string.
__name__是给定类的字符名字。它适用于那种只需要字符串(类对象的名字),而非类对象本身的情况。甚至一些内建的类型也有这个属性,我们将会用到其中之一来展示__name__字符串的益处。
The type object is an example of one built-in type that has a __name__ attribute. Recall that type() returns a type object when invoked. There may be cases where we just want the string indicating the type rather than an object. We can use the __name__ attribute of the type object to obtain the string name. Here is an example:
类型对象是一个内建类型的例子,它有__name__的属性。回忆一下,type()返回被调用对象的类型。这可能就是那种我们所说的仅需要一个字符串指明类型,而不需要一个对象的情况。我们能可以使用类型对象的__name__属性来取得相应的字符串名。如下例示:
>>> stype = type('What is your quest?')
>>> stype # stype is a type object stype是一个类型对象
<type 'string'>
>>> stype.__name__ # get type as a string 得到类型名(字符串表示)
'string'
>>>
>>> type(3.14159265) # also a type object 又一个类型对象
>>> type(3.14159265).__name__ # get type as a string 得到类型名(字符串表示)
'float'
__doc__ is the documentation string for the class, similar to the documentation string for functions and modules, and must be the first unassigned string succeeding the header line. The documentation string is not inherited by derived classes, an indication that they must contain their own documentation strings.
__doc__是类的文档字符串,与函数及模块的文档字符串相似,必须紧随头行(header line)后的字符串。文档字符串不能被派生类继承,也就是说派生类必须含有它们自己的文档字符串。
__bases__ deals with inheritance, which we will cover later in this chapter;it contains a tuple that consists of a class’s parent classes.
本章后面会讲到,__bases__用来处理继承,它包含了一个由所有父类组成的元组。
The aforementioned __dict__ attribute consists of a dictionary containing the data attributes of a class. When accessing a class attribute, this dictionary is searched for the attribute in question. If it is not found in __dict__, the hunt continues in the dictionary of base classes, in “depth-first search” order. The set of base classes is searched in sequential order, left-to-right in the same order as they are defined as parent classes in a class declaration. Modification of a class attribute affects only the current class’s dictionary; no base class __dict__ attributes are ever modified.
前述的__dict__属性包含一个字典,由类的数据属性组成。访问一个类属性的时候,Python解释器将会搜索字典以得到需要的属性。如果在__dict__中没有找到,将会在基类的字典中进行搜索,采用“深度优先搜索”顺序。基类集的搜索是按顺序的,从左到右,按其在类定义时,定义父类参数时的顺序。对类的修改会仅影响到此类的字典;基类的__dict__属性不会被改动的。
Python supports class inheritance across modules. To better clarify a class’s description, the __module__ was introduced in version 1.5 so that a class name is fully qualified with its module. We present the following example:
Python支持模块间的类继承。为更清晰地对类进行描述,1。1,1.5版本中引入了__module__,这样类名就完全由模块名所限定。看一下下面的例子:
>>> class C(object):
... pass
...
>>> C
<class __main__.C at 0x53f90>
>>> C.__module__
'__main__'
The fully qualified name of class C is "__main__.C", i.e., source_ module.class_name. If class C was located in an imported module, such as mymod, we would see the following:
类C的全名是“__main__.C”,比如,source_module.class_name。如果类C位于一个导入的模块中,如mymod,像下面的:
>>> from mymod import C
>>> C
<class mymod.C at 0x53ea0>
>>> C.__module__
'mymod'
In previous versions of Python without the special attribute __module__, it was much more difficult to ascertain the location of a class simply because classes did not use their fully qualified names.
在以前的版本中,没有特殊属性__module__,很难简单定位类的位置,因为类没有使用它们的全名。
Finally, because of the unification of types and classes, when you access the __class__ attribute of any class, you will find that it is indeed an instance of a type object. In other words, a class is a type now! Because clas- sic classes do not share in this equality (a classic class is a class object, and a type is a type object), this attribute is undefined for those objects.
最后,由于类型和类的统一性,当访问任何类的__class__属性时,你将发现它就是一个类型对象的实例。换句话说,一个类已是一种类型了。因为经典类并不认同这种等价性(一个经典类是一个类对象,一个类型是一个类型对象),对这些对象来说,这个属性并未定义。
13.5 Instances
实例
Whereas a class is a data structure definition type, an instance is a declaration of a variable of that type. In other words, instances are classes brought to life. Once a blueprint is provided, the next step to bring them to fruition. Instances are the objects that are used primarily during execution, and the types of all instances are the class from which they were instantiated. Prior to Python 2.2, instances were “instance types,” regardless of which class they came from.
如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。换言之,实例是有生命的类。就像设计完一张蓝图后,就是设法让它成为现实。实例是那些主要用在运行期时的对象,类被实例化得到实例,该实例的类型就是这个被实例化的类。在Python2.2版本之前,实例是“实例类型”,而不考虑它从哪个类而来。
13.5.1 Instantiation: Creating Instances by Invoking Class Object
13.5.1 初始化:通过调用类对象来创建实例
Many other OO languages provide a new keyword with which to create an instance of a class. Python’s approach is much simpler. Once a class has been defined, creating an instance is no more difficult than calling a function― literally. Instantiation is realized with use of the function operator, as in the following example:
很多其它的OO语言都提供new关键字,通过new可以创建类的实例。Python的方式更加简单。一旦定义了一个类,创建实例比调用一个函数还容易------不费吹灰之力。实例化的实现,可以使用函数操作符,如下示:
>>> class MyClass(object): # define class 定义类
... pass
>>> mc = MyClass() # instantiate class 初始化类
As you can see, creating instance mc of class MyClass consists of “calling” the class: MyClass(). The returned object is an instance of the class you called. When you “call” a class using the functional notation, the interpreter instantiates the object, and calls the closest thing Python has to a constructor (if you have written one [see the next section]) to perform any final customization such as setting instance attributes, and finally returns the instance to you.
可以看到,仅调用("calling")类:MyClass(),就创建了类MyClass的实例mc。返回的对象是你所调用类的一个实例。当使用函数记法来调用("call")一个类时,解释器就会实例化该对象,并且调用Python所拥有与构造函数最相近的东西(如果你定义了的话)来执行最终的定制工作,比如设置实例属性,最后将这个实例返回给你。
CORE NOTE: Classes and instances before and after Python 2.2
核心笔记:Python2.2前后的类和实例
Classes and types were unified in 2.2, making Python behave more like other object-oriented languages. Instances of any class or type are objects of those types. For example, if you ask Python to tell you, it will say that an instance mc of the MyClass class is an instance of the MyClass class. Redundant yes, but the interpreter will not lie. Likewise, it will tell you that 0 is an instance of the integer type:
类和类型在2.2版本中就统一了,这使得Python的行为更像其它面向对象编程语言。任何类或者类型的实例都是这种类型的对象。比如,如果你让Python告诉你,类MyClass的实例mc是否是类MyClass的一个实例。回答是肯定的,Python不会说谎。同样,它会告诉你零是integer类型的一个实例:
>>> mc = MyClass()
>>> type(mc)
<class '__main__.MyClass'>
>>> type(0)
<type 'int'>
But if you look carefully and compare MyClass with int, you will find that both are indeed types:
但如果你仔细看,比较MyClass和int,你将会发现二者都是类型(type):
>>> type(MyClass)
<type 'type'>
>>> type(int)
<type 'type'>
In contrast for those of you using classic classes and Python versions earlier than 2.2, classes are class objects and instances are instance objects.There
is no further relationship between the two object types other than an instance’s __class__ attribute refers to the class from which it was instantiated. Redefining MyClass as a classic class and running the same calls in Python 2.1 (note that int() has not been turned into a factory function yet … it was still only a regular built-in function):
对比一下,如果在Python早于2.2版本时,使用经典类,此时类是类对象,实例是实例对象。在这两个对象类型之间没有任何关系,除了实例的__class__属性引用了被实例化以得到该实例的类。把MyClass在Python2.1版本中作为经典类重新定义,并运行相同的调用(注意:int()那时还不具备工厂功能...它还仅是一个通常的内建函数):
>>> type(mc)
<type 'instance'>
>>> type(0)
<type 'int'>
>>>
>>> type(MyClass)
<type 'class'>
>>> type(int)
To avoid any confusion, just keep in mind that when you define a class, you are not creating a new type, just a new class object; and for 2.2 and after,
when you define a (new-style) class you are creating a new type.
为了避免任何混淆,你只要记住当你定义一个类时,你并没有创建一个新的类型,而是仅仅一个类对象;而对2.2及后续版本,当你定义一个(新式的)类后,你已创建了一个新的类型。
13.5.2 __init__() “Constructor ” Method
13.5.2 __init__() "构造器"方法
When the class is invoked, the first step in the instantiation process is to create the instance object. Once the object is available, Python checks if an __init__() method has been implemented. By default, no special actions are enacted on the instance without the definition of (or the overriding) of the special method __init__(). Any special action desired requires the programmer to implement __init__(), overriding its default behavior. If __init__() has not been implemented, the object is then returned and the instantiation process is complete.
当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python检查是否实现了__init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作.任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。如果__init__()没有实现,则返回它的对象,实例化过程完毕。
However, if __init__() has been implemented, then that special method is invoked and the instance object passed in as the first argument(self), just like a standard method call. Any arguments passed to the class invocation call are passed on to __init__(). You can practically envision the call to create the instance as a call to the constructor.
然而,如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。调用类时,传进的任何参数都交给了__init__()。实际中,你可以想像成这样:把创建实例的调用当成是对构造器的调用。
In summary, (a) you do not call new to create an instance, and you do not define a constructor: Python creates the object for you; and (b) __init__(), is simply the first method that is called after the interpreter creates an instance for you in case you want to prep the object a little bit more before putting it to use.
总之,(a)你没有通过调用new来创建实例,你也没有定义一个构造器。是Python为你创建了对象; (b) __init__(),是在解释器为你创建一个实例后调用的第一个方法,在你开始使用它之前,这一步可以让你做些准备工作。
__init__() is one of many special methods that can be defined for classes. Some of these special methods are predefined with inaction as their default behavior, such as __init__(), and must be overridden for customiza- tion while others should be implemented on an as-needed basis. We will cover many more of these special methods throughout this chapter. You will find use
of __init__() everywhere, so we will not present an example here.
__init__()是很多为类定义的特殊方法之一。其中一些特殊方法是预定义的,缺省情况下,不进行任何操作,比如__init__(),要定制,就必须对它进行重载,还有些方法,可能要按需要去实现。本章中,我们会讲到很多这样的特殊方法。你将会经常看到__init__()的使用,在此,就不举例说明了。
13.5.3 __new__() “Constructor ” Method
13.5.3 __new__() “构造器”方法
The __new__() special method bears a much closer resemblance to a real constructor than __init__(). With the unification of types and classes in
2.2, Python users now have the ability to subclass built-in types, and so there needed to be a way to instantiate immutable objects, e.g., subclassing strings, numbers, etc.
与__init__()相比,__new__()方法更像一个真正的构造器。类型和类在版本2.2就统一了,Python用户可以对内建类型进行派生,因此,需要一种途径来实例化不可变对象,比如,派生字符串,数字,等等。
In such cases, the interpreter calls __new__(), a static method, with the class and passing in the arguments made in the class instantiation call. It is the responsibility of __new__() to call a superclass __new__() to create the object (delegating upward).
在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。
The reason why we say that __new__() is more like a constructor than __init__() is that it has to return a valid instance so that the interpreter can then call __init__() with that instance as self. Calling a superclass __new__() to create the object is just like using a new keyword to create an object in other languages.
为何我们认为__new__()比__init__()更像构造器呢?这是因为__new__()必须返回一个合法的实例,这样解释器在调用__init__()时,就可以把这个实例作为self传给它。调用父类的__new__()来创建对象,正像其它语言中使用new关键字一样。
__new__() and __init__() are both passed the (same) arguments as in the class creation call. For an example of using __new__(), see Section 13.11.3.
__new__()和__init__()在类创建时,都传入了(相同)参数。13.11.3节中有个例子使用了__new__()。
13.5.4 __del__() “Destructor ” Method
13.5.4 __del__() "解构器"方法
Likewise, there is an equivalent destructor special method called __del__(). However, due to the way Python manages garbage collection of objects (by reference counting), this function is not executed until all references to an instance object have been removed. Destructors in Python are methods that provide special processing before instances are deallocated and are not com- monly implemented since instances are seldom deallocated explicitly. If you do override __del__(), be sure to call any parent class __del__() first so those pieces can be adequately deallocated.
同样,有一个相应的特殊解构器(destructor)方法名为__del__()。然而,由于Python具有垃圾对象回收机制(靠引用计数),这个函数要直到该实例对象所有的引用都被清除掉后才会执行。Python中的解构器是在实例释放前提供特殊处理功能的方法,它们通常没有被实现,因为实例很少被显式释放。
Example
举例
In the following example, we create (and override) both the __init__() and __del__() constructor and destructor functions, respectively, then instantiate the class and assign more aliases to the same object. The id() built-in function is then used to confirm that all three aliases reference the same object. The final step is to remove all the aliases by using the del statement and discovering when and how many times the destructor is called.
在下面的例子中,我们分别创建(并覆盖)__init__()和__del__()构造及解构函数,然后,初始化类并给同样的对象分配很多别名。id()内建函数可用来确定引用同一对象的三个别名。最后一步是使用del语句清除所有的别名,显示何时,调用了多少次解构器。
class C(P): # class declaration 类声明
def __init__(self): # "constructor" 构造器
print 'initialized'
def __del__(self): # "destructor" 解构器
P.__del__(self) # call parent destructor print 'deleted' 调用父类解构器来打印 'deleted'
>>> c1 = C() # instantiation initialized 实例初始化
>>> c2 = c1 # create additional alias 创建另外一个别名
>>> c3 = c1 # create a third alias 创建第三个别名
>>> id(c1), id(c2), id(c3) # all refer to same object 同一对象所有引用
(11938912, 11938912, 11938912)
>>> del c1 # remove one reference 清除一个引用
>>> del c2 # remove another reference 清除另一个引用
>>> del c3 # remove final reference deleted # destructor finally invoked 解构器最后调用
Notice how, in the above example, the destructor was not called until all references to the instance of class C were removed, e.g., when the reference count has decreased to zero. If for some reason your __del__() method is not being called when you are expecting it to be invoked, this means that somehow your instance object’s reference count is not zero, and there may be some other reference to it that you are not aware of that is keeping your object around.
注意,在上面的例子中,解构器是在类C实例所有的引用都被清除掉后,才被调用的,比如,当引用计数已减少到0。如果你预期你的__del__()方法会被调用,却实际上没有被调用,这意味着,你的实例对象由于某些原因,其引用计数不为0,这可能有别的对它的引用,而你并不知道这些让你的对象还活着的引用所在。
Also note that the destructor is called exactly once, the first time the reference count goes to zero and the object deallocated. This makes sense because any object in the system is allocated and deallocated only once. 另外,要注意,解构器只能被调用一次,一旦引用计数为0,则对象就被清除了。这非常合理,因为系统中任何对象都只被分配及解构一次。
Summary:
总结:
Do not forget to call a superclass __del__() first.
不要忘记首先调用父类的__del__()。
Invoking del x does not call x.__del__()―as you saw above,it just decrements the reference count of x.
调用del x 不表示调用了x.__del__() -----前面也看到,它仅仅是减少x的引用计数。
If you have a cycle or some other cause of lingering references to an instance, an object’s __del__() may never be called.
如果你有一个循环引用或其它的原因,让一个实例的引用逗留不去,该对象的__del__()可能永远不会被执行。
Uncaught exceptions in __del__() are ignored (because some variables used in __del__() may have already been deleted). Try not to do anything in __del__() not related to an instance.
__del__()未捕获的异常会被忽略掉(因为一些在__del__()用到的变量或许已经被删除了)。不要在__del__()中干与实例没任何关系的事情。
Implementing __del__() is not a common occurrence―only do it if you really know what you are doing.
除非你知道你正在干什么,否则不要去实现__del__()。
If you define __del__, and instance is part of a cycle, the garbage collector will not break the cycle―you have to do it yourself by explicitly using del.
如果你定义了__del__,并且实例是某个循环的一部分,垃圾回收器将不会终止这个循环——你需要自已显式调用del。
CORE NOTE: Keeping track of instances
核心笔记:跟踪实例
Python does not provide any internal mechanism to track how many instances of a class have been created or to keep tabs on what they are. You can explicitly add some code to the class definition and perhaps __init__() and __del__() if such functionality is desired. The best way is to keep track of the number of instances using a static member. It would be dangerous to keep track of instance objects by saving references to them, because you must manage these references properly or else your instances will never be deallocated (because of your extra reference to them)! An example follows:
Python没有提供任何内部机制来跟踪一个类有多少个实例被创建了,或者记录这些实例是些什么东西。如果需要这些功能,你可以显式加入一些代码到类定义或者__init__()和__del__()中去。最好的方式是使用一个静态成员来记录实例的个数。靠保存它们的引用来跟踪实例对象是很危险的,因为你必须合理管理这些引用,不然,你的引用可能没办法释放(因为还有其它的引用)!看下面一个例子:
class InstCt(object):
count = 0 # count is class attr count是一个类属性
def __init__(self): # increment count 增加count
InstCt.count += 1
def __del__(self): # decrement count 减少count
InstCt.count -= 1
def howMany(self): # return count 返回count
return InstCt.count
>>> a = InstTrack()
>>> b = InstTrack()
>>> b.howMany()
2
>>> a.howMany()
2
>>> del b
>>> a.howMany()
1
>>> del a
>>> InstTrack.count
0
13.6 Instance Attributes
实例属性
Instances have only data attributes (methods are strictly class attributes) and are simply data values that you want to be associated with a particular instance of any class and are accessible via the familiar dotted-attribute notation. These values are independent of any other instance or of the class it was instantiated from. When an instance is deallocated, so are its attributes.
实例仅拥有数据属性(方法严格来说是类属性),后者只是与某个类的实例相关联的数据值,并且可以通过句点属性标识法来访问。这些值独立于其它实例或类。当一个实例被释放后,它的属性同时也被清除了。
13.6.1 “Instantiating” Instance Attributes
(or Creating a Better Constructor)
“实例化”实例属性(或创建一个更好的构造器)
Instance attributes can be set any time after an instance has been created, in any piece of code that has access to the instance. However, one of the key places where such attributes are set is in the constructor, __init__().
设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。构造器__init()__是设置这些属性的关键点之一。
CORE NOTE: Instance attributes
核心笔记:实例属性
Being able to create an instance attribute “on-the-fly” is one of the great features of Python classes, initially (but gently) shocking those coming from C++ or Java in which all attributes must be explicitly defined/ declared first.
能够在“运行时”创建实例属性,是Python类的优秀特性之一,从C++或Java转过来的人会被小小的震惊一下,因为C++或Java中所有属性在使用前都必须明确定义/声明。
Python is not only dynamically typed but also allows for such dynamic creation of object attributes during run-time. It is a feature that once used may be difficult to live without. Of course, we should mention to the reader that one much be cautious when creating such attributes.
Python不仅是动态类型,而且在运行时,允许这些对象属性的动态创建。这种特性让人爱不释手。当然,我们必须提醒读者,创建这样的属性时,必须谨慎。
One pitfall is when such attributes are created in conditional clauses: if you attempt to access such an attribute later on in your code, that attribute may not exist if the flow had not entered that conditional suite. The moral of the story is that Python gives you a new feature you were not used to before, but if you use it, you need to be more careful, too.
一个缺陷是,属性在条件语句中创建,如果该条件语句块并未被执行,属性也就不存在,而你在后面的代码中试着去访问这些属性,就会有错误发生。故事的精髓是告诉我们,Python让你体验从未用过的特性,但如果你使用它了,你还是要小心为好。
Constructor First Place to Set Instance Attributes
在构造器中首先设置实例属性
The constructor is the earliest place that instance attributes can be set because __init__() is the first method called after instance objects have been created. There is no earlier opportunity to set instance attributes. Once __init__() has finished execution, the instance object is returned, com- pleting the instantiation process.
构造器是最早可以设置实例属性的地方,因为__init__()是实例创建后第一个被调用的方法。再没有比这更早的可以设置实例属性的机会了。一旦__init__()执行完毕,返回实例对象,即完成了实例化过程。
Default Arguments Provide Default Instance Setup
默认参数提供默认的实例安装
One can also use __init__() along with default arguments to provide an effective way of preparing an instance for use in the real world. In many situ- ations, the default values represent the most common cases for setting up instance attributes, and such use of default values precludes them from having to be given explicitly to the constructor. We also outlined some of the general benefits of default arguments in Section 11.5.2. One caveat is that default arguments should be immutable objects; mutable objects like lists and dictionaries act like static data, maintaining their contents with each method call.
在实际应用中,带默认参数的__init__()提供一个有效的方式来初始化实例。在很多情况下,默认值表示设置实例属性的最常见的情况,如果提供了默认值,我们就没必要显式给构造器传值了。我们在11.5.2节中也提到默认参数的常见好处。需要明白一点,默认参数应当是不变的对象;像列表(list)和字典(dictionary)这样的可变对象可以扮演静态数据,然后在每个方法调用中来维护它们的内容。
Example 13.1 shows how we can use the default constructor behavior to help us calculate some sample total room costs for lodging at hotels in some
of America’s large metropolitan areas.
例13.1描述了如何使用默认构造器行为来帮助我们计算在美国一些大都市中的旅馆中寄宿时,租房总费用。
The main purpose of our code is to help someone figure out the daily hotel room rate, including any state sales and room taxes. The default is for the general area around San Francisco, which has an 8.5% sales tax and a 10% room tax. The daily room rate has no default value, thus it is required for any instance to be created.
代码的主要目的是来帮助某人计算出每日旅馆租房费用,包括所有州销售税和房税。缺省为旧金山附近的普通区域,它有8.5%销售税及10%的房间税。每日租房费用没有缺省值,因此在任何实例被创建时,都需要这个参数。
Example 13.1 Using Default Arguments with Instantiation
例13.1 使用缺省参数进行实例化
(hotel.py)
Class definition for a fictitious hotel room rate calculator. The __init__() constructor method initializes several instance attributes. A calcTotal() method is used to determine either a total daily room rate or the total room cost for an entire stay.
定义一个类来计算这个假想旅馆租房费用。__init__()构造器对一些实例属性进行初始化。calcTotal()方法用来决定是计算每日总的租房费用还是计算所有天全部的租房费。
1 class HotelRoomCalc(object):
2 'Hotel room rate calculator'
3
4 def __init__(self, rt, sales=0.085, rm=0.1):
5 '''HotelRoomCalc default arguments:
6 sales tax == 8.5% and room tax == 10%'''
7 self.salesTax = sales
8 self.roomTax = rm
9 self.roomRate = rt
10
11 def calcTotal(self, days=1):
12 'Calculate total; default to daily rate'
13 daily = round((self.roomRate *14 (1 + self.roomTax + self.salesTax)), 2)
15 return float(days) * daily
The setup work is done after instantiation by __init__() in lines 4?8, and the other core part of our code is the calcTotal() method, lines 10?14. The job of __init__() is to set the values needed to determine the total base room rate of a hotel room (not counting room service, phone calls, or other incidental items). calcTotal() is then used to either determine the total daily rate or the cost of an entire stay if the number of days is provided. The round() built-in function is used to round the calculation to the closest penny (two decimal places). Here is some sample usage of this class:
设置工作是由__init__()在实例化之后完成的,如上第4到8行,其余部分的核心代码是calcTotal()方法,从第10到14行。__init__()的工作即是设置一些参数值来决定旅馆总的基本租房费用(不包括住房服务,电话费,或其它偶发事情)。calcTotal()可以计算每日所有费用,如果提供了天数,那么将计算整个旅程全部的住宿费用。内建的round()函数可以大约计算出最接近的费用(两个小数位)。下面是这个类的用法:
>>> sfo = HotelRoomCalc(299) # new instance 新的实例
>>> sfo.calcTotal() # daily rate 日租金
354.32
>>> sfo.calcTotal(2) # 2-day rate 2天的租金
708.64
>>> sea = HotelRoomCalc(189, 0.086, 0.058) # new instance 新的实例
>>> sea.calcTotal()
216.22
>>> sea.calcTotal(4)
864.88
>>> wasWkDay = HotelRoomCalc(169, 0.045, 0.02) # new instance 新实例
>>> wasWkEnd = HotelRoomCalc(119, 0.045, 0.02) # new instance 新实例
>>> wasWkDay.calcTotal(5) + wasWkEnd.calcTotal() # 7-day rate 7天的租金
1026.69
The first two hypothetical examples were San Francisco, which used the defaults, and then Seattle, where we provided different sales tax and room tax rates. The final example, Washington, D.C., extended the general usage by calculating a hypothetical longer stay: a five-day weekday stay plus a special rate for one weekend day, assuming a Sunday departure to return home.
最开始的两个假想例子都是在旧金山(San Francisco), 使用了默认值,然后是在西雅图(Seattle),这里我们提供了不同的销售税和房间税率。最后一个例子在华盛顿特区(Washington.D.C)。经过计算更长的假想时间,来扩展通常的用法:停留五个工作日,外加一个周六,此时有特价,假定是星期天出发回家。 Do not forget that all the flexibility you get with functions, such as default arguments, applies to methods as well. The use of variable-length arguments is another good feature to use with instantiation (based on an application’s needs, of course).
不要忘记,函数所有的灵活性,比如默认参数,也可以应用到方法中去。在实例化时,可变长度参数也是一个好的特性(当然,这要根据应用的需要)
__init__() Should Return None
__init__()应当返回None
As you are now aware, invoking a class object with the function operator cre- ates a class instance, which is the object returned on such an invocation, as in the following example:
你也知道,采用函数操作符调用类对象会创建一个类实例,也就是说这样一种调用过程返回的对象就是实例,下面示例可以看出:
>>> class MyClass(object):
... pass
>>> mc = MyClass()
>>> mc
<__main__.MyClass instance at 95d390>
If a constructor is defined, it should not return any object because the instance object is automatically returned after the instantiation call. Correspondingly, __init__() should not return any object (or return None); otherwise, there is a conflict of interest because only the instance should be returned. Attempting to return any object other than None will result in a TypeError exception:
如果定义了构造器,它不应当返回任何对象,因为实例对象是自动在实例化调用后返回的。相应地,__init__()就不应当返回任何对象(应当为None);否则,就可能出现冲突,因为只能返回实例。试着返回非None的任何其它对象都会导致TypeError异常:
>>> class MyClass:
... def __init__(self):
... print 'initialized'
... return 1
...
>>> mc = MyClass()
initialized
Traceback (innermost last): File "<stdin>", line 1, in ?
mc = MyClass()
TypeError: __init__() should return None
13.6.2 Determining Instance Attributes
查看实例属性
The dir() built-in function can be used to show all instance attributes in the same manner that it can reveal class attributes:
内建函数dir()可以显示类属性,同样还可以打印所有实例属性:
>>> class C(object):
... pass
>>> c = C()
>>> c.foo = 'roger'
>>> c.bar = 'shrubber'
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__', '__weakref__', 'bar', 'foo']
Similar to classes, instances also have a __dict__ special attribute (also accessible by calling vars() and passing it an instance), which is a dictionary representing its attributes:
与类相似,实例也有一个__dict__特殊属性(可以调用vars()并传入一个实例来获取),它是实例属性构成的一个字典:
>>> c.__dict__
{'foo': 'roger', 'bar': 'shrubber'}
13.6.3 Special Instance Attributes
特殊的实例属性
Instances have only two special attributes (see Table 13.2). For any instance I:
实例仅有两个特殊属性(见表13.2)。对于任意对象I:
Table 13.2 Special Instance Attributes
表13.2 特殊实例属性
I.__class__ Class from which I is instantiated 实例化I的类
I.__dict__ Attributes of I I的属性
We will now take a look at these special instance attributes using the class C and its instance c:
现在使用类C及其实例C来看看这些特殊实例属性:
>>> class C(object): # define class 定义类
... pass
...
>>> c = C() # create instance 创建实例
>>> dir(c) # instance has no attributes 实例还没有属性
[]
>>> c.__dict__ # yep, definitely no attributes 也没有属性
{}
>>> c.__class__ # class that instantiated us 实例化c的类
<class '__main__.C'>
As you can see, c currently has no data attributes, but we can add some and recheck the __dict__ attribute to make sure they have been added properly:
你可以看到,c现在还没有数据属性,但我们可以添加一些再来检查__dict__属性,看是否添加成功了:
>>> c.foo = 1
>>> c.bar = 'SPAM'
>>> '%d can of %s please' % (c.foo, c.bar)
'1 can of SPAM please'
>>> c.__dict__
{'foo': 1, 'bar': 'SPAM'}
The __dict__ attribute consists of a dictionary containing the attributes of an instance. The keys are the attribute names, and the values are the attributes’ corresponding data values. You will only find instance attributes in this dictionary―no class attributes or special attributes.
__dict__属性由一个字典组成,包含一个实例的所有属性。键是属性名,值是属性相应的数据值。字典中仅有实例属性,没有类属性或特殊属性。
CORE STYLE: Modifying __dict__
核心风格:修改__dict__
Although the __dict__ attributes for both classes and instances are mutable, it is recommended that you not modify these dictionaries unless or until you know exactly what you are doing. Such modification contaminates your OOP and may have unexpected side effects. It is more acceptable to access and manipulate attributes using the familiar dotted-attribute notation. One of the few cases where you would modify the __dict__ attribute directly is when you are overriding the __setattr__ special method. Implementing __setattr__() is another adventure story on its own, full of traps and pitfalls such as infinite recursion and corrupted instance objects―but that is another tale for another time.
对类和实例来说,尽管__dict__属性是可修改的,但还是建议你不要修改这些字典,除非你知道你的目的。这些修改可能会破坏你的OOP,造成不可预料的副作用。使用熟悉的句点属性标识来访问及操作属性会更易于接受。需要你直接修改__dict__属性的情况很少,其中之一是你要重载__setattr__特殊方法。实现__setattr__()本身是一个冒险的经历,满是圈套和陷阱,例如无穷递归和破坏实例对象。这个故事还是留到下次说吧。
13.6.4 Built-in Type Attributes
内建类型属性
Built-in types are classes, too … do they have the same attributes as classes?
(The same goes for instances.) We can use dir() on built-in types just like for any other object to get a list of their attribute names:
内建类型也是类,它们有没有像类一样的属性呢?那实例有没有呢?对内建类型也可以使用dir(),与任何其它对象一样,可以得到一个包含它属性名字的列表:
>>> x = 3+0.14j
>>> x.__class__
<type 'complex'>
>>> dir(x)
['__abs__', '__add__', '__class__', '__coerce__',
'__delattr__', '__div__', '__divmod__', '__doc__', '__eq__',
'__float__', '__floordiv__', '__ge__', '__getattribute__',
'__getnewargs__', '__gt__', '__hash__', '__init__',
'__int__', '__le__', '__long__', '__lt__', '__mod__',
'__mul__', '__ne__', '__neg__', '__new__', '__nonzero__',
'__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__',
'__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__',
'__rmod__', '__rmul__', '__rpow__', '__rsub__',
'__rtruediv__', '__setattr__', '__str__', '__sub__',
'__truediv__', 'conjugate', 'imag', 'real']
>>>
>>> [type(getattr(x, i)) for i in ('conjugate', 'imag',
'real')]
[, ,
]
Now that we know what kind of attributes a complex number has, we can access the data attributes and call its methods:
既然我们知道了一个复数有什么样的属性,我们就可以访问它的数据属性,调用它的方法了:
>>> x.imag
2.0
>>> x.real
1.0
>>> x.conjugate()
(1-2j)
Attempting to access __dict__ will fail because that attribute does not exist for built-in types:
试着访问__dict__会失败,因为在内建类型中,不存在这个属性:
>>> x.__dict__
Traceback (innermost last): File "<stdin>", line 1, in ?
AttributeError: __dict__
13.6.5 Instance Attributes versus Class Attributes
13.6.5 实例属性 vs 类属性
We first described class data attributes in Section 13.4.1. As a brief reminder, class attributes are simply data values associated with a class and not any par- ticular instances like instance attributes are. Such values are also referred to as static members because their values stay constant, even if a class is invoked due to instantiation multiple times. No matter what, static members maintain their values independent of instances unless explicitly changed. (Comparing instance attributes to class attributes is barely like that of automatic vs. static variables, but this is just a vague analogy . . . do not read too much into it, especially if you are not familiar with auto and static variables.)
我们已在13.4.1节中描述了类数据属性。这里简要提一下,类属性仅是与类相关的数据值,和实例属性不同,类属性和实例无关。这些值像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变。不管如何,静态成员不会因为实例而改变它们的值,除非实例中显式改变它们的值。(实例属性与类属性的比较,类似于自动变量和静态变量,但这只是笼统的类推。在你对自动变量和静态变量还不是很熟的情况下,不要深究这些。
Classes and instances are both namespaces. Classes are namespaces for class attributes. Instances are namespaces for instance attributes.
类和实例都是名字空间。类是类属性的名字空间,实例则是实例属性的。
There are a few aspects of class attributes and instance attributes that should be brought to light. The first is that you can access a class attribute with either the class or an instance, provided that the instance does not have an attribute with the same name.
关于类属性和实例属性,还有一些方面需要指出。你可采用类来访问类属性,如果实例没有同名的属性的话,你也可以用实例来访问。
Access to Class Attributes
访问类属性
Class attributes can be accessed via a class or an instance. In the example below, when class C is created with the version class attribute, naturally access is allowed using the class object, i.e., C.version. When instance c is created, access to c.version fails for the instance, and then Python initiates a search for the name version first in the instance, then the class, and then the base classes in the inheritance free. In this case, it is found in the class:
类属性可通过类或实例来访问。下面的示例中,类C在创建时,带一个version属性,这样通过类对象来访问它是很自然的了,比如,C.version。当实例c被创建后,对实例c而言,访问c.version会失败,不过Python首先会在实例中搜索名字version,然后是类,再就是继承树中的基类。本例中,version在类中被找到了:
>>> class C(object): # define class 定义类
... version = 1.2 # static member 静态成员
...
>>> c = C() # instantiation 实例化
>>> C.version # access via class 通过类来访问
1.2
>>> c.version # access via instance 通过实例来访问
1.2
>>> C.version += 0.1 # update (only) via class 通过类(只能这样)来更新
>>> C.version # class access 类访问
1.3
>>> c.version # instance access, which 实例访问它,其值已被改变
1.3 # also reflected change
However, we can only update the value when referring to it using the class, as in the C.version increment statement above. Attempting to set or update the class attribute using the instance name will create an instance attribute that “shadows” access to the class attribute, effectively hiding it from scope until or unless that shadow is removed.
然而,我们只有当使用类引用version时,才能更新它的值,像上面的C.version递增语句。如果尝试在实例中设定或更新类属性会创建一个实例属性c.version,后者会阻止对类属性C.versioin的访问,因为第一个访问的就是c.version,这样可以对实例有效地“遮蔽”类属性C.version,直到c.version被清除掉。
Use Caution When Accessing Class Attribute with Instance
从实例中访问类属性须谨慎
Any type of assignment of a local attribute will result in the creation and assignment of an instance attribute, just like a regular Python variable. If a class attribute exists with the same name, interesting side effects can occur.(This is true for both classic and new-style classes.)
与通常Python变量一样,任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并且对其赋值。如果类属性中存在同名的属性,有趣的副作用即产生。(经典类和新式类都存在)
>>> class Foo(object):
... x = 1.5
...
>>> foo = Foo()
>>> foo.x
1.5
>>> foo.x = 1.7 # try to update class attr 试着更新类属性
>>> foo.x # looks good so far... 现在看起来还不错
1.7
>>> Foo.x # nope, just created a new inst attr 呵呵,没有变,只是创建了一个新的实例属性
1.5
In the above code snippet, a new instance attribute named version is created, overriding the reference to the class attribute. However, the class attribute itself is unscathed and still exists in the class domain and can still be accessed as a class attribute, as we can see above. What would happen if we delete this new reference? To find out, we will use the del statement on c.version.
在上面的代码片断中,创建了一个名为version的新实例属性,它覆盖了对类属性的引用。然而,类属性本身并没有受到伤害,仍然存在于类域中,还可以通过类属性来访问它,如上例可以看到的。好了,那么如果把这个新的version删除掉,会怎么样呢?为了找到结论,我们将使用del语句删除c.version。
>>> del foo.x # delete instance attribute 删除实例属性
>>> foo.x # can now access class attr again 又可以访问到类属性
1.5
So by assigning an instance attribute with the same name as a class attribute, we effectively “hide” the class attribute, but once we remove the instance attribute, we can “see” the class one again. Now let us try to update the class attribute again, but this time, we will just try an innocent increment:
所以,给一个与类属性同名的实例属性赋值,我们会有效地“隐藏”类属性,但一旦我们删除了这个实例属性,类属性又重见天日。现在再来试着更新类属性,但这次,我们只尝试一下“无辜”的增量动作:
>>> foo.x += .2 # try to increment class attr 试着增加类属性
>>> foo.x
1.7
>>> Foo.x # nope, same thing 呵呵,照旧
1.5
It is still a “no go.” We again created a new instance attribute while leaving the original class attribute intact. (For those who have or want a deeper understanding of Python: the attribute was already in the class’s dictionary [__dict__]. With the assignment, one is now added to the instance’s __dict__.) The expression on the right-hand side of the assignment evaluates the original class variable, adds 0.2 to it, and assigns it to a newly created instance attribute. Note that the following is an equivalent assignment, but it may provide more clarification:
还是没变。我们同样创建了一个新的实例属性,类属性原封不动。(深入理解Python相关知识:属性已存于类字典[__dict__]中。通过赋值,其被加入到实例的__dict__中了。)赋值语句右边的表达式计算出原类的变量,增加0.2,并且把这个值赋给新创建的实例属性。注意下面是一个等价的赋值方式,但它可能更加清楚些:
foo.x = Foo.x + 0.2
But … all of this changes if the class attribute is mutable:
但...在类属性可变的情况下,一切都不同了:
>>> class Foo(object):
... x = {2003: 'poe2'}
...
>>> foo = Foo()
>>> foo.x
{2003: 'poe2'}
>>> foo.x[2004] = 'valid path'
>>> foo.x
{2003: 'poe2', 2004: 'valid path'}
>>> Foo.x # it works!!! 生效了
{2003: 'poe2', 2004: 'valid path'}
>>> del foo.x # no shadow so cannot delete 没有遮蔽所以不能删除掉
Traceback (most recent call last): File "<stdin>", line 1, in ?
del foo.x
AttributeError: x
>>>
Class Attributes More Persistent
类属性持久性
Static members, true to their name, hang around while instances (and their attributes) come and go (hence independent of instances). Also, if a new instance is created after a class attribute has been modified, the updated value will be reflected. Class attribute changes are reflected across all instances:
静态成员,如其名所言,任凭整个实例(及其属性)的如何进展,它都不理不采(因此独立于实例)。同时,当一个实例在类属性被修改后才创建,那么更新的值就将生效。类属性的修改会影响到所有的实例:
>>> class C(object):
... spam = 100 # class attribute 类属性
...
>>> c1 = C() # create an instance 创建一个实例
>>> c1.spam # access class attr thru inst. 通过实例访问类属性
100
>>> C.spam += 100 # update class attribute 更新类属性
>>> C.spam # see change in attribute 查看属性值改变
200
>>> c1.spam # confirm change in attribute 在实例中验证属性值改变
200
>>> c2 = C() # create another instance 创建另一个实例
>>> c2.spam # verify class attribute 验证类属性
200
>>> del c1 # remove one instance 删除一个实例
>>> C.spam += 200 # update class attribute again 再次更新类属性
>>> c2.spam # verify that attribute changed 验证那个属性值改变
400
CORE TIP: Use a class attribute to modify itself (not an instance attribute)
核心提示:使用类属性来修改自身(不是实例属性)
As we have seen above, it is perilous to try and modify a class attribute by using an instance attribute.The reason is because instances have their own set of attributes, and there is no clear way in Python to indicate that you want to modify the class attribute of the same name, e.g., there is no global keyword like there is when setting a global inside a function (instead of a local variable of the same name). Always modify a class attribute with the class name, not an instance.
正如上面所看到的那样,使用实例属性来试着修改类属性是很危险的。原因在于实例拥有它们自已的属性集,在Python中没有明确的方法来指示你想要修改同名的类属性,比如,没有global关键字可以用来在一个函数中设置一个全局变量(来代替同名的局部变量)。修改类属性需要使用类名,而不是实例名。
13.7 Binding and Method Invocation -------------andelf 从这里开始校对----------
绑定和方法调用
Now we need to readdress the Python concept of binding, which is associated primarily with method invocation. We will first review some facts regarding methods. First, a method is simply a function defined as part of a class. (This means that methods are class attributes and not instance attributes).
现在我们需要再次阐述 Python 中绑定(binding)的概念,它主要与方法调用相关连。我们先来回顾一下与方法相关的知识。首先,方法仅仅是类内部定义的函数。(这意味着方法是类属性而不是实例属性)。
Second, methods can be called only when there is an instance of the class upon which the method was invoked. When there is an instance present, the method is considered bound (to that instance). Without an instance, a method is considered unbound.
其次,方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是绑定到那个实例了。没有实例时方法就是未绑定的。
And finally, the first argument in any method definition is the variable self, which represents the instance object invoking the method.
最后,任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象。
CORE NOTE: What is self?
核心笔记:self 是什么?
The variable self is used in class instance methods to reference the instance to which the method is bound. Because a method’s instance is always passed as the first argument in any method call, self is the name that was chosen to represent the instance. You are required to put self in the method declaration (you may have noticed this already) but do not need to actually use the instance (self) within the method. If you do not use self in your method, you might consider creating a regular function instead, unless you have a particular reason not to. After all, your code, because it does not use the instance object in any way, “unlinks” its functionality from the class, making it seem more like a general function. In other object-oriented languages, self may be named this.
self变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是作为第一个参数传递的,self被选中用来代表实例。你必须在方法声明中放上self(你可能已经注意到了这点),但可以在方法中不使用实例(self)。如果你的方法中没有用到self , 那么请考虑创建一个常规函数,除非你有特别的原因。毕竟,你的方法代码没有使用实例,没有与类关联其功能,这使得它看起来更像一个常规函数。在其它面向对象语言中,self可能被称为 this。
13.7.1 Invoking Bound Methods
调用绑定方法
Methods, whether bound or not, are made up of the same code. The only dif- ference is whether there is an instance present so that the method can be invoked. In most cases, you the programmer will be calling a bound method. Let us say that you have a class MyClass and an instance of it called mc, and you want to call the MyClass.foo() method. Since you already have an instance, you can just call the method with mc.foo(). Recall that self is required to be declared as the first argument in every method declaration. Well, when you call a bound method, self never needs to be passed explicitly when you invoke it with an instance. That is your bonus for being “required” to declare self as the first argument. The only time when you have to pass it in is when you do not have an instance and need to call a method unbound.
方法,不管绑定与否,都是由相同的代码组成的。唯一的不同在于是否存在一个实例可以调用此方法。在很多情况下,程序员调用的都是一个绑定的方法。假定现在有一个 MyClass 类和此类的一个实例 mc,而你想调用MyClass.foo()方法。因为已经有一个实例,你只需要调用mc.foo()就可以。记得self在每一个方法声明中都是作为第一个参数传递的。当你在实例中调用一个绑定的方法时,self 不需要明确地传入了。这算是"必须声明self作为第一个参数"对你的报酬。当你还没有一个实例并且需要调用一个非绑定方法的时候你必须传递self参数。
13.7.2 Invoking Unbound Methods
调用非绑定方法
Calling an unbound method happens less frequently. The main use case for calling a method belonging to a class that you do not have an instance for is the case where you are deriving a child class and override a parent method where you need to call the parent’s constructor you are overriding. Let us look at an example back in the chapter introduction:
调用非绑定方法并不经常用到。需要调用一个还没有任何实例的类中的方法的一个主要的场景是:你在派生一个子类,而且你要覆盖父类的方法,这时你需要调用那个父类中想要覆盖掉的构造方法。这里是一个本章前面介绍过的例子:
class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class' # 员工地址记录条目
def __init__(self, nm, ph, em):
AddrBookEntry.__init__(self, nm, ph)
self.empid = id
self.email = em
EmplAddrBookEntry is a subclass of AddrBookEntry, and we are over- riding the constructor __init__(). Rather than cutting and pasting code from the parent constructor, we want to have as much code reuse as possible. This will also prevent bugs from being propagated because any fixes made would be propagated to us here in the child. This is exactly what we want― there is no need to copy lines of code. This all hinges on somehow being able to call the parent constructor, but how?
EmplAddrBookEntry是AddrBookEntry的子类,我们重载了构造器__init__()。我们想尽可能多地重用代码, 而不是去从父类构造器中剪切,粘贴代码。这样做还可以避免BUG传播,因为任何修复都可以传递给子类。这正是我们想要的 --- 没有必要一行一行地复制代码。只需要能够调用父类的构造器即可,但该怎么做呢?
We would not have an instance of AddrBookEntry at runtime. What do we have? Well, we will have an instance of EmplAddrBookEntry, and it is so similar to AddrBookEntry, can’t we somehow use it instead? The answer is yes!
我们在运行时没有AddrBookEntry的实例。那么我们有什么呢?我们有一个EmplAddrBookEntry的实例,它与AddrBookEntry是那样地相似,我们难道不能用它代替呢?当然可以!
When an EmplAddrBookEntry is instantiated and __init__() called, there is very little difference between it and an instance of AddrBookEntry, mainly because we have not yet had a chance to customize our EmplAddr- BookEntry instance to really make it different from AddrBookEntry.
当一个EmplAddrBookEntry被实例化,并且调用 __init__() 时,其与AddrBookEntry的实例只有很少的差别,主要是因为我们还没有机会来自定义我们的EmplAddrBookEntry实例,以使它与AddrBookEntry不同。
This is the perfect place to call an unbound method. We will call the parent class constructor from the child class constructor and explicitly pass in the self argument as required by the (parent class) constructor (since we are without a parent class instance). The first line of __init__() in the child consists of a call to __init__() of the parent. We call it via the parent class name and pass in self plus its required arguments. Once that call returns, we can perform the (instance) customization that is unique to our (child) class.
这是调用非绑定方法的最佳地方了。我们将在子类构造器中调用父类的构造器并且明确地传递(父类)构造器所需要的self参数(因为我们没有一个父类的实例)。子类中 __init__() 的第一行就是对父类__init__()的调用。我们通过父类名来调用它,并且传递给它 self 和其他所需要的参数。一旦调用返回,我们就能定义那些与父类不同的仅存在我们的(子)类中的(实例)定制。
13.8 Static Methods and Class Methods
13.8 静态方法和类方法
Static methods and class methods were introduced in Python 2.2. They can be used with both classic classes and new-style classes. A pair of built-in functions were added to “tag,” “cast,” or “convert” methods declared as part of class definitions as either one of these two types of methods.
静态方法和类方法在Python2.2中引入。经典类及新式(new-style)类中都可以使用它。一对内建函数被引入,用于将作为类定义的一部分的某一方法声明“标记”(tag),“强制类型转换”(cast)或者“转换”(convert)为这两种类型的方法之一。
Static methods are exactly what they are if you are coming from C++ or Java. They are simply functions (no instance required) that are part of class definitions. In fact, before static methods were added to Python, users just created functions in the global namespace as a proxy for this missing feature― sometimes using a class object inside such functions to manipulate the class (or rather, class attributes). Using module functions is still far more common than using static class methods.
如果你有一定的C++或者Java经验,静态方法和这些语言中的是一样的。它们仅是类中的函数(不需要实例)。事实上,在静态方法加入到Python之前,用户只能在全局名字空间中创建函数,作为这种特性的替代实现 - 有时在这样的函数中使用类对象来操作类(或者是类属性)。使用模块函数比使用静态类方法更加常见。
Recall that regular methods require an instance (self) as the first argu- ment, and upon (bound) method invocation, self is automagically passed to the method. Well, for class methods, instead of the instance, the class is required as the first argument, and it is passed in to the method by the inter- preter. The class does not need to be specifically named like self, but most people use cls as the variable name.
回忆一下,通常的方法需要一个实例(self)作为第一个参数,并且对于(绑定的)方法调用来说,self是自动传递给这个方法的。而对于类方法而言,需要类而不是实例作为第一个参数,它是由解释器传给方法。类不需要特别地命名, 类似self,不过很多人使用cls作为变量名字。
13.8.1 staticmethod() and classmethod() Built-in Functions
13.8.1 staticmethod()和classmethod()内建函数
Now let us look at some examples of these types of methods using classic classes (you can also use new-style classes if you want to):
现在让我们看一下在经典类中创建静态方法和类方法的一些例子(你也可以把它们用在新式类中):
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo = staticmethod(foo)
class TestClassMethod:
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
foo = classmethod(foo)
The corresponding built-in functions are converted into their respective types and are reassigned back to the same variable name. Without the func- tion calls, both would generate errors from the Python compiler, which is expecting regular method declarations with self. We can then call these functions from either the class or an instance … it makes no difference:
对应的内建函数被转换成它们相应的类型,并且重新赋值给了相同的变量名。如果没有调用这两个函数,二者都会在Python编译器中产生错误,显示需要带self的常规方法声明。现在, 我们可以通过类或者实例调用这些函数....这没什么不同:
>>> tsm = TestStaticMethod()
>>> TestStaticMethod.foo()
calling static method foo()
>>> tsm.foo()
calling static method foo()
>>>
>>> tcm = TestClassMethod()
>>> TestClassMethod.foo()
calling class method foo()
foo() is part of class: TestClassMethod
>>> tcm.foo()
calling class method foo()
foo() is part of class: TestClassMethod
13.8.2 Using Decorators
13.8.2 使用函数修饰符
Now, seeing code like foo = staticmethod(foo) can irritate some pro- grammers. There is something unsettling about it, and many folks were upset with such a flimsy syntax, although van Rossum had pointed out that it was to be temporary until the semantics were worked out with the community. In Section 11.3.6 of Chapter 11, “Functions”, we looked at decorators, a new feature introduced in Python 2.4. They are used in places where you want to apply a function to a function object but want to rebind the new function object to the original variable. This is a perfect place to use them to partially clean up the syntax. By using decorators, we can avoid the reassignment above:
现在,看到像foo=staticmethod(foo)这样的代码会刺激一些程序员。很多人对这样一个没意义的语法感到心烦,即使van Rossum曾指出过,它只是临时的,有待社区对些语义进行处理。在第11章“函数”的11.3.6节中,我们了解了函数修饰符,一种在Python2.4中加入的新特征。你可以用它把一个函数应用到另个函数对象上, 而且新函数对象依然绑定在原来的变量。我们正是需要它来整理语法。通过使用decorators,我们可以避免像上面那样的重新赋值:
class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
13.9 Composition
13.9 组合
Once a class is defined, the goal is to use it as a model programmatically, embedding this object throughout your code, intermixing use with other data types and the logical flow of execution. There are two ways of utilizing classes in your code. The first is composition. This is where different classes are mingled with and into other classes for added functionality and code reusability. You may create instances of your class inside a larger class, containing other attributes and methods enhancing the use of the original class object. The other way is with derivation, discussed in the next section.
一个类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去,同其它数据类型及逻辑执行流混合使用。有两种方法可以在你的代码中利用类。第一种是组合(composition)。就是让不同的类混合并加入到其它类中,来增加功能和代码重用性。你可以在一个大点的类中创建你自已的类的实例,实现一些其它属性和方法来增强对原来的类对象。另一种方法是通过派生,我们将在下一节中讨论它.
For example, let us imagine an enhanced design of the address book class we created at the beginning of the chapter. If, during the course of our design, we created separate classes for names, addresses, etc., we would want to integrate that work into our AddrBookEntry class, rather than have to redesign each of those supporting classes. We have the added advantages of time and effort saved, as well as more consistent code―when bugs are fixed in that same piece of code, that change is reflected in all the applications that reuse that code.
举例来说,让我们想象一个对本章一开始创建的地址本类的加强性设计。如果在设计的过程中,为 names,addresses等等创建了单独的类。那么最后我们可能想把这些工作集成到AddrBookEntry类中去,而不是重新设计每一个需要的类。这样就节省了时间和精力,而且最后的结果是容易维护的代码 --- 一块代码中的bugs被修正,将反映到整个应用中。
Such a class would perhaps contain a Name instance, not to mention others like StreetAddress, Phone (home, work, telefacsimile, pager, mobile, etc.), Email (home, work, etc.), and possibly a few Date instances (birthday, wed-ding, anniversary, etc.). Here is a simple example with some of the classes mentioned above:
这样的类可能包含一个Name实例,以及其它的像 StreetAddress, Phone(home, work, telefacsimile, pager, mobile, 等等),Email (home, work, 等等。),还可能需要一些Date实例(birthday,wedding,anniversary,等等)。下面是一个简单的例子:
class NewAddrBookEntry(object): # class definition 类定义
'new address book entry class'
def __init__(self, nm, ph): # define constructor 定义构造器
self.name = Name(nm) # create Name instance 创建Name实例
self.phone = Phone(ph) # create Phone instance 创建Phone实例
print 'Created instance for:', self.name
The NewAddrBookEntry class is a composition of itself and other classes. This defines a “has-a” relationship between a class and other classes it is composed of. For example, our NewAddrBookEntry class “has a” Name class instance and a Phone instance, too.
NewAddrBookEntry类由它自身和其它类组合而成。这就在一个类和其它组成类之间定义了一种“has-a / 有一个”的关系。比如,我们的NewAddrBookEntry类“有一个” Name类实例和一个Phone实例。
Creating composite objects enables such additional functionality and makes sense because the classes have nothing in common. Each class manages its own namespace and behavior. When there are more intimate relationships between objects, the concept of derivation may make more sense in your appli- cation, especially if you require like objects, with slightly different functionality.
创建复合对象就可以实现这些附加的功能,并且很有意义,因为这些类都不相同。每一个类管理它们自己的名字空间和行为。不过当对象之间有更接近的关系时,派生的概念可能对你的应用程序来说更有意义,特别是当你需要一些相似的对象,但却有少许不同功能的时候。
13.10 Subclassing and Derivation
13.10 子类和派生
Composition works fine when classes are distinct and are a required component of larger classes, but when you desire “the same class but with some tweaking,” derivation is a more logical option.
当类之间有显著的不同,并且(较小的类)是较大的类所需要的组件时,组合表现得很好,但当你设计“相同的类但有一些不同的功能”时,派生就是一个更加合理的选择了。
One of the more powerful aspects of OOP is the ability to take an already defined class and extend it or make modifications to it without affecting other pieces of code in the system that use the currently existing classes. OOD allows for class features to be inherited by descendant classes or subclasses. These subclasses derive the core of their attributes from base (aka ancestor, super) classes. In addition, this derivation may be extended for multiple gen- erations. Classes involved in a one-level derivation (or that are adjacent vertically in a class tree diagram) have a parent and child class relationship. Those classes that derive from the same parent (or that are adjacent horizontally in a class tree diagram) have a sibling relationship. Parent and all higher-level classes are considered ancestors.
OOP的更强大方面之一是能够使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响系统中使用现存类的其它代码片段。OOD允许类特征在子孙类或子类中进行继承。这些子类从基类(或称祖先类,超类)继承它们的核心属性。而且,这些派生可能会扩展到多代。在一个层次的派生关系中的相关类(或者是在类树图中垂直相邻)是父类和子类关系。从同一个父类派生出来的这些类(或者是在类树图中水平相邻)是同胞关系。父类和所有高层类都被认为是祖先。
Using our example from the previous section, let us imagine having to create different types of address books. We are talking about more than just cre- ating multiple instances of address books―in this case, all objects have everything in common. What if we wanted a EmplAddrBookEntry class whose entries would contain more work-related attributes such as employee ID and e-mail address? This would differ from a PersonalAddrBookEntry class, which would contain more family-oriented information such as home address, relationship, birthday, etc.
使用前一节中的例子,如果我们必须创建不同类型的地址本。即,不仅仅是创建地址本的多个实例,在这种情况下,所有对象几乎是相同的。如果我们希望EmplAddrBookEntry 类中包含更多与工作有关的属性,如员工ID和e-mail地址?这跟PersonalAddrBookEntry类不同,它包含更多基于家庭的信息,比如家庭地址,关系,生日等等。
For both of these cases, we do not want to design these classes from scratch, because it would duplicate the work already accomplished to create the generic AddressBook class. Wouldn’t it be nice to subsume all the features and characteristics of the AddressBook class and add specialized customization for your new, yet related, classes? This is the entire motivation and desire for class derivation.
两种情况下,我们都不想到从头开始设计这些类,因为这样做会重复创建通用的AddressBook类时的操作。包含AddressBook类所有的特征和特性并加入需要的定制特性不是很好吗?这就是类派生的动机和要求。
13.10.1 Creating Subclasses
13.10.1 创建子类
The syntax for creating a subclass looks just like that for a regular (new-style) class, a class name followed by one or more parent classes to inherit from:
创建子类的语法看起来与普通(新式)类没有区别,一个类名,后跟一个或多个需要从其中派生的父类:
class SubClassName (ParentClass1[, ParentClass2, ...]):
'optional class documentation string'
class_suite
If your class does not derive from any ancestor class, use object as the name of the parent class. The only example that differs is the declaration of a classic class that does not derive from ancestor classes―in this case, there are no parentheses:
如果你的类没有从任何祖先类派生,可以使用object作为父类的名字。经典类的声明唯一不同之处在于其没有从祖先类派生---此时,没有圆括号:
class ClassicClassWithoutSuperclasses:
pass
We have already seen some examples of classes and subclasses so far, but here is another simple example:
至此,我们已经看到了一些类和子类的例子,下面还有一个简单的例子:
class Parent(object): # define parent class 定义父类
def parentMethod(self):
print 'calling parent method'
class Child(Parent): # define child class 定义子类
def childMethod(self):
print 'calling child method'
>>> p = Parent() # instance of parent 父类的实例
>>> p.parentMethod()
calling parent method
>>>
>>> c = Child() # instance of child 子类的实例
>>> c.childMethod() # child calls its method 子类调用它的方法
calling child method
>>> c.parentMethod() # calls parent's method 调用父类的方法
calling parent method
13.11 Inheritance
继承
Inheritance describes how the attributes of base classes are “bequeathed” to a derived class. A subclass inherits attributes of any of its base classes whether they be data attributes or methods.
继承描述了基类的属性如何“遗传”给派生类。一个子类可以继承它的基类的任何属性,不管是数据属性还是方法。
We present an example below. P is a simple class with no attributes. C is a class with no attributes that derives from (and therefore is a subclass of ) P:
举个例子如下。P是一个没有属性的简单类。C从P继承而来(因此是它的子类),也没有属性:
class P(object): # parent class 父类
pass
class C(P): # child class 子类
pass
>>> c = C() # instantiate child 实例化子类
>>> c.__class__ # child "is a" parent 子类“是一个”父类
<class '__main__.C'>
>>> C.__bases__ # child's parent class(es) 子类的父类
(<class '__main__.P'>,)
Because P has no attributes, nothing was inherited by C. Let us make our example more useful by giving P some attributes:
因为P没有属性,C没有继承到什么。下面我们给P添加一些属性:
class P: # parent class 父类
'P class'
def __init__(self):
print 'created an instance of', \
self.__class__.__name__
class C(P): # child class 子类
pass
We now create P with a documentation string (__doc__) and a construc- tor that will execute when we instantiate P, as in this interactive session:
现在所创建的P有文档字符串(__doc__)和构造器,当我们实例化P时它被执行,如下面的交互会话所示:
>>> p = P() # parent instance 父类实例
created an instance of P
>>> p.__class__ # class that created us 显示p所属的类名
<class '__main__.P'>
>>> P.__bases__ # parent's parent class(es) 父类的父类
(<type 'object'>,)
>>> P.__doc__ # parent's doc string 父类的文档字符串
'P class'
The “created an instance” output comes directly from __init__(). We also display some more about the parent class P for your information. We will now instantiate C, showing you how the __init__() (constructor) method is inherited with its execution:
“created an instance”是由__init__()直接输出的。我们也可显示更多关于父类的信息。我们现在来实例化C,展示 __init__()(构造)方法在执行过程中是如何继承的:
>>> c = C() # child instance 子类实例
created an instance of C
>>> c.__class__ # class that created us 显示c所属的类名
<class '__main__.C'>
>>> C.__bases__ # child's parent class(es) 子类的父类
(<class '__main__.P'>,)
>>> C.__doc__ # child's doc string 子类的文档字符串
>>>
C has no declared method __init__(), yet there is still output when instance c of class C is created. The reason is that C inherits __init__() from P. The __bases__ tuple now lists P as its parent class. Note that docu- mentation strings are unique to classes, functions/methods, and modules, so a special attribute like __doc__ is not inherited by its derived classes.
C没有声明__init__()方法,然而在类C的实例c被创建时,还是会有输出信息。原因在于C继承了P的__init__()。__bases__元组列出了其父类 P。需要注意的是文档字符串对类,函数/方法,还有模块来说都是唯一的,所以特殊属性__doc__不会从基类中继承过来。
13.11.1 __bases__ Class Attribute
13.11.1 __bases__类属性
In Section 13.4.4, we briefly introduced the __bases__ class attribute, which is a tuple containing the set of parent classes for any (sub)class. Note that we specifically state “parents” as opposed to all base classes (which includes all ancestor classes). Classes that are not derived will have an empty __bases__ attribute. Let us look at an example of how to make use of __bases__.
在第13.4.4节中,我们概要地介绍了__bases__类属性,对任何(子)类,它是一个包含其父类(parent)的集合的元组。注意,我们明确指出“父类”是相对所有基类(它包括了所有祖先类)而言的。那些没有父类的类,它们的__bases__属性为空。下面我们看一下如何使用__bases__的。
>>> class A(object): pass # define class A 定义类A
...
>>> class B(A): pass # subclass of A A的子类
...
>>> class C(B): pass # subclass of B (and indirectly, A) B的子类(A的间接子类)
...
>>> class D(A, B): pass # subclass of A and B A,B的子类
...
>>> A.__bases__
(<type 'object'>,)
>>> C.__bases__
(<class __main__.B at 8120c90>,)
>>> D.__bases__
(, <class __main__.B at 8120c90>)
In the example above, although C is a derived class of both A (through B) and B, C’s parent is B, as indicated in its declaration, so only B will show up in C.__bases__. On the other hand, D inherits from two classes, A and B.
(Multiple inheritance is covered in Section 13.11.4.)
在上面的例子中,尽管C是A和B的子类(通过B传递继承关系),但C的父类是B,这从它的声明中可以看出,所以,只有B会在C.__bases__中显示出来。另一方面,D是从两个类A和B中继承而来的。(多重继承参见13.11.4)
13.11.2 Overriding Methods through
Inheritance
通过继承覆盖(Overriding)方法
Let us create another function in P that we will override in its child class:
我们在P中再写一个函数,然后在其子类中对它进行覆盖。
class P(object):
def foo(self):
print 'Hi, I am P-foo()'
>>> p = P()
>>> p.foo()
Hi, I am P-foo()
Now let us create the child class C, subclassed from parent P:
现在来创建子类C,从父类P派生:
class C(P):
def foo(self):
print 'Hi, I am C-foo()'
>>> c = C()
>>> c.foo()
Hi, I am C-foo()
Although C inherits P’s foo() method, it is overridden because C defines its own foo() method. One reason for overriding methods is because you may want special or different functionality in your subclass. Your next obvious question then must be, “Can I call a base class method that I overrode in my subclass?”
尽管C继承了P的foo()方法,但因为C定义了它自已的foo()方法,所以 P 中的 foo() 方法被覆盖。覆盖方法的原因之一是,你的子类可能需要这个方法具有特定或不同的功能。所以,你接下来的问题肯定是:“我还能否调用那个被我覆盖的基类方法呢?”
The answer is yes, but this is where you will have to invoke an unbound base class method, explicitly providing the instance of the subclass, as we do here:
答案是肯定的,但是这时就需要你去调用一个未绑定的基类方法,明确给出子类的实例,例如下边:
>>> P.foo(c)
Hi, I am P-foo()
Notice that we already had an instance of P called p from above, but that is nowhere to be found in this example. We do not need an instance of P to call a method of P because we have an instance of a subclass of P which we can use, c. You would not typically call the parent class method this way. Instead, you would do it in the overridden method and call the base class method explicitly:
注意,我们上面已经有了一个P的实例p,但上面的这个例子并没有用它。我们不需要P的实例调用P的方法,因为已经有一个P的子类的实例c可用。典型情况下,你不会以这种方式调用父类方法,你会在子类的重写方法里显式地调用基类方法。
class C(P):
def foo(self):
P.foo(self)
print 'Hi, I am C-foo()'
Note how we pass in self explicitly in this (unbound) method call. A bet- ter way to make this call would be to use the super() built-in method:
注意,在这个(未绑定)方法调用中我们显式地传递了self. 一个更好的办法是使用super()内建方法:
class C(P):
def foo(self):
super(C, self).foo()
print 'Hi, I am C-foo()'
super() will not only find the base class method, but pass in self for us so we do not have to as in the previous example. Now when we call the child class method, it does exactly what you think it should do:
super()不但能找到基类方法,而且还为我们传进self,这样我们就不需要做这些事了。现在我们只要调用子类的方法,它会帮你完成一切:
>>> c = C()
>>> c.foo()
Hi, I am P-foo() Hi, I am C-foo()
CORE NOTE: Overriding __init__ does not invoke base class __init__
核心笔记:重写__init__不会自动调用基类的__init__
Similar to overriding non-special methods above, when deriving a class with a constructor __init__(), if you do not override __init__(), it will be inherited and automatically invoked. But if you do override __init__() in a subclass, the base class __init__() method is not invoked automatically when the subclass is instantiated. This may be surprising to those of you who know Java.
类似于上面的覆盖非特殊方法,当从一个带构造器 __init()__的类派生,如果你不去覆盖__init__(),它将会被继承并自动调用。但如果你在子类中覆盖了__init__(),子类被实例化时,基类的__init__()就不会被自动调用。这可能会让了解JAVA的朋友感到吃惊。
class P(object):
def __init__(self):
print "calling P's constructor"
class C(P):
def __init__(self):
print "calling C's constructor"
>>> c = C()
calling C's constructor
If you want the base class __init__() invoked, you need to do that explicitly in the same manner as we just described, calling the base class (unbound) method with an instance of the subclass. Updating our class C appropriately results in the following desired execution:
如果你还想调用基类的 __init__(),你需要像上边我们刚说的那样,明确指出,使用一个子类的实例去调用基类(未绑定)方法。相应地更新类C,会出现下面预期的执行结果:
class C(P):
def __init__(self):
P.__init__(self)
print "calling C's constructor"
>>> c = C()
calling P's constructor
calling C's constructor
In the above example, we call the base class __init__() method before the rest of the code in our own __init__() method. It is fairly common practice (if not mandatory) to initialize base classes for setup purposes, then proceed with any local setup. This rule makes sense because you want the inherited object properly initialized and “ready” by the time the code for the derived class constructor runs because it may require or set inherited attributes.
上边的例子中,子类的__init__()方法首先调用了基类的的__init__()方法。这是相当普遍(不是强制)的做法,用来设置初始化基类,然后可以执行子类内部的设置。这个规则之所以有意义的原因是,你希望被继承的类的对象在子类构造器运行前能够很好地被初始化或作好准备工作,因为它(子类)可能需要或设置继承属性。
Those of you familiar with C++ would call base class constructors in a derived class constructor declaration by appending a colon to the declaration followed by calls to any base class constructors.Whether the programmer does it or not, in Java, the base class constructor always gets called (first) in derived class constructors.
对C++熟悉的朋友,可能会在派生类构造器声明时,通过在声明后面加上冒号和所要调用的所有基类构造器这种形式来调用基类构造器。而在JAVA中,不管程序员如何处理,子类构造器都会去调用基类的的构造器。
Python’s use of the base class name to invoke a base class method is directly comparable to Java’s when using the keyword super, and that is why the super() built-in function was eventually added to Python, so you could “do the correct thing” functionally:
Python使用基类名来调用类方法,对应在JAVA中,是用关键字super来实现的,这就是super()内建函数引入到Python中的原因,这样你就可以“依葫芦画瓢”了:
class C(P):
def __init__(self):
super(C, self).__init__()
print "calling C's constructor"
The nice thing about using super() is that you do not need to give any base class name explicitly… it does all the legwork for you! The importance of using super() is that you are not explicitly specifying the parent class. This means that if you change the class hierarchy, you only need to change one line (the class statement itself ) rather than tracking through what could be a large amount of code in a class to find all mentions of what is now the old class name.
使用super()的漂亮之处在于,你不需要明确给出任何基类名字...“跑腿事儿”,它帮你干了!使用super()的重点,是你不需要明确提供父类。这意味着如果你改变了类继承关系,你只需要改一行代码(class语句本身)而不必在大量代码中去查找所有被修改的那个类的名字。
13.11.3 Deriving Standard Types
13.11.3 从标准类型派生
Not being able to subclass a standard data type was one of the most significant problems of classic classes. Fortunately that was remedied back in 2.2 with the unification of types and classes and the introduction of new-style classes. Below we present two examples of subclassing a Python type, one mutable and the other not.
经典类中,一个最大的问题是,不能对标准类型进行子类化。幸运的是,在2.2以后的版本中,随着类型(types)和类(class)的统一和新式类的引入, 这一点已经被修正。下面,介绍两个子类化Python类型的相关例子,其中一个是可变类型,另一个是不可变类型。
Immutable Type Example
不可变类型的例子
Let us assume you wanted to work on a subclass of floating point numbers to be used for financial applications. Any time you get a monetary value (as a float), you always want to round evenly to two decimal places. (Yes, the Decimal class is a better solution than standard floats to accurately store floating point values, but you still need to round them [occasionally] to two digits!) The beginnings of your class can look like this:
假定你想在金融应用中,应用一个处理浮点数的子类。每次你得到一个贷币值(浮点数给出的),你都需要通过四舍五入,变为带两位小数位的数值。(当然,Decimal类比起标准浮点类型来说是个用来精确保存浮点值的更佳方案,但你还是需要[有时候]对其进行舍入操作!)你的类开始可以这样写:
class RoundFloat(float):
def __new__(cls, val):
return float.__new__(cls, round(val, 2))
We override the __new__() special method, which customizes our object to be just a little bit different from the standard Python float: we round the original floating point number using the round() built-in function and then instantiate our float, RoundFloat. We create the actual object by calling our parent class constructor, float.__new__(). Note that all __new__() methods are class methods, and we have to explicitly pass in the class as the first argument, similar to how self is required for regular methods like __init__().
我们覆盖了__new__()特殊方法来定制我们的对象,使之和标准Python 浮点数(float)有一些区别:我们使用round()内建函数对原浮点数进行舍入操作,然后实例化我们的float,RoundFloat。我们是通过调用父类的构造器来创建真实的对象的,float.__new__()。注意,所有的__new()__方法都是类方法,我们要显式传入类传为第一个参数,这类似于常见的方法如__init__()中需要的self。
While our example is simple enough, i.e., we know we have a float, we are only subclassing from one type, etc., for general cases, it is better to use the super() built-in function to go and hunt down the appropriate superclass __new__() method to call. Below, we have modified our example with this change:
现在的例子还非常简单,比如,我们知道有一个float,我们仅仅是从一种类型中派生而来等等.通常情况下,最好是使用super()内建函数去捕获对应的父类以调用它的__new()__方法,下面,对它进行这方面的修改:
class RoundFloat(float):
def __new__(cls, val):
return super(RoundFloat, cls).__new__(cls, round(val, 2))
This example is far from complete, so keep an eye out for getting it in better shape as we progress through this chapter. Here is some sample output:
这个例子还远不够完整,所以,请留意本章我们将使它有更好的表现。下面是一些样例输出:
>>> RoundFloat(1.5955)
1.6
>>> RoundFloat(1.5945)
1.59
>>> RoundFloat(-1.9955)
-2.0
Mutable Type Example
可变类型的例子
Subclassing a mutable type is similar, and you probably do not need to use __new__() (or even __init__()) because there is typically not as much setup required. Usually the default behavior of the type you are deriving is what you want. In this simple example, we create a new dictionary type where its keys are returned sorted by the keys() method:
子类化一个可变类型与此类似,你可能不需要使用__new__() (或甚至__init__()),因为通常设置不多。一般情况下,你所继承到的类型的默认行为就是你想要的。下例中,我们简单地创建一个新的字典类型,它的keys()方法会自动排序结果:
class SortedKeyDict(dict):
def keys(self):
return sorted(super( SortedKeyDict, self).keys())
Recall that a dictionary can be created with dict(), dict(mapping), dict(sequence_of_2_tuples), or dict(**kwargs). Below is an exam- ple of using our new class:
回忆一下,字典(dictionary)可以由dict(),dict(mapping),dict(sequence_of_2_tuples),或者dict(**kwargs)来创建,看看下面使用新类的例子:
d = SortedKeyDict((('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2)))
print 'By iterator:'.ljust(12), [key for key in d]
print 'By keys():'.ljust(12), d.keys()
If we put all the code in a script and run it, we get the following output:
把上面的代码全部加到一个脚本中,然后运行,可以得到下面的输出:
By iterator: ['zheng-cai', 'xin-yi', 'hui-jun']
By keys(): ['xin-yi', 'hui-jun', 'zheng-cai']
For our example, the iterator progresses through the keys in the hashed order while using our (overridden) keys() method gives the keys in lexicographically sorted order.
在上例中,通过keys迭代过程是以散列顺序的形式,而使用我们(重写的)keys()方法则将keys变为字母排序方式了。
Always be cautious and conscious of what you are doing. What if, you say, “Your method is overly complicated with the call to super(),” and instead, you prefer keys() to be simpler (and easier to understand)... like this:
一定要谨慎,而且要意识到你正在干什么。如果你说,“你的方法调用super()过于复杂”,取而代之的是,你更喜欢keys()简简单单(也容易理解)....,像这样:
def keys(self):
return sorted(self.keys())
This is Exercise 13?19 at the end of the chapter.
这是本章后面的练习13-19。
13.11.4 Multiple Inheritance
多重继承
Like C++, Python allows for subclassing from multiple base classes. This fea- ture is commonly known as multiple inheritance. The concept is easy, but the hard work is in how to find the correct attribute when it is not defined in the current (sub)class. There are two different aspects to remember when using multiple inheritance. The first is, again, being able to find the correct attribute. Another is when you have overridden methods, all of which call parent class methods to “take care of their responsibilities” while the child class takes care of its own obligations. We will discuss both simultaneously but focus on the latter as we describe the method resolution order.
同C++一样,Python允许子类继承多个基类。这种特性就是通常所说的多重继承。概念容易,但最难的工作是,如何正确找到没有在当前(子)类定义的属性。当使用多重继承时,有两个不同的方面要记住。首先,还是要找到合适的属性。另一个就是当你重写方法时,如何调用对应父类方法以“发挥他们的作用”,同时,在子类中处理好自己的义务。我们将讨论两个方面,但侧重后者,讨论方法解析顺序。
Method Resolution Order (MRO)
方法解释顺序(MRO)
In Python versions before 2.2, the algorithm was simple enough: a depth-first left-to-right search to obtain the attribute to use with the derived class. Unlike other Python algorithms that override names as they are found, multi- ple inheritance takes the first name that is found.
在Python 2.2以前的版本中,算法非常简单:深度优先,从左至右进行搜索,取得在子类中使用的属性。其它Python算法只是覆盖被找到的名字,多重继承则取找到的第一个名字。
Because of the entirely new structure of classes and types and the sub- classing of built-in types, this algorithm was no longer feasible, so a new MRO algorithm had to be developed. The initial one debuting in 2.2 was a good attempt but had a flaw (see Core Note below). It was immediately replaced in 2.3, which is the current one that is in use today.
由于类,类型和内建类型的子类,都经过全新改造, 有了新的结构,这种算法不再可行. 这样一种新的MRO算法被开发出来,在2.2版本中初次登场,是一个好的尝试,但有一个缺陷(看下面的核心笔记)。这在2.3版本中立即被修改,也就是今天还在使用的版本。
The exact resolution order is complex and is beyond the scope of this text, but you can read about it in the references given later on in this sec- tion. We can say that the new resolution method is more breadth-first than it is depth-first.
精确顺序解释很复杂,超出了本文的范畴,但你可以去阅读本节后面的参考书目提到的有关内容。这里提一下,新的查询方法是采用广度优先,而不是深度优先。
CORE NOTE: Python 2.2 uses a unique yet faulty MRO
核心笔记:Python 2.2 使用一种唯一但不完善的MRO
Python 2.2 was the first release using a new-style MRO that had to replace the algorithm from classic classes due to the reasons outlined above.For 2.2, the algorithm had the basic idea of following the hierarchy of each ancestor class and building a list of classes encountered, strategically removing duplicates. However, it was pointed out on the core Python developers mailing list that it fails to maintain monotonicity (order preservation), and had to be replaced by the new C3 algorithm that has been in place since 2.3.
Python 2.2 是首个使用新式MRO的版本,它必须取代经典类中的算法,原因在上面已谈到过。在2.2版本中,算法基本思想是根据每个祖先类的继承结构,编译出一张列表,包括搜索到的类,按策略删除重复的。然而,在Python核心开发人员邮件列表中,有人指出,在维护单调性方面失败过(顺序保存),必须使用新的C3算法替换,也就是从2.3版开始使用的新算法。
Let us give you an example to see how the method resolution order differs between classic and new-style classes.
下面的示例,展示经典类和新式类中,方法解释顺序有什么不同。
Simple Attribute Lookup Example
简单属性查找示例
The simple example below will highlight the differences between the old and new styles of resolution. The script consists of a pair of parent classes, a pair of child classes, and one grandchild class.
下面这个例子将对两种类的方案不同处做一展示。脚本由一组父类,一组子类,还有一个子孙类组成。
class P1: #(object): # parent class 1 父类1
def foo(self):
print 'called P1-foo()'
class P2: #(object): # parent class 2 父类2
def foo(self):
print 'called P2-foo()'
(foo) P1 P2 (foo, bar)
| \ / |
| / \ |
C1 C2 (bar)
\ /
GC
Figure 13-2 Relationships between parent, children, and grandchild classes as well as the methods they define.
图13-2 父类,子类及子孙类的关系图,还有它们各自定义的方法
def bar(self):
print 'called P2-bar()'
class C1(P1, P2): # child 1 der. from P1, P2 #子类1,从P1,P2派生
pass
class C2(P1, P2): # child 2 der. from P1, P2 #子类2,从P1,P2派生
def bar(self):
print 'called C2-bar()'
class GC(C1, C2): # define grandchild class #定义子孙类
pass # derived from C1 and C2 #从C1,C2派生
In Figure 13-2, we see the class relationships between the parent, chil- dren, and grandchildren classes. P1 defines foo(), P2 defines foo() and bar(), and C2 defines bar(). Let us now demonstrate the behavior of both classic and new-style classes.
在图13-2中,我们看到父类,子类及子孙类的关系。P1中定义了foo(),P2定义了foo()和bar(),C2定义了bar()。下面举例说明一下经典类和新式类的行为。
Classic Classes
经典类
We are going to use classic classes first. Upon executing the above declartions in the interactive interpreter, we can confirm the resolution order that classic classes use, depth-first, left to right:
首先来使用经典类。通过在交互式解释器中执行上面的声明,我们可以验证经典类使用的解释顺序,深度优先,从左至右:
>>> gc = GC()
>>> gc.foo() # GC ==> C1 ==> P1
called P1-foo()
>>> gc.bar() # GC ==> C1 ==> P1 ==> P2
called P2-bar()
When calling foo(), it looks in the current class (GC) first. If it cannot be found, it goes up to its immediate parent, C1. The search fails there so it con- tinues up the tree to its parent, P1, which is where foo() is found.
当调用foo()时,它首先在当前类(GC)中查找。如果没找到,就向上查找最亲的父类,C1。查找未遂,就继续沿树上访到父类P1,foo()被找到。
Likewise for bar(), it searches through GC, C1, and P1 before then find- ing it in P2. C2.bar() is never found because of the resolution order used.
同样,对bar()来说,它通过搜索GC,C1,P1然后在P2中找到。因为使用这种解释顺序的缘故,C2.bar()根本就不会被搜索了。
Now, you may be thinking, “I would prefer to call C2’s bar() because it is closer to me in the inheritance tree, thus more relevant.” In this case, you can still use it, but you have to do it in the typical unbound fashion by invoking its fully qualified name and providing a valid instance:
现在,你可能在想,“我更愿意调用C2的bar()方法,因为它在继承树上和我更亲近些,这样才会更合适。”在这种情况下,你当然还可以使用它,但你必须调用它的合法的全名,采用典型的非绑定方式去调用,并且提供一个合法的实例:
>>> C2.bar(gc)
called C2-bar()
New-Style Classes
新式类
Now uncomment the (object) next to the class declarations for P1 and P2 and reexecute. The new-style method resolution gives us something different:
取消类P1和类P2声明中的对(object)的注释,重新执行一下。新式方法的查询有一些不同:
>>> gc = GC()
>>> gc.foo() # GC ==> C1 ==> C2 ==> P1
called P1-foo()
>>> gc.bar() # GC ==> C1 ==> C2
called C2-bar()
Instead of following the tree up each step, it looks at the siblings first, giving it more of a breadth-first flavor. When looking for foo(), it checks GC, fol- lowed by C1 and C2, and then finds it in P1. If P1 did not have it, it would have gone to P2. The bottom line for foo() is that both classic and new-style classes would have found it in P1, but they took different paths to get there.
与沿着继承树一步一步上溯不同,它首先查找同胞兄弟,采用一种广度优先的方式。当查找foo(),它检查GC,然后是C1和C2,然后在P1中找到。如果P1中没有,查找将会到达P2。foo()的底线是,包括经典类和新式类都会在P1中找到它,然而它们虽然是同归,但殊途!
The result for bar() is different, though. It searches GC and C1, and finds it next in C2 and uses it there. It does not continue up to the grandparents P1 and P2. In this case, the new-style resolution fit into the scheme better if you did prefer to call the “closest” bar() from GC. And of course, if you still need to call one higher up, just do it in an unbound manner as before:
然而,bar()的结果是不同的。它搜索GC和C1,紧接着在C2中找到了。这样,就不会再继续搜索到祖父P1和P2。这种情况下,新的解释方式更适合那种要求查找GC更亲近的bar()的方案。当然,如果你还需要调用上一级,只要按前述方法,使用非绑定的方式去做,即可。
>>> P2.bar(gc)
called P2-bar()
New-style classes also have an __mro__ attribute that tells you what the search order is:
新式类也有一个__mro__属性,告诉你查找顺序是怎样的:
>>> GC.__mro__
(, , , , , <type 'object'>)
*MRO Problems Caused by Diamonds
菱形效应为难MRO
The classic class method resolution never gave folks too many problems. It was simple to explain and easy to understand. Most classes were single inheritance, and multiple inheritance was usually limited to mixing two completely discrete classes together. This is where the term mix-in classes (or “mix-ins”) comes from.
经典类方法解释不会带来很多问题。它很容易解释,并理解。大部分类都是单继承的,多重继承只限用在对两个完全不相关的类进行联合。这就是术语mixin类(或者“mix-ins”)的由来。
Why the Classic Classes MRO Fails
为什么经典类MRO会失败
The unification of types and classes in 2.2 brought about a new “problem,” and that is related to all (root) classes inheriting from object, the mother of all types. The diagram of a simple multiple inheritance hierarchy now formed a diamond. Taking some inspiration from Guido van Rossum’s essay, let us say that you have classic classes B and C, as defined below where C overrides its constructor but B does not, and D inherits from both B and C:
在版本2.2中,类型与类的统一,带来了一个新的“问题”,波及所有从object(所有类型的祖先类)派生出来的(根)类,一个简单的继承结构变成了一个菱形。从Guido van Rossum的文章中得到下面的灵感,打个比方,你有经典类B和C,C覆盖了构造器,B没有,D从B和C继承而来:
class B:
pass
class C:
def __init__(self):
print "the default constructor"
class D(B, C):
pass
When we instantiate D, we get:
当我们实例化D,得到:
>>> d = D()
the default constructor
Figure 13.3 illustrates the class hierarchy for B, C, and D, as well as the problem introduced when we change the code to use new-style classes:
图13.3为B,C和D的类继承结构,现在把代码改为采用新式类的方式,问题也就产生了:
class B(object):
pass
class C(object):
def __init__(self):
print "the default constructor"
(object)
A
/ \
/ \
B C ==> B C
\ / \ /
\ / \ /
D D
Classic classes New-style classes
Figure 13.3 Inheritance problems are caused by the appearance of the base class required by new-style classes, forming a diamond shape in the inheritance hierarchy. An instance of D should not miss an upcall to C nor should it upcall to A twice (since both B and C derive from A). Be sure to read the “Cooperative Methods” section of Guido van Rossum’s essay for further clarification.
图13.3 继承的问题是由于在新式类中,需要出现基类,这样就在继承结构中,形成了一个菱形。D的实例上溯时,不应当错过C,但不能两次上溯到A(因为B和C都从A派生)。去读读Guido van Rossum的文章中有关"协作方法"的部分,可以得到更深地理解。
Not much change here other than adding (object) to both class declara- tions, right? That is true, but as you can see in the diagram, the hierarchy is now a diamond; the real problem is in the MRO now. If we used the classic class MRO, when instantiating D, we no longer get C.__init__()… we get object.__init__()! This is the exact reason why the MRO needed to be changed.
代码中仅仅是在两个类声明中加入了(object),对吗?没错,但从图中,你可以看出,继承结构已变成了一个菱形;真正的问题就存在于MRO了。如果我们使用经典类的MRO,当实例化D时,不再得到C.__init__()之结果.....而是得到object.__init__()!这就是为什么MRO需要修改的真正原因。
Although we saw that it does change the way attributes are looked up in our example above with the GC class, you do not have to worry about lots of code breaking. Classic classes will still use the old MRO while new-style classes will use its MRO. Again, if you do not need all of the features of the new-style classes, there is nothing wrong with continuing to develop using classic classes.
尽管我们看到了,在上面的例子中,类GC的属性查找路径被改变了,但你不需要担心会有大量的代码崩溃。经典类将沿用老式MRO,而新式类将使用它自己的MRO。还有,如果你不需要用到新式类中的所有特性,可以继续使用经典类进行开发,不会有问题的。
Summary
总结
Classic classes have a depth-first MRO algorithm. Because new-style classes inherit from object, a new MRO had to be created because of problems ensuing from the now-diamond-shaped class hierarchy.
经典类,使用深度优先算法。因为新式类继承自object,新的菱形类继承结构出现,问题也就接着而来了,所以必须新建一个MRO。
You can read more about new-style classes, the MROs, and more in:
你可以在下面的链接中读在更多有关新式类、MRO的文章:
Guido van Rossum’s essay on the unification of types and classes:
Guido van Rossum的有关类型和类统一的文章:
http://www.python.org/download/releases/2.2.3/descrintro
PEP 252: Making Types Look More Like Classes
PEP 252:使类型看起来更像类
http://www.python.org/doc/peps/pep-0252
“What’s New in Python 2.2” document
“Python 2.2 新亮点” 文档
http://www.python.org/doc/2.2.3/whatsnew
Python 2.3 Method Resolution order paper
论文:Python 2.3 方法解释顺序
http://python.org/download/releases/2.3/mro/
13.12 Built-in Functions for Classes, Instances, and Other Objects
类、实例和其他对象的内建函数
13.12.1 issubclass()
13.12.1 issubclass()
The issubclass() Boolean function determines if one class is a subclass or descendant of another class. It has the following syntax:
issubclass() 布尔函数判断一个类是另一个类的子类或子孙类。它有如下语法:
issubclass(sub, sup)
issubclass() returns True if the given subclass sub is indeed a subclass of the superclass sup (and False otherwise). This function allows for an “improper” subclass, meaning that a class is viewed as a subclass of itself, so the function returns True if sub is either the same class as sup or derived from sup. (A “proper” subclass is strictly a derived subclass of a class.)
issubclass() 返回True的情况:给出的子类sub确实是父类sup的一个子类(反之,则为False)。这个函数也允许“不严格”的子类,意味着,一个类可视为其自身的子类,所以,这个函数如果当sub就是sup,或者从sup派生而来,则返回True。(一个“严格的”子类是严格意义上的从一个类派生而来的子类。)
Beginning with Python 2.3, the second argument of issubclass() can be tuple of possible parent classes for which it will return True if the first argu- ment is a subclass of any of the candidate classes in the given tuple.
从Python 2.3开始,issubclass()的第二个参数可以是可能的父类组成的tuple(元组),这时,只要第一个参数是给定元组中任何一个候选类的子类时,就会返回True。
13.12.2 isinstance()
The isinstance() Boolean function is useful for determining if an object is an instance of a given class. It has the following syntax:
isinstance() 布尔函数在判定一个对象是否是另一个给定类的实例时,非常有用。它有如下语法:
isinstance(obj1, obj2)
isinstance() returns True if obj1 is an instance of class obj2 or is an instance of a subclass of obj2 (and False otherwise), as indicated in the fol- lowing examples:
isinstance()在obj1是类obj2的一个实例,或者是obj2的子类的一个实例时,返回True(反之,则为False),看下面的例子:
>>> class C1(object): pass
...
>>> class C2(object): pass
...
>>> c1 = C1()
>>> c2 = C2()
>>> isinstance(c1, C1) True
>>> isinstance(c2, C1) False
>>> isinstance(c1, C2) False
>>> isinstance(c2, C2) True
>>> isinstance(C2, c2) Traceback (innermost last):
File "<stdin>", line 1, in ?
isinstance(C2, c2)
TypeError: second argument must be a class
Note that the second argument should be a class; otherwise, you get a TypeError. The only exception is if the second argument is a type object. This is allowed because you can also use isinstance() to check if an object obj1 is of the type obj2, i.e.,
注意:第二个参数应当是类;不然,你会得到一个TypeError。但如果第二个参数是一个类型对象,则不会出现异常。这是允许的,因为你也可以使用isinstance()来检查一个对象obj1是否是obj2的类型,比如:
>>> isinstance(4, int) True
>>> isinstance(4, str) False
>>> isinstance('4', str) True
If you are coming from Java, you may be aware of the warning against using its equivalent, instanceof(), due to performance reasons. A call to Python’s isinstance() will not have the same performance hit primarily because it only needs it to perform a quick search up the class hierarchy to determine what classes it is an instance of, and even more importantly, it is written in C!
如果你对Java有一定的了解,那么你可能知道Java中有个等价函数叫instanceof(),但由于性能上的原因,instanceof()并不被推荐使用。调用Python的isinstance()不会有性能上的问题,主要是因为它只用来来快速搜索类族集成结构,以确定调用者是哪个类的实例,还有更重要的是,它是用C写的!
Like issubclass(), isinstance() can also take a tuple as its second argument. This feature was added in Python 2.2. It will return True if the first argument is an instance of any of the candidate types and classes in the given tuple. Also be sure to read more about isinstance() in Section 13.16.1 on page 595.
同issubclass()一样,isinstance()也可以使用一个元组(tuple)作为第二个参数。这个特性是从Python 2.2版本中引进的。如果第一个参数是第二个参数中给定元组的任何一个候选类型或类的实例时,就会返回True。你还可以在595页,第13.16.1节中了解到更多有isinstance()的内容。
// 这里的 595 页依排版而订
13.12.3 hasattr(), getattr(),setattr(), delattr()
The *attr() functions can work with all kinds of objects, not just classes and instances. However, since they are most often used with those objects, we present them here. One thing that might throw you off is that when using these functions, you pass in the object you are working on as the first argument, but the attribute name, the second argument to these functions, is the string name of the attribute. In other words, when operating with obj.attr, the function call will be like *attr(obj, 'attr'...)―this will be clear in the examples that follow.
*attr()系列函数可以在各种对象下工作,不限于类(class)和实例(instances)。然而,因为在类和实例中使用极其频繁,就在这里列出来了。需要说明的是,当使用这些函数时,你传入你正在处理的对象作为第一个参数,但属性名,也就是这些函数的第二个参数,是这些属性的字符串名字。换句话说,在操作obj.attr时,就相当于调用*attr(obj,'attr'....)系列函数------下面的例子讲得很清楚。
The hasattr() function is Boolean and its only purpose is to determine whether or not an object has a particular attribute, presumably used as a check before actually trying to access that attribute. The getattr() and setattr() functions retrieve and assign values to object attributes, respectively. getattr() will raise an AttributeError exception if you attempt to read an object that does not have the requested attribute, unless a third, optional default argument is given. setattr() will either add a new attribute to the object or replace a pre-existing one. The delattr() function removes an attribute from an object.
hasattr()函数是Boolean型的,它的目的就是为了决定一个对象是否有一个特定的属性,一般用于访问某属性前先作一下检查。getattr()和setattr()函数相应地取得和赋值给对象的属性,getattr()会在你试图读取一个不存在的属性时,引发AttributeError异常,除非给出那个可选的默认参数。setattr()将要么加入一个新的属性,要么取代一个已存在的属性。而delattr()函数会从一个对象中删除属性。
Here are some examples using all the *attr() BIFs:
下面一些例子使用到了*attr()系列函数:
>>> class myClass(object):
... def __init__(self):
... self.foo = 100
...
>>> myInst = myClass()
>>> hasattr(myInst, 'foo') True
>>> getattr(myInst, 'foo')
100
>>> hasattr(myInst, 'bar') False
>>> getattr(myInst, 'bar') Traceback (most recent call last):
File "<stdin>", line 1, in ?
getattr(myInst, 'bar')
AttributeError: myClass instance has no attribute 'bar'
>>> getattr(c, 'bar', 'oops!')
'oops!'
>>> setattr(myInst, 'bar', 'my attr')
>>> dir(myInst)
['__doc__', '__module__', 'bar', 'foo']
>>> getattr(myInst, 'bar') # same as myInst.bar #等同于 myInst.bar
'my attr'
>>> delattr(myInst, 'foo')
>>> dir(myInst)
['__doc__', '__module__', 'bar']
>>> hasattr(myInst, 'foo') False
13.12.4 dir()
We first experienced dir() in Exercises 2-12, 2-13, and 4-7. In those exercises, we used dir() to give us information about all the attributes of a module. We now know that dir() can be applied to objects as well.
前面用到dir()是在练习2-12,2-13和4-7。在这些练习中,我们用dir()列出一个模块所有属性的信息。现在你应该知道dir()还可以用在对象上。
In Python 2.2, dir() received a significant upgrade. Because of these changes, the voluntarily implemented __members__ and __methods__ data attributes have been deprecated. dir() provides more details than the old one. According to the documentation, “In addition to the names of instance variables and regular methods, it also shows the methods that are normally invoked through special notations, like __iadd__ (+=), __len__ (len()), __ne__ (!=).” Here are more specifics from the Python documentation:
在Python 2.2中, dir()得到了重要的更新。因为这些改变,那些 __members__和__methods__ 数据属性已经被宣告即将不支持。dir()提供的信息比以前更加详尽。根据文档,“除了实例变量名和常用方法外,它还显示那些通过特殊标记来调用的方法,像__iadd__(+=),__len__(len()),__ne__(!=)。” 在Python文档中有详细说明。
dir() on an instance (classic or new-style) shows the instance variables as well as the methods and class attributes defined by the instance’s class and all its base classes.
dir() on a class (classic or new-style) shows the contents of the __dict__ of the class and all its base classes. It does not show class attributes that are defined by a metaclass.
dir() on a module shows the contents of the module’s __dict__. (This is unchanged.)
dir() without arguments shows the caller’s local variables.(Again, unchanged.)
There are more details; in particular, for objects that override __dict__ or __class__, these are honored, and for backwards compatibility, __members__ and __methods__ are honored if they are defined.
dir()作用在实例上(经典类或新式类)时,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性。
dir()作用在类上(经典类或新式类)时,则显示类以及它的所有基类的__dict__中的内容。但它不会显示定义在元类(metaclass)中的类属性。
dir()作用在模块上时,则显示模块的__dict__的内容。(这没改动)。
dir()不带参数时,则显示调用者的局部变量。(也没改动)。 关于更多细节:对于那些覆盖了__dict__或__class__属性的对象,就使用它们;出于向后兼容的考虑,如果已定义了__members__和__methods__,则使用它们。
13.12.5 super()
The super() function was added in 2.2 for new-style classes. The purpose of this function is to help the programmer chase down the appropriate superclass with which the proper method can be invoked. In simple cases, the programmer will likely just call the ancestor class method in an unbound fashion. Using super() simplifies the task of search for a suitable ancestor and passes in the instance or type object on your behalf when you call it.
super()函数在Python2.2版本新式类中引入。这个函数的目的就是帮助程序员找出相应的父类,然后方便调用相关的属性。一般情况下,程序员可能仅仅采用非绑定方式调用祖先类方法。使用super()可以简化搜索一个合适祖先的任务,并且在调用它时,替你传入实例或类型对象。
In Section 13.11.4, we described the method resolution order (MRO) that is used to chase down attributes in ancestor classes. For each class defined, an attribute named __mro__ is created as a tuple that lists the classes that need to be searched, in the order they are searched. Here is its syntax:
在第13.11.4节中,我们描述了文档解释顺序(MRO),用于在祖先类中查找属性。对于每个定义的类,都有一个名为__mro__的属性,它是一个元组,按照他们被搜索时的顺序,列出了备搜索的类。语法如下:
super(type[, obj])
Given type, super() “returns the superclass” of type. You may also pass in obj, which should be of the type type if you want the superclass to be bound, otherwise it will be unbound. The obj argument can also be a type,but it needs to be a subclass of type. In summary, when obj is given:
给出type,super()“返回此type的父类”。如果你希望父类被绑定,你可以传入obj参数(obj必须是type类型的).否则父类不会被绑定。obj参数也可以是一个类型,但它应当是type的一个子类。通常,当给出obj时:
If obj is an instance, then isinstance(obj, type) must be True
If obj is a class or type, then issubclass(obj, type) must be True
如果obj是一个实例,isinstance(obj,type)就必须返回True
如果obj是一个类或类型,issubclass(obj,type)就必须返回True
Actually, super() is a factory function that makes a super object that uses the __mro__ attribute for a given class to find the appropriate superclass. Most notably, it searches that MRO starting from the point where the current class is found. For more details, again please see Guido van Rossum’s essay on type and class unification, where he even gives a pure Python implementation of super() so you can get a better idea of how it works!
事实上,super()是一个工厂函数,它创造了一个super object,为一个给定的类使用__mro__去查找相应的父类。很明显,它从当前所找到的类开始搜索MRO。更多详情,请再看一下Guido van Rossum有关统一类型和类的文章,他甚至给出了一个super()的纯Python实现,这样,你可以加深其印象,知道它是如何工作的!
Final thought … super()’s primary use is in the lookup of a superclass attribute, e.g., super(MyClass, self).__init__(). If you are not performing such a lookup, you probably do not need to be using super().
最后想到.... super()的主要用途,是来查找父类的属性,比如,super(MyClass,self).__init__()。如果你没有执行这样的查找,你可能不需要使用super()。
There are various examples how to use super() scattered throughout this chapter. Also be sure to read the important notes about super()in Section 13.11.2, especially the Core Note in that section.
有很多如何使用super()的例子分散在本章中。记得阅读一下第13.11.2节中有关super()的重要提示,尤其是那节中的核心笔记。
13.12.6 vars()
The vars() built-in function is similar to dir() except that any object given as the argument must have a __dict__ attribute. vars() will return a dictionary of the attributes (keys) and values of the given object based on the values in its __dict__ attribute. If the object provided does not have such an attribute, an TypeError exception is raised. If no object is provided as an argument to vars(), it will display the dictionary of attributes (keys) and the values of the local namespace, i.e., locals(). We present an example of calling vars() with a class instance:
vars()内建函数与dir()相似,只是给定的对象参数都必须有一个__dict__属性。vars()返回一个字典,它包含了对象存储于其__dict__中的属性(键)及值。如果提供的对象没有这样一个属性,则会引发一个TypeError异常。如果没有提供对象作为vars()的一个参数,它将显示一个包含本地名字空间的属性(键)及其值的字典,也就是,locals()。我们来看一下例子,使用类实例调用vars():
class C(object):
pass
>>> c = C()
>>> c.foo = 100
>>> c.bar = 'Python'
>>> c.__dict__
{'foo': 100, 'bar': 'Python'}
>>> vars(c)
{'foo': 100, 'bar': 'Python'}
Table 13.3 summarizes the built-in functions for classes and class instances.
表13.3概括了类和类实例的内建函数。
Table 13.3 Built-in Functions for Classes, Instances,and Other Objects
表13.3 类,实例及其它对象的内建函数
Built-in Function Description
内建函数 描述
issubclass(sub, sup) Returns True if class sub is a subclass of class
sup, False otherwise
如果类sub是类sup的子类,则返回True,反之,为False。
isinstance(obj1, obj2) Returns True if instance obj1 is an instance
of class obj2 or is an instance of a subclass of
obj2; will also return True if obj1 is of type
obj2; otherwise it returns False
如果实例obj1是类obj2或者obj2子类的一个实例;
或者如果obj1是obj2的类型,则返回True;
反之,为False。
hasattr(obj, attr) Returns True if obj has attribute attr (given
as a string), False otherwise
如果obj有属性attr(用字符串给出),返回True,反之,返回False。
Table 13.3 Built-in Functions for Classes, Instances,and Other Objects (continued)
表13.3 类,实例及其它对象的内建函数(续)
Built-in Function Description
内建函数 描述
getattr(obj, attr[, default]) Retrieves attribute attr of obj; same as
return obj.attr; if attr is not an attribute
of obj, default returned if given; else
AttributeError exception raised
获取obj的attr属性;与返回obj.attr类似;如果attr不是obj的属性,
如果提供了默认值,则返回默认值;不然,就会引发一个AttributeError异常。
setattr(obj, attr, val) Sets attribute attr of obj to value val,
overriding any previously existing attribute value;
otherwise, attribute is created; same as obj.attr = val
设置obj的attr属性值为val,替换任何已存在的属性值;
不然,就创建属性;类似于obj.attr=val
delattr(obj, attr) Removes attribute attr (given as a string)
from obj; same as del obj.attr
从obj中删除属性attr(以字符串给出);类似于del obj.attr。
dir(obj=None) Returns a list of the attributes of obj; if obj not given,
dir() displays local namespace attributes, i.e., locals().keys()
返回obj的属性的一个列表;如果没有给定obj,dir()则显示局部名字空间
空间中的属性,也就是locals().keys()
super(type, obj=None) a Returns a proxy object representing the superclass of type;
if obj is not passed in, the super object returned is unbound; otherwise if obj is a type issubclass(obj, type) must be True; otherwise isinstance(obj, type) must be True
返回一个表示父类类型的代理对象;如果没有传入obj,则返回的super对象是非绑定的;反之,如果obj是一个type,issubclass(obj,type)必为True;否则,isinstance(obj,type)就必为True。
vars(obj=None) Returns a dictionary of the attributes and values
of obj; if obj not given, vars() displays local
namespace dictionary (attributes and values),
i.e., locals()
返回obj的属性及其值的一个字典;如果没有给出obj,vars()显示局部名字空间字典(属性及其值),也就是locals()。
------------------------------------------
a. New in Python 2.2; only works with new-style classes.
a. Python2.2中新增;仅对新式类有效
13.13 Customizing Classes with Special Methods
用特殊方法定制类
We covered two important aspects of methods in preceding sections of this chapter: first, that methods must be bound (to an instance of their corre- sponding class) before they can be invoked; and second, that there are two special methods which provide the functionality of constructors and destructors, namely __init__() and __del__() respectively.
我们已在本章前面部分讲解了方法的两个重要方面:首先,方法必须在调用前被绑定(到它们相应类的某个实例中);其次,有两个特殊方法可以分别作为构造器和析够器的功能,分别名为__init__()和__del__()。
In fact, __init__() and __del__() are part of a set of special methods which can be implemented. Some have the predefined default behavior of inaction while others do not and should be implemented where needed. These special methods allow for a powerful form of extending classes in Python. In particular, they allow for:
事实上,__init__()和__del__()只是可自定义特殊方法集中的一部分。它们中的一些有预定义的默认行为,而其它一些则没有,留到需要的时候去实现。这些特殊方法是Python中用来扩充类的强有力的方式。它们可以实现:
Emulating standard types
Overloading operators
模拟标准类型
重载操作符
Special methods enable classes to emulate standard types by overloading standard operators such as +, *, and even the slicing subscript and mapping operator [ ]. As with most other special reserved identifiers, these methods begin and end with a double underscore ( __ ). Table 13.4 presents a list of all special methods and their descriptions.
特殊方法允许类通过重载标准操作符+,*, 甚至包括分段下标及映射操作操作[] 来模拟标准类型。如同其它很多保留标识符,这些方法都是以双下划线(__)开始及结尾的。表13.4列出了所有特殊方法及其它的描述。
Table 13.4 Special Methods for Customizing Classes
表13.4 用来定制类的特殊方法
Special Method Description
特殊方法 描述
Basic Customization
基本定制型
C.__init__(self[, arg1, ...]) Constructor (with any optional arguments)
构造器(带一些可选的参数)
C.__new__(self[, arg1, ...])a Constructor (with any optional arguments); usually used for setting up subclassing of immutable data types
构造器(带一些可选的参数);通常用在设置不变数据类型的子类。
C.__del__(self) Destructor
解构器
C.__str__(self) Printable string representation;
str() built-in and print statement
可打印的字符输出;内建str()及print语句
C.__repr__(self) Evaluatable string representation;
repr() built-in and ‘‘ operator
运行时的字符串输出;
内建repr() 和‘‘ 操作符
C.__unicode__(self)b Unicode string representation;
unicode() built-in
Unicode字符串输出;
内建unicode()
C.__call__(self, *args) Denote callable instances
表示可调用的实例
C.__nonzero__(self) Define False value for object;
bool() built-in (as of 2.2)
为object定义False值;
内建bool() (从2.2版开始)
C.__len__(self) “Length” (appropriate for class);
len() built-in
“长度”(可用于类);内建len()
-------------------------------------------------
(continued)
(待续)
Table 13.4 Special Methods for Customizing Classes (continued)
表13.4 可以定制类的特殊方法(续)
Special Method Description
特殊方法 描述
Object (Value) Comparison c
对象(值)比较
C.__cmp__(self, obj) object comparison; cmp() built-in
对象比较;内建cmp()
C.__lt__(self, obj) and less than/less than or equal to;
C.__le__(self, obj) < and <= operators
小于/小于或等于;对应<及<=操作符
C.__gt__(self, obj) and greater than/greater than or equal to;
C.__ge__(self, obj) > and >= operators
大于/大于或等于;对应>及>=操作符
C.__eq__(self, obj) and equal/not equal to;
C.__ne__(self, obj) ==,!= and <> operators
等于/不等于;对应==,!=及<>操作符
Attributes
属性
C.__getattr__(self, attr) Get attribute; getattr() built-in;
called only if attributes not found
获取属性;内建getattr();仅当属性没有找到时调用
C.__setattr__(self, attr, val) Set attribute;
设置属性
C.__delattr__(self, attr) Delete attribute;
删除属性
C.__getattribute__(self, attr) a Get attribute; getattr() built-in;
always called
获取属性;内建getattr();总是被调用
C.__get__(self, attr) a (descriptor) Get attribute
(描述符)获取属性
C.__set__(self, attr, val) a (descriptor) Set attribute
(描述符)设置属性
C.__delete__(self, attr) a (descriptor) Delete attribute
(描述符)删除属性
Customizing Classes / Emulating Types
定制类/模拟类型
Numeric Types: Binary Operators d
数值类型:二进制操作符
C.__*add__(self, obj) Addition; + operator
加;+操作符
C.__*sub__(self, obj) Subtraction; - operator
减;-操作符
C.__*mul__(self, obj) Multiplication; * operator
乘;*操作符
C.__*div__(self, obj) Division; / operator
除;/操作符
C.__*truediv__(self, obj) e True division; / operator
True 除;/操作符
C.__*floordiv__(self, obj) e Floor division; // operator
Floor除;//操作符
C.__*mod__(self, obj) Modulo/remainder; % operator
取模/取余;%操作符
C.__*divmod__(self, obj) Division and modulo; divmod() built-in
除和取模;内建divmod()
C.__*pow__(self, obj[, mod]) Exponentiation; pow() built-in; **operator
乘幂;内建pow();**操作符
C.__*lshift__(self, obj) Left shift; << operator
左移位;<<操作符
Table 13.4 Special Methods for Customizing Classes (continued)
表13.4 可定制类的特殊方法(续)
Special Method Description
特殊方法 描述
Customizing Classes / Emulating Types
定制类/模拟类型
Numeric Types: Binary Operators f
数值类型:二进制操作符
C.__*rshift__(self, obj) Right shift; >> operator
右移;>>操作符
C.__*and__(self, obj) Bitwise AND; & operator
按位与;&操作符
C.__*or__(self, obj) Bitwise OR; | operator
按位或;|操作符
C.__*xor__(self, obj) Bitwise XOR; ^ operator
按位与或;^操作符
Numeric Types: Unary Operators
数值类型:一元操作符
C.__neg__(self) Unary negation
一元负
C.__pos__(self) Unary no-change
一元正
C.__abs__(self) Absolute value; abs() built-in
绝对值;内建abs()
C.__invert__(self) Bit inversion; ~ operator
按位求反;~操作符
Numeric Types: Numeric Conversion
数值类型:数值转换
C.__complex__(self, com) Convert to complex; complex() built-in
转为complex(复数);内建complex()
C.__int__(self) Convert to int; int() built-in
转为int;内建int()
C.__long__(self) Convert to long; long() built-in
转为long;内建long()
C.__float__(self) Convert to float; float() built-in
转为float;内建float()
Numeric Types: Base Representation (String)
数值类型:基本表示法(String)
C.__oct__(self) Octal representation; oct() built-in
八进制表示;内建oct()
C.__hex__(self) Hexadecimal representation; hex() built-in
十六进制表示;内建hex()
Numeric Types: numeric coercion
数值类型:数值压缩
C.__coerce__(self, num) Coerce to same numeric type; coerce() built-in
压缩成同样的数值类型;内建coerce()
C.__index__(self)g Coerce alternate numeric type to integer if/when necessary (e.g., for slice indexes, etc.)
在有必要时,压缩可选的数值类型为整型(比如:用于切片索引等等)
-----------------------------------------------------
(continued)
续
Table 13.4 Special Methods for Customizing Classes (continued)
表13.4 定制类的特殊方法(待续)
Sequence Types e
序列类型
C.__len__(self) Number of items in sequence
序列中项的数目
C.__getitem__(self, ind) Get single sequence element
得到单个序列元素
C.__setitem__(self, ind,val) Set single sequence element
设置单个序列元素
C.__delitem__(self, ind) Delete single sequence element
删除单个序列元素
Special Method Description
特殊方法 描述
Sequence Types e
序列类型
C.__getslice__(self, ind1,ind2) Get sequence slice
得到序列片断
C.__setslice__(self, i1, i2,val) Set sequence slice
设置序列片断
C.__delslice__(self, ind1,ind2) Delete sequence slice
删除序列片断
C.__contains__(self, val) f Test sequence membership; in keyword
测试序列成员;内建in关键字
C.__*add__(self,obj) Concatenation; + operator
串连;+操作符
C.__*mul__(self,obj) Repetition; * operator
重复;*操作符
C.__iter__(self) e Create iterator class; iter() built-in
创建迭代类;内建iter()
Mapping Types
映射类型
C.__len__(self) Number of items in mapping
mapping中的项的数目
C.__hash__(self) Hash function value
散列(hash)函数值
C.__getitem__(self,key) Get value with given key
得到给定键(key)的值
C.__setitem__(self,key,val) Set value with given key
设置给定键(key)的值
C.__delitem__(self,key) Delete value with given key
删除给定键(key)的值
C.__missing__(self,key) Provides default value when dictio-nary does not have given key
给定键如果不存在字典中,则提供一个默认值
----------------------------------------------
a. New in Python 2.2; for use with new-style classes only.
Python 2.2中新引入;仅用于新式类中。
b. New in Python 2.3.
Python 2.3中新引入。
c. All except cmp() new in Python 2.1.
除了cmp()外,其余全是在Python新引入的。
d. “*” either nothing (self OP obj), “r” (obj OP self ), or “i” for in-place operation (new in Python 2.0), i.e., __add__, __radd__, or __iadd__.
"*" 代表''(selp OP obj), 'r'(obj OP self),或'i'(原位(in-place)操作, Py2.0 新增), 例如 __add__, __radd__, or __iadd__.
e. New in Python 2.2.
Python 2.2中新引入。
f. “*” either nothing (self OP obj), “r” (obj OP self ), or “i” for in-place operation (new in Python 1.6), i.e., __add__, __radd__, or __iadd__.
g. New in Pathon 2.5.
Python 2.5中新引入。
---------------andelf 校对至这里-----12月2日17:51完成---------------
The Basic Customization and Object (Value) Comparison special methods can be implemented for most classes and are not tied to emulation of any specific types. The latter set, also known as Rich Comparisons, was added in Python 2.1. The Attributes group helps manage instance attributes of your class. This is also independent of emulation. There is also one more, __getattribute__(), which applies to new-style classes only, so we will describe it in an upcoming section.
基本的定制和对象(值)比较特殊方法在大多数类中都可以被实现,且没有同任何特定的类型模型绑定。延后设置,也就是所谓的Rich比较,在Python2.1中加入。属性组帮助管理您的类的实例属性。这同样独立于模型。还有一个,__getattribute__(),它仅用在新式类中,我们将在后面的章节中对它进行描述。
The Numeric Types set of special methods can be used to emulate various numeric operations, including those of the standard (unary and binary) oper- ators, conversion, base representation, and coercion. There are also special methods to emulate sequence and mapping types. Implementation of some of these special methods will overload operators so that they work with instances of your class type.
特殊方法中数值类型部分可以用来模拟很多数值操作,包括那些标准(一元和二进制)操作符,类型转换,基本表示法,及压缩。也还有用来模拟序列和映射类型的特殊方法。实现这些类型的特殊方法将会重载操作符,以使它们可以处理你的类类型的实例。
The additional division operators __*truediv__() and __*floor- div__() were added in Python 2.2 to support the pending change to the Python division operator―also see Section 5.5.3. Basically, if the interpreter has the new division enabled, either via a switch when starting up Python or via the import of division from __future__, the single slash division opera- tor ( / ) will represent true division, meaning that it will always return a float- ing point value, regardless of whether floats or integers make up the operands (complex division stays the same). The double slash division opera- tor ( // ) will provide the familiar floor division with which most engineers who come from the standard compiled languages like C/C++ and Java are familiar. Similarly, these methods will only work with these symbols applied to classes that implement these methods and when new division is enabled.
另外,除操作符__*truediv__()和__*floordiv__()在Python2.2中加入,用来支持Python除操作符中待定的更改---可查看5.5.3节。基本上,如果解释器启用新的除法,不管是通过一个开关来启动Python,还是通过"from __future__ import division",单斜线除操作(/)表示的将是ture除法,意思是它将总是返回一个浮点值,不管操作数是否为浮点数或者整数(复数除法保持不变)。双斜线除操作(//)将提供大家熟悉的浮点除法,从标准编译型语言像C/C++及Java过来的工程师一定对此非常熟悉。同样,这些方法只能处理实现了这些方法并且启用了新的除操作的类的那些符号。
Numeric binary operators in the table annotated with a wildcard asterisk in their names are so denoted to indicate that there are multiple versions of those methods with slight differences in their name. The asterisk either symbolizes no additional character in the string, or a single “r” to indicate a right-hand operation. Without the “r,” the operation occurs for cases that are of the format self OP obj; the presence of the “r” indicates the format obj OP self. For example, __add__(self, obj) is called for self + obj, and __radd__(self, obj) would be invoked for obj + self.
表格中,在它们的名字中,用星号通配符标注的数值二进制操作符则表示这些方法有多个版本,在名字上有些许不同。星号可代表在字符串中没有额外的字符,或者一个简单的“r”指明是一个右结合操作。没有“r”,操作则发生在对于self OP obj的格式; “r”的出现表明格式obj OP self。比如,__add__(self,obj)是针对self+obj的调用,而__radd__(self,obj)则针对obj+self来调用。
Augmented assignment, new in Python 2.0, introduces the notion of “in-place” operations. An “i” in place of the asterisk implies a combination left- hand operation plus an assignment, as in self = self OP obj. For exam- ple, __iadd__(self, obj) is called for self = self + obj.
增量赋值,起于Python 2.0,介绍了“原位”操作符。一个“i”代替星号的位置,表示左结合操作与赋值的结合,相当是在self=self OP obj。举例,__iadd__(self,obj)相当于self=self+obj的调用。
With the arrival of new-style classes in Python 2.2, several more special methods have been added for overriding. However, as we mentioned at the beginning of the chapter, we are now focusing only on the core portion of material applicable to both classic classes as well as new-style classes,and then later on in the chapter, we address the advanced features of new-style classes.
随着Python 2.2中新式类的引入,有一些更多的方法增加了重载功能。然而,在本章开始部分提到过,我们仅关注经典类和新式类都适应的核心部分,本章的后续部分,我们介绍新式类的高级特性。
13.13.1 Simple Customization ( RoundFloat2)
13.13.1 简单定制(RoundFloat2)
Our first example is totally trivial. It is based to some extent on the Round- Float class we saw earlier in the section on subclassing Python types. This example is simpler. In fact, we are not even going to subclass anything (except object of course)… we do not want to “take advantage” of all the “goodies” that come with floats. No, this time, we want to create a barebones example so that you have a better idea of how class customization works. The premise of this class is still the same as the other one: we just want a class to save a floating point number rounded to two decimal places.
我们的第一个例子很普通。在某种程度上,它基于我们前面所看到的从Python类型中派生出的派生类RoundFloat。这个例子很简单。事实上,我们甚至不想去派生任何东西(当然,除object外)...我们也不想采用与floats有关的所有“好东西”。不,这次,我们想创建一个苗条的例子,这样你可以对类定制的工作方式有一个更好的理解。这种类的前提与其它类是一样的:我们只要一个类来保存浮点数,四舍五入,保留两位小数位。
class RoundFloatManual(object):
def __init__(self, val):
assert isinstance(val, float), \
"Value must be a float!"
self.value = round(val, 2)
This class takes a single floating point value―it asserts that the type must be a float as it is passed to the constructor―and saves it as the instance attribute value. Let us try to execute it and create an instance of this class:
这个类仅接收一个浮点值----它断言了传递给构造器的参数类型必须为一个浮点数----并且将其保存为实例属性值。让我们来试试,创建这个类的一个实例:
>>> rfm = RoundFloatManual(42)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "roundFloat2.py", line 5, in __init__
assert isinstance(val, float), \ AssertionError: Value must be a float!
>>> rfm = RoundFloatManual(4.2)
>>> rfm
>>> print rfm
As you can see, it chokes on invalid input, but provides no output if input was valid. But look what happens when we try to dump the object in the interactive interpreter. We get some information, but this is not what we were looking for. (We wanted to see the numeric value, right?) And calling print does not apparently help, either.
你已看到,它因输入非法,而“噎住”,但如果输入正确时,就没有任何输出了。可是,当把这个对象转存在交互式解释器中时,看一下发生了什么。我们得到一些信息,却不是我们要找的。(我们想看到数值,对吧?)调用print语句同样没有明显的帮助。
Unfortunately, neither print (using str()) nor the actual object’s string representation (using repr()) reveals much about our object. One good idea would be to implement either __str__() or __repr__(), or both so that we can “see” what our object looks like. In other words, when you want to display your object, you actually want to see something meaningful rather than the generic Python object string (