免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 7416 | 回复: 0
打印 上一主题 下一主题

面向对象编程 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-23 02:08 |只看该作者 |倒序浏览
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 (). Let us add a __str__() method, overriding the default behavior: 不幸的是,print(使用str())和真正的字符串对象表示(使用repr())都没能显示更多有关我们对象的信息。一个好的办法是,去实现__str__()和__repr__()二者之一,或者两者都实现,这样我们就能“看到”我们的对象是个什么样子了。换句话说,当你想显示你的对象,实际上是想看到有意义的东西,而不仅仅是通常的Python对象字符串()。让我们来添加一个__str()__方法,以覆盖默认的行为: def __str__(self): return str(self.value) Now we get the following: 现在我们得到下面的: >>> rfm = RoundFloatManual(5.590464) >>> rfm <roundFloat2.RoundFloatManual object at 0x5eff0> >>> print rfm 5.59 >>> rfm = RoundFloatManual(5.5964) >>> print rfm 5.6  We still have a few problems . . . one is that just dumping the object in the interpreter still shows the default object notation, but that is not so bad. If we wanted to fix it, we would just override __repr__(). Since our string repre- sentation is also a Python object, we can make the output of __repr__() the same as __str__().  我们还有一些问题...一个问题是仅仅在解释器中转储(dump)对象时,仍然显示的是默认对象符号,但这样做也算不错。如果我们想修复它,只需要覆盖__repr__()。因为字符串表示法也是Python对象,我们可以让__repr__()和__str__()的输出一致。 To accomplish this, we can just copy the code from __str__() to __repr__(). This is a simple example, so it cannot really hurt us, but as a programmer, you know that is not the best thing to do. If a bug existed in __str__(), then we will copy that bug to __repr__(). 为了完成这些,只要把__str__()的代码复制给__repr__()。这是一个简单的例子,所以它没有真正对我们造成负面影响,但作为程序员,你知道那不是一个最好的办法。如果__str__()中存在bug,那么我们会将bug也复制给__repr__()了。 The best solution is to recall that the code represented by __str__() is an object too, and like all objects, references can be made to them, so let us just make __repr__() an alias to __str__(): 最好的方案,在__str__()中的代码也是一个对象,同所有对象一样,引用可以指向它们,所以,我们可以仅仅让__repr__()作为__str__()的一个别名: __repr__ = __str__ In the second example with 5.5964, we see that it rounds the value cor- rectly to 5.6, but we still wanted two decimal places to be displayed. One more tweak, and we should be done. Here is the fix: 在带参数5.5964的第二个例子中,我们看到它舍入值刚好为5.6,但我们还是想显示带两位小数的数。来玩玩一个更好的妙计吧,看下面: def __str__(self): return '%.2f' % self.value And here is the resulting output with both str() and repr() output: 这里就同时具备str()和repr()的输出了: >>> rfm = RoundFloatManual(5.5964) >>> rfm 5.60 >>> print rfm 5.60 Example 13.2 Basic Customization (roundFloat2.py) 例13.2 基本定制(roundFloat2.py) 1 #!/usr/bin/env python 2 3 class RoundFloatManual(object): 4 def __init__(self, val): 5 assert isinstance(val, float), \ 6 "Value must be a float!" 7 self.value = round(val, 2) 8 9 def __str__(self): 10 return '%.2f' % self.value 11 12 __repr__ = __str__ In our original RoundFloat example at the beginning of this chapter, we did not have to worry about all the fine-grained object display stuff; the reason is that __str__() and __repr__() have already been defined for us as part of the float class. All we did was inherit them. Our more “manual” ver- sion required additional work from us. Do you see how useful derivation is? You do not even need to know how far up the inheritance tree the interpreter needs to go to find a declared method that you are using without guilt. We present the full code of this class in Example 13.2. 在本章开始部分,最初的RoundFloat例子,我们没有担心所有细致对象的显示问题;原因是__str__()和__repr__()作为float类的一部分已经为我们定义好了。我们所要做的就是去继承它们。增强版本“手册”中需要另外的工作。你发现派生是多么的有益了吗?我们甚至不需要知道解释器在继承树上要执行多少步才能找到一个已声明的你正在使用却没有考虑过的方法。我们将在例13.2中列出这个类的全部代码。 Now let us try a slightly more complex example. 现在开始一个稍复杂的例子。 13.13.2 Numeric Customization (Time60) 13.13.2 数值定制(Time60) For our first realistic example, let us say we wanted to create a simple application that manipulated time as measured in hours and minutes. The class we are going to create can be used to track the time worked by an employee, the amount of time spent online by an ISP (Internet service provider) subscriber, the amount of total uptime for a database (not inclusive of downtime for backups and upgrades), the total amount of time played in a poker tournament, etc. 作为第一个实际的例子,我们可以想象需要创建一个简单的应用,用来操作时间,精确到小时和分。我们将要创建的这个类可用来跟踪职员工作时间,ISP用户在线时间,数据库总的运行时间(不包括备份及升级时的停机时间),在扑克比赛中玩家总时间,等等。 For our Time60 class, we will take integers as hours and minutes as input to our constructor. 在Time60类中,我们将整数的小时和分钟作为输入传给构造器。 class Time60(object): # ordered pair 顺序对 def __init__(self, hr, min): # constructor 构造器 self.hr = hr # assign hours 给小时赋值 self.min = min # assign minutes 给分赋值 Display 显示 Also, as seen in the previous example, we want meaningful output if we display our instances, so we need to override __str__() (and __repr__() if so desired). As humans, we are used to seeing hours and minutes in colon-delimited format, e.g. “4:30,” representing four and a half hours (four hours and thirty minutes): 同样,如前面的例子所示,在显示我们的实例的时候,我们需要一个有意义的输出,那么就要覆盖__str__()(如果有必要的话,__repr__()也要覆盖)。我们都习惯看小时和分,用冒号分隔开的格式,比如,“4:30”,表示四个小时,加半个小时(4个小时及30分钟): def __str__(self): return '%d:%d' % (self.hr, self.min)  Using this class, we can instantiate some objects. In the example below, we are starting a timesheet to track the number of billable hours for a contractor:  用此类,可以实例化一些对象。在下面的例子中,我们启动一个工时表来跟踪对应构造器的计费小时数: >>> mon = Time60(10, 30) >>> tue = Time60(11, 15) >>> >>> print mon, tue 10:30 11:15  The output is very nice, exactly what we wanted to see. What is the next step? Let us say we want our objects to interact. In particular, for our timesheet application, it is a necessity to be able to add Time60 instances together and have our objects do all meaningful operations. We would love to see something like this:  输出不错,正是我们想看到的。下一步干什么呢?可考虑与我们的对象进行交互。比如在时间片的应用中,有必要把Time60的实例放到一起让我们的对象执行所有有意义的操作。我们更喜欢像这样的: >>> mon + tue 21:45 Addition 加法 With Python, overloading operators is simple. For the plus sign ( + ), we need to overload the __add__() special method, and perhaps __radd__() and __iadd__(), if applicable. More on those in a little while. Implementing __add__() does not sound too difficult―we just add the hours together followed by the minutes. Most of the complexity lies in what we do with the new totals. If we want to see “21:45,” we have to realize that that is another Time60 object. We are not modifying mon or tue, so our method would have to create another object and fill it in with the sums we calculated. Python的重载操作符很简单。像加号(+),我们只需要重载__add__()方法,如果合适,还可以用__radd__()及__iadd__()。稍后有更多有关这方面的描述。实现__add__()听起来不难----只要把分和小时加在一块。大多数复杂性源于我们怎么处理这个新的总数。如果我们想看到“21:45”,就必须认识到这是另一个Time60对象,我们没有修改mon或tue,所以,我们的方法就应当创建另一个对象并填入计算出来的总数。  We implement the __add__() special method in such a way that we calculate the individual sums first, then call the class constructor to return a new object:  实现__add__()特殊方法时,首先计算出个别的总数,然后调用类构造器返回一个新的对象: def __add__(self, other): return self.__class__(self.hr + other.hr, self.min + other.min)  The new object is created by invoking the class as in any normal situation. The only difference is that from within the class, you typically would not invoke the class name directly. Rather, you take the __class__ attribute of self, which is the class from which self was instantiated, and invoke that. Because self.__class__ is the same as Time60, calling self.__class__() is the same as calling Time60(). 和正常情况下一样,新的对象通过调用类来创建。唯一的不同点在于,在类中,你一般不直接调用类名, 而是使用self的__class__属性,即实例化self的那个类,并调用它。由于self.__class__与Time60相同,所以调用self.__class__()与调用Time60()是一回事。  This is the more object-oriented approach anyway. The other reason is that if we used the real class name everywhere we create a new object and later on decided to change the class name to something else, we would have to perform very careful global search-and-replace. By using self.__class__, we do not have to do anything other than change the name in the class directive.  不管怎样,这是一个更面向对象的方式。另一个原因是,如果我们在创建一个新对象时,处处使用真实的类名,然后,决定将其改为别的名字,这时,我们就不得不非常小心地执行全局搜索并替换。如果靠使用self.__class__,就不需要做任何事情,只需要直接改为你想要的类名。 With our plus sign overloading, we can now “add” Time60 objects: 好了,我们现在来使用加号重载,“增加”Time60 对象: >>> mon = Time60(10, 30) >>> tue = Time60(11, 15) >>> mon + tue <time60.Time60 object at 0x62190> >>> print mon + tue 21:45  Oops, we forgot to add an __repr__ alias to __str__, which is easily fixable. One question you may have is, “What happens when I try to use an operator in an overload situation where I do not have the appropriate special methods defined?” The answer is a TypeError exception:  哎哟,我们忘记添加一个别名__repr__给__str__了,这很容易修复。你可能会问,“当我们试着在重载情况下使用一个操作符,却没有定义相对应的特殊方法时还有很多需要优化和重要改良的地方,会发生什么事呢?” 答案是一个TypeError异常: >>> mon - tue Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: unsupported operand type(s) for -: 'Time60' and 'Time60' In-Place Addition 原位加法 With augmented assignment (introduced back in Python 2.0), we may also wish to override the “in-place” operators, for example, __iadd__(). This is for supporting an operation like mon += tue and having the correct result placed in mon. The only trick with overriding an __i*__() method is that it has to return self. Let us add the following bits of code to our example, fixing our repr() issue above as well as supporting augmented assignment: 有了增量赋值(在Python 2.0中引入),我们也许还有希望覆盖“原位”操作,比如,__iadd__()。这是用来支持像mon += tue这样的操作符,并把正确的结果赋给mon。重载一个__i*__()方法的唯一秘密是它必须返回self。把下面的片断加到我们例子中,以修复上面的repr()问题,并支持增量赋值: __repr__ = __str__ def __iadd__(self, other): self.hr += other.hr self.min += other.min return self Here is our resulting output: 下面是结果输出: >>> mon =Time60(10,30) >>> tue =Time60(11,15) >>> mon 10:30 >>> id(mon) 401872 >>> mon += tue >>> id(mon) 401872 >>> mon 21:45  Note the use of the id() built-in function to confirm that before and after the in-place addition we are indeed modifying the same object and not creating a new one. This is a great start at a class that has a lot of potential. The complete class definition for Time60 is given in Example 13.3. 注意,使用id()内建函数是用来确定一下,在原位加的前后,我们确实是修改了原来的对象,而没有创建一个新的对象。对一个具有巨大潜能的类来说,这是很好的开始。在例13.3中给出了Time60的类的完全定义 Example 13.3 Intermediate Customization (time60.py) 例13.3 中级定制(time60.py) 1 #!/usr/bin/env python 2 3 class Time60(object): 4 'Time60 - track hours and minutes' 5 6 def __init__(self, hr, min): 7 'Time60 constructor - takes hours and minutes' 8 self.hr = hr 9 self.min = min 10 11 def __str__(self): 12 'Time60 - string representation' 13 return '%d:%d' % (self.hr, self.min) 14 15 __repr__ = __str__ 16 17 def __add__(self, other): 18 'Time60 - overloading the addition operator' 19 return self.__class__(self.hr + other.hr,self.min + other.min) 21 22 def __iadd__(self, other): 23 'Time60 - overloading in-place addition' 24 self.hr += other.hr 25 self.min += other.min 26 return self Example 13.4 Random Sequence Iterator (randSeq.py) 例13.4 随机序列迭代器(randSeq.py) 1 #!/usr/bin/env python 2 3 from random import choice 4 5 class RandSeq(object): 6 def __init__(self, seq): 7 self.data = seq 8 9 def __iter__(self): 10 return self 11 12 def next(self): 13 return choice(self.data) Further Refinements 升华 We will leave it here, but there is plenty of optimization and significant improvements that can be made to this class. For example, wouldn’t it be nice if we could just feed a 2-tuple (10, 30) into our constructor rather than having to pass in two separate arguments? What about a string like “10:30”? 现在暂不管它了,但在这个类中,还有很多需要优化和改良的地方。比如,如果我们不传入两个分离的参数,而传入一个2值元组给构造器作为参数,是不是更好些呢?如果是像“10:30”这样的字符串的话,结果会怎样?  The answer is yes, you can, and it is easy to do in Python but not by over- loading the constructor as the case may be with other object-oriented pro- gramming languages. Python does not allow overloading callables with multiple signatures, so the only way to make it happen is with a single constructor and performing self-introspection with the isinstance() and (per- haps) type() built-in functions.  答案是肯定的,你可以这样做,在Python中很容易做到,但不是像很多其他面向对象语言一样通过重载构造器来实现.Python不允许用多个签名重载可调用对象.所以实现这个功能的唯一的方式是使用单一的构造器,并由isinstance()和(可能的)type()内建函数执行自省功能。 Supporting multiple forms of input makes our application more robust and flexible. The same is true for the ability to perform other operations like subtraction. Of course these are optional and serve as icing on the cake, but what we should be worried about first are two moderate flaws: undesired format- ting when there are fewer than ten minutes and the lack of support of ***agesimal 1 (base 60) operations: 能支持多种形式的输入,能够执行其它操作像减法等,可以让我们的应用更健壮,灵活。当然这些是可选的,就像“蛋糕上的冰”,但我们首先应该担心的是两个中等程度的缺点:1.当比十分钟还少时,格式并不是我们所希望的,2. 不支持60进制(基数60)的操作: >>> wed = Time60(12, 5) >>> wed 12:5 >>> thu = Time60(10, 30) >>> fri = Time60(8, 45) >>> thu + fri 18:75 1. Latin-originated name for base 60; sometimes hexagesimal is used, a hybrid combining the Greek root “hexe” with the Latin “gesimal.” 1.源自拉丁语的基数是60的名字;有时,六十进制会被用到,这是一种希腊词根“hexe”和拉丁“gesmal”的混合。  Displaying wed should have resulted in “12:05,” and summing thu and fri should have given an output of “19:15.” The fixes for these flaws and the improvements suggested just above are great practice building your class customization skills. You can get a more complete description of these upgrades in Exercise 13?20 at the end of the chapter.  显示wed结果是“12:05”,把thu和fri加起来结果会是“19:15”。修改这些缺陷,实现上面的改进建议可以实际性地提高你编写定制类技能。这方面的更新,更详细的描述在本章的练习13.20中。  Hopefully, you now have a better understanding of operator overloading, why you would want to do it, and how you can implement special methods to accomplish that task. Let’s look at more complex customizations, continuing with the optional section that follows. 我们希望,你现在对于操作符重载,为什么要使用操作符重载,以及如何使用特殊方法来实现它已有了一个更好的理解了。接下来为选看章节内容,让我们来了解更多复杂的类定制的情况。 13.13.3 Iterators (RandSeq and AnyIter) 13.13.3 迭代器(RandSeq和AnyIter) RandSeq We were introduced to iterators formally in Chapter 8 but we have been using them throughout this text. They are simply a mechanism to go through items of a sequence (or sequence-like object) one at a time. In Chapter 8 we described how implementing the __iter__() and next() methods of a class can be used to create an iterator. We will demonstrate that with two examples here. 我们正式介绍迭代器是在第8章,但在全书中都在用它。它可以一次一个的遍历序列(或者是类似序列对象)中的项。在第8章中,我们描述了如何利用一个类中的__iter__()和next()方法,来创建一个迭代器。我们在此展示两个例子。  The first example is a RandSeq (short for RANDom SEQuence). We feed an initial sequence to our class, then let the user iterate (infinitely) through it via next().  第一个例子是RandSeq(RANDom SEQuence的缩写)。我们给我们的类传入一个初始序列,然后让用户通过next()去迭代(无穷)。 The __init__() method does the aforementioned assignment. The __iter__() just returns self, which is how you declare an object is an iterator, and finally, next() is called to get successive values of iteration. The only catch with this iterator is that it never ends. __init__()方法执行前述的赋值操作。__iter__()仅返回self,这就是如何将一个对象声明为迭代器的方式,最后,调用next()来得到迭代器中连续的值。这个迭代器唯一的亮点是它没有终点。  This example demonstrates some unusual things we can do with custom class iterations. One is infinite iteration. Because we read the sequence nondestructively, we never run out of elements. Each time the user calls next(),it gets the next value, but our object never raises StopIteration. If we run it, we will get output similar to the following: 这个例子展示了一些我们可以用定制类迭代器来做的与众不同的事情。一个是无穷迭代。因为我们无损地读取一个序列,所以它是不会越界的。每次用户调用next()时,它会得到下一个迭代值,但我们的对象永远不会引发StopIteration异常。我们来运行它,将会看到下面的输出: >>> from randseq import RandSeq >>> for eachItem in RandSeq( ... ('rock', 'paper', 'scissors')): ... print eachItem ... scissors scissors rock paper paper scissors : Example 13.5 Any Number of Items Iterator (anyIter.py) 例13.5 任意项的迭代器(anyIter.py) 1 #!/usr/bin/env python 2 3 class AnyIter(object): 4 def __init__(self, data, safe=False): 5 self.safe = safe 6 self.iter = iter(data) 7 8 def __iter__(self): 9 return self 10 11 def next(self, howmany=1): 12 retval = [] 13 for eachItem in range(howmany): 14 try: 15 retval.append(self.iter.next()) 16 except StopIteration: 17 if self.safe: 18 break 19 else: 20 raise 21 return retval AnyIter In the second example, we do create an iterator object, but rather than iterating through one item at a time, we give the next() method an argument telling how many items to return. Here is the code for our (ANY number of items ITERator): 在第二个例子中,我们的确创建了一个迭代器对象,我们传给next()方法一个参数,控制返回条目的数目,而不是去一次一个地迭代每个项。下面是我们的代码(ANY number of items ITERator):   Like RandSeq, the AnyIter class should be fairly simple to figure out. We described the basic operation above … it works just like any other itera- tor except that users can request the next N items of the iterable instead of only one.  和RandSeq类的代码一样,类AnyIter很容易领会。我们在上面描述了基本的操作...它同其它迭代器一样工作,只是用户可以请求一次返回N个迭代的项,而不仅是一个项。 We create the object by being given an iterable and a safe flag. If the flag is True, we will return any items retrieved before exhausting the iterable, but if the flag is False, we will reraise the exception if the user asked for too many items. The core of any complexity lies in next(), specifically how it quits (lines 14-21). 我们给出一个迭代器和一个安全标识符(safe)来创建这个对象。如果这个标识符(safe)为真(True),我们将在遍历完这个迭代器前,返回所获取的任意条目,但如果这个标识符为假(False),则在用户请求过多条目时,将会引发一个异常。错综复杂的核心在于next(),特别是它如何退出的(14-21行)。  In the last part of next(), we create a list of items to return and call the object’s next() for each item. If we exhaust the list and get a StopIteration exception, we check the safe flag. If unsafe, we throw the exception back to the caller (raise); otherwise, we return whatever items we have saved up (break and return).  在next()的最后一部分中,我们创建用于返回的一个列表项,并且调用对象的next()方法来获得每一项条目。如果我们遍历完列表,得到一个StopIteration异常,这时则检查安全标识符(safe)。如果不安全(即,self.safe=False),则将异常抛还给调用者(raise);否则, 退出(break)并返回(return)已经保存过的所有项。 >>> a = AnyIter(range(10)) >>> i = iter(a) >>> for j in range(1,5): >>> print j, ':', i.next(j) 1 : [0] 2 : [1, 2] 3 : [3, 4, 5] 4 : [6, 7, 8, 9]  The execution above ran fine because the iteration fit the number of items perfectly. What happens when things go awry? Let us try “unsafe” mode first, which is how we created our iterator to begin with from above:  上面程序的运行没有问题,因为迭代器正好符合项的个数。当情况出现偏差,会发生什么呢?让我们首先试试“不安全(unsafe)”的模式,这也就是紧随其后创建我们的迭代器: >>> i = iter(a) >>> i.next(14) Traceback (most recent call last): File "<stdin>", line 1, in ? File "anyIter.py", line 15, in next retval.append(self.iter.next()) StopIteration  The StopIteration exception was raised because we exceeded our supply of items, and that exception was reraised back to the caller (line 20). If we were to recreate the iterator in “safe” mode and run it with the same example, we get back whatever the iterator could get us before running out of items: 因为超出了项的支持量,所以出现了StopIteration异常,并且这个异常还被重新引发回调用者(第20行)。如果我们使用“安全(safe)”模式重建迭代器,再次运行一次同一个例子的话,我们就可以在项失控出现前得到迭代器所得到的元素: >>> a = AnyIter(range(10), True) >>> i = iter(a) >>> i.next(14) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 13.13.4 *Multi-type Customization (NumStr) 13.13.4 *多类型定制(NumStr) Let us create another new class, NumStr, consisting of a number-string ordered pair, called n and s, respectively, using integers as our number type. Although the “proper” notation of an ordered pair is (n, s), we choose to represent our pair as [n :: s] just to be different. Regardless of the nota- tion, these two data elements are inseparable as far as our model is concerned. We want to set up our new class, called NumStr, with the following characteristics: 现在创建另一个新类,NumStr,由一个数字-字符对组成,相应地,记为n和s,数值类型使用整型(integer)。尽管这组顺序对的“合适的”记号是(n,s),但我们选用[n::s]来表示它,有点不同。暂不管记号,这两个数据元素只要我们模型考虑好了,就是一个整体。可以创建我们的新类了,叫做NumStr,有下面的特征: Initialization 初始化 The class should be initialized with both the number and string; if either (or both) is missing, then 0 and the empty string should be used, i.e., n=0 and s='', as defaults. 类应当对数字和字符串进行初始化;如果其中一个(或两)没有初始化,则使用0和空字符串,也就是,n=0且s='',作为默认。 Addition 加法 We define the addition operator functionality as adding the numbers together and concatenating the strings; the tricky part is that the strings must be concatenated in the correct order. For example, let NumStr1 = [n1 :: s1] and NumStr2 = [n2 :: s2]. Then NumStr1 + NumStr2 is performed as [n1 + n2 :: s1 + s2] where + represents addition for numbers and concatenation for strings. 我们定义加法操作符,功能是把数字加起来,把字符连在一起;要点部分是字符串要按顺序相连。比如,NumStr1=[n1::s1]且NumStr2=[n2::s2]。则NumStr1+NumStr2表示[n1+n2::s1+s2],其中,+代表数字相加及字符相连接。 Multiplication 乘法 Similarly, we define the multiplication operator functionality as multiplying the numbers together and repeating or concatenating the strings, i.e., NumStr1 * NumStr2 = [n1 * n :: s1 * n]. 类似的,定义乘法操作符的功能为,数字相乘,字符累积相连,也就是,NumStr1*NumStr2=[n1*n::s1*n]。 False Value False 值 This entity has a false value when the number has a numeric value of zero and the string is empty, i.e., when NumStr = [0 :: '']. 当数字的数值为0且字符串为空时,也就是当NumStr=[0::'']时,这个实体即有一个false值。 Comparisons 比较 Comparing a pair of NumStr objects, i.e., [n1 :: s1] vs. [n2 :: s2], we find nine different combinations (i.e., n1 > n2 and s1 s2, etc.). We use the normal numeric and lexicographic compares for numbers and strings, respectively, i.e., the ordinary comparison of cmp(obj1, obj2) will return an integer less than zero if obj1 < obj2, greater than zero if obj1 > obj2, or equal to zero if the objects have the same value. 比较一对NumStr对象,比如,[n1::s1] vs. [n2::s2],我们可以发现九种不同的组合(即,n1>n2 and s1<s2,n1==n2 and s1>s2,等等)。对数字和字符串,我们一般按照标准的数值和字典顺序的进行比较,即,如果obj1<obj2,普通比较cmp(obj1,obj2)的返回值是一个小于0的整数,当obj1>obj2时,比较的返回值大于0,当两个对象有相同的值时,比较的返回值等于0。  The solution for our class is to add both of these values and return the result. The interesting thing is that cmp() does not always return -1, 0, or 1 for us. It is, as described above, an integer less than, equal to, or greater than zero.  我们的类的解决方案是把这些值相加,然后返回结果。有趣的是cmp()不会总是返回-1,0,或1。上面提到过,它是一个小于,等于或大于0的整数。  In order to correctly compare our objects, we need __cmp__() to return a value of 1 if (n1 > n2) and (s1 > s2), -1 if (n1 < n2) and (s1 < s2), and 0 if both sets of numbers and strings are the same, or if the comparisons offset each other, i.e., (n1 < n2) and (s1 > s2), or vice versa. 为了能够正确的比较对象,我们需要让__cmp__()在(n1>n2) 且 (s1>s2)时,返回 1,在(n1<n2)且(s1<s2)时,返回-1,而当数值和字符串都一样时,或是两个比较的结果正相反时(即(n1<n2)且(s1>s2),或相反),返回0.反之亦然。 Example 13.6 Multi-Type Class Customization (numstr.py) 例13.6 多类型类定制(numstr.py) 1 #!/usr/bin/env python 2 3 class NumStr(object): 4 5 def __init__(self, num=0, string=''): 6 self.__num = num 7 self.__string = string 8 9 def __str__(self): # define for str() 10 return '[%d :: %r]' % \ 11 self.__num, self.__string) 12 __repr__ = __str__ 13 14 def __add__(self, other): # define for s+o 15 if isinstance(other, NumStr): 16 return self.__class__(self.__num + \ 17 other.__num, \ 18 self.__string + other.__string) 19 else: 20 raise TypeError, \ 21 'Illegal argument type for built-in operation' 22 23 def __mul__(self, num): # define for o*n 24 if isinstance(num, int): 25 return self.__class__(self.__num * num 26 self.__string * num) 27 else: 28 raise TypeError, \ 29 'Illegal argument type for built-in operation' 30 31 def __nonzero__(self): # False if both are 32 return self.__num or len(self.__string) 33 34 def __norm_cval(self, cmpres):# normalize cmp() 35 return cmp(cmpres, 0) 36 37 def __cmp__(self, other): # define for cmp() 38 return self.__norm_cval( 39 cmp(self.__num, other.__num)) + \ 40 self.__norm_cval( 41 cmp(self.__string, other.__string)) Given the above criteria, we present the code below for numstr.py, with some sample execution: 根据上面的特征,我们列出numstr.py的代码,执行一些例子: >>> a = NumStr(3, 'foo') >>> b = NumStr(3, 'goo') >>> c = NumStr(2, 'foo') >>> d = NumStr() >>> e = NumStr(string='boo') >>> f = NumStr(1) >>> a [3 :: 'foo'] >>> b [3 :: 'goo'] >>> c [2 :: 'foo'] >>> d [0 :: ''] >>> e [0 :: 'boo'] >>> f [1 :: ''] >>> a < b True >>> b < c False >>> a == a True >>> b * 2 [6 :: 'googoo'] >>> a * 3 [9 :: 'foofoofoo'] >>> b + e [3 :: 'gooboo'] >>> e + b [3 :: 'boogoo'] >>> if d: 'not false' # also bool(d) ... >>> if e: 'not false' # also bool(e) ... 'not false' >>> cmp(a,b) -1 >>> cmp(a,c) 1 >>> cmp(a,a) 0 Line-by-Line Explanation 逐行解释 Lines 1-7 第1-7行 The top of our script features the constructor __init__() setting up our instance initializing itself with the values passed via the class instantiator call NumStr(). If either value is missing, the attribute takes on the default false value of either zero or the empty string, depending on the argument. 脚本的开始部分为构造器__init__(),通过调用NumStr()时传入的值来设置实例,完成自身初始化。如果有参数缺失,属性则使用false值,即默认的0或空字符,这取决于参数情况。  One significant oddity is the use of double underscores to name our attributes. As we will find out in the next section, this is used to enforce a level, albeit elementary, of privacy. Programmers importing our module will not have straightforward access to our data elements. We are attempting to enforce one of the encapsulation properties of OO design by permitting access only through accessor functionality. If this syntax appears odd or uncomfortable to you, you can remove all double underscores from the instance attributes, and the examples will still work in the exact same manner.  一个重要怪癖是命名属性时,双下划线的使用。我们在下一节中会看到,这是在信息隐藏时,强加一个级别,尽管不够成熟。程序员导入一个模块时,就不能直接访问到这些数据元素。我们正试着执行一种OO设计中的封装特性,只有通过存取函数才能访问。如果这种语法让你感觉有点怪异,不舒服的话,你可以从实例属性中删除所有双下划线,程序同样可以良好地运行。  All attributes that begin with a double underscore ( __ ) are “mangled” so that these names are not as easily accessible during runtime. They are not, however, mangled in such a way so that it cannot be easily reverse-engineered. In fact, the mangling pattern is fairly well known and easy to spot. The main point is to pre-vent the name from being accidentally used when it is imported by an external module where conflicts may arise. The name is changed to a new identifier name containing the class name to ensure that it does not get “stepped on” unintentionally. For more information, check out Section 13.14 on privacy. 所有的由双下划线(__)开始的属性都被“混淆”(mangled)了,导致这些名字在程序运行时很难被访问到。但是它们并没有用一种难于被逆向工程的方法来“混淆”。事实上,“混淆”属性的方式已众所周知,很容易被发现。这里主要是为了防止这些属性在被外部模块导入时,由于被意外使用而造成的名字冲突。我们将名字改成含有类名的新标志符,这样做,可以确保这些属性不会被无意“访问”。更多信息,请参见13.14节中关于私有成员的内容。 Lines 9-12 第9-12行 We choose the string representation of our ordered pair to be “[num ::'str']” so it is up to __str__() to provide that representation whenever str() is applied to our instance and when the instance appears in a print statement. Because we want to emphasize that the second element is a string, it is more visually convincing if the users view the string surrounded by quota- tion marks. To that end, we use the “repr()” representation format conversion code “%r” instead of “%s.” It is equivalent to calling repr() or using the single back quotation marks to give the evaluatable version of a string, which does have quotation marks: 我们把顺序对的字符串表示形式确定为“[num::'str']”,这样不论我们的实例用str()还是包含在print语句中时候,我们都可以用__str__()来提供这种表示方式。我们想强调一点,第二个元素是一个字符串,如果用户看到由引号标记的字符串时,会更加直观。要做到这点,我们使用“repr()”表示法对代码进行转换,把“%s”替换成“%r”。这相当于调用repr()或者使用单反引号来给出字符串的可求值版本--可求值版本的确要有引号: >>> print a [3 :: 'foo']  Not calling repr() on self.__string (leaving the backquotes off or using “%s”) would result in the string quotations being absent:  如果在self.__string中没有调用repr()(去掉单反引号或使用“%s”)将导致字符串引号丢失: return '[%d :: %s]' % (self.__num, self.__string) Now calling print again on an instance results in: 现在对实例再次调用print,结果: >>> print a [3 :: foo] How does that look without the quotations? Not as convincing that “foo” is a string, is it? It looks more like a variable. The author is not as convinced either. (We quickly and quietly back out of that change and pretend we never even touched it.) 没有引号,看起来会如何呢?不能信服“foo”是一个字符串,对吧?它看起来更像一个变量。连作者可能也不能确定。(我们快点悄悄回到这一变化之前,假装从来没看到这个内容。)  The first line of code after the __str__() function is the assignment of that function to another special method name, __repr__. We made a decision that an evaluatable string representation of our instance should be the same as the printable string representation. Rather than defining an entirely new function that is a duplicate of __str__(), we just create an alias, copying the reference. When you implement __str__(), it is the code that is called by the interpreter if you ever apply the str() built-in function using that object as an argument. The same goes for __repr__() and repr(). 代码中__str__()函数后的第一行是把这个函数赋给另一个特殊方法名,__repr__。我们决定我们的实例的一个可求值的字符串表示应当与可打印字符串表示是一样的。而不是去定义一个完整的新函数,成为__str__()的副本,我们仅去创建一个别名,复制其引用。当你实现__str__()后,一旦使用那个对象作为参数来应用内建str()函数,解释器就会调用这段代码.对__repr__()及repr()也一样。 How would our execution differ if we chose not to implement __repr__()? If the assignment is removed, only the print statement that calls str() will show us the contents of our object. The evaluatable string representation defaults to the Python standard of . 如果不去实现__repr__(),我们的结果会有什么不同呢?如果赋值被取消,只有调用str()的print语句才会显示对象的内容。而可求值字符串表示恢复成默认的Python标准形式 >>> print a # calls str(a) [3 :: 'foo'] >>> a # calls repr(a) <NumStr.NumStr instance at 122640> Lines 14-21 第14-21行 One feature we would like to add to our class is the addition operation, which we described earlier. One of Python’s features for customizing classes is that we can overload operators to make these types of customizations more “realistic.” Invoking a function such as “add(obj1, obj2)” to “add” objects obj1 and obj2 may seem like addition, but is it not more compelling to be able to invoke that same operation using the plus sign ( + ) like this? ==> obj1+ obj2 我们想加到我们的类中的一个特征就是加法操作,前面已提到过。Python用于定制类的特征之一是,我们可以重载操作符,以使定制的这些类型更“实用”。调用一个函数,像“add(obj1,obj2)”是为“add”对象obj1和ojb2,这看起来好像加法,但如果能使用加号(+)来调用相同的操作是不是更具竞争力呢?像这样,obj1+obj2。 Overloading the plus sign requires the implementation of __add__() for self (SELF) and the other operand (OTHER). The __add__() function takes care of the Self + Other case, but we do not need to define __radd__() to handle the Other + Self because that is taken care of by the __add__() for Other. The numeric addition is not affected as much as the string concatenation because order matters. 重载加号,需要去为self(SELF)和其它操作数实现(OTHER)__add__().__add__()函数考虑Self+Other的情况,但我们不需要定义__radd__()来处理Other+Self,因为这可以由Other的__add__()去考虑。数值加法不像字符串那样结果受到(操作数)顺序的影响.  The addition operation adds each of the two components, with the pair of results forming a new object―created as the results are passed to a call for instantiation as calling self.__class__() (again, also previously explained above). Any object other than a like type should result in a TypeError exception, which we raise in such cases. 加法操作把两个部分中的每一部分加起来,并用这个结果对形成一个新的对象----通过将结果做为参数调用self.__class__()来实例化(同样,在前面已解释过).碰到任何类型不正确的对象时,我们会引发一个TypeError异常. Lines 23-29 第23-29行 We also overload the asterisk [by implementing __mul__()] so that both numeric multiplication and string repetition are performed, resulting in a new object, again created via instantiation. Since repetition allows only an integer to the right of the operator, we must enforce this restriction as well. We also do not define __rmul__() for the same reason. 我们也可以重载星号[靠实现__mul__()],执行数值乘法和字符串重复,并同样通过实例化来创建一个新的对象。因为重复只允许整数在操作数的右边,因此也必执行此规则。基于同样的原因,我们在此也没有实现__rmul__()。 Lines 31-32 第31-32行 Python objects have a Boolean value at any time. For the standard types, objects have a false value when they are either a numeric equivalent of zero or an empty sequence or mapping. For our class, we have chosen both that its numeric value must be zero and that the string be empty in order for any such instance to have a false value. We override the __nonzero__() method for this purpose. Other objects such as those that strictly emulate sequence or mapping types use a length of zero as a false value. In those cases, you would implement the __len__() method to effect that functionality. Python对象任何时候都有一个Boolean值。对标准类型而言,对象有一个false值的情况为:它是一个类似于0的数值,或是一个空序列,或者映射。就我们的类而言,我们选择数值必须为0,字符串要为空 作为一个实例有一个false值的条件。覆盖__nonzero__()方法,就是为此目的。其它对象,像严格模拟序列或映射类型的对象,使用一个长度为0作为false值。这些情况,你需要实现__len__()方法,以实现那个功能。 Lines 34-41 第34-41行 __norm_cval() (short for “normalize cmp() value”) is not a special method. Rather, it is a helper function to our overriding of __cmp__(); its sole purpose is to convert all positive return values of cmp() to 1, and all negative values to -1. cmp() normally returns arbitrary positive or negative values (or zero) based on the result of the comparison, but for our purposes, we need to restrict the return values to only -1, 0, and 1. Calling cmp() with integers and comparing to zero will give us the result we need, being equivalent to the following snippet of code: __norm_cval() (“normalize cmp() value的缩写”)不是一个特殊方法。它是一个帮助我们重载__cmp__()的助手函数:唯一的目的就是把cmp()返回的正值转为1,负值转为-1。cmp()基于比较的结果,通常返回任意的正数或负数(或0),但为了我们的目的,需要严格规定返回值为-1,0和1。对整数调用cmp()及与0比较,结果即是我们所需要的,相当于如下代码片断: def __norm_cval(self, cmpres): if cmpres < 0: return -1 elif cmpres > 0: return 1 else: return 0  The actual comparison of two like objects consists of comparing the numbers and the strings, and returning the sum of the comparisons. 两个相似对象的实际比较是比较数字,比较字符串,然后返回这两个比较结果的和。 13.14 Privacy 13.14 私有化 Attributes in Python are, by default, “public” all the time, accessible by both code within the module and modules that import the module containing the class. Many OO languages provide some level of privacy for the data and provide only accessor functions to provide access to the values. This is known as implementation hiding and is a key component to the encapsulation of the object. 默认情况下,属性在Python中都是“public”,类所在模块和导入了类所在模块的其他模块的代码都可以访问到。很多OO语言给数据加上一些可见性,只提供访问函数来访问其值。这就是熟知的实现隐藏,是对象封装中的一个关键部分。 Most OO languages provide “access specifiers” to restrict access to member functions. 大多数OO语言提供“访问控制符”来限定成员函数的访问。 Double Underscore ( __ ) 双下划线(__) Python provides an elementary form of privacy for class elements (attributes or methods). Attributes that begin with a double underscore ( __ ) are man- gled during runtime so direct access is thwarted. In actuality, the name is prepended with an underscore followed by the class name. For example, let us take the self.__num attribute found in Example 13.6 (numstr.py). After the mangling process, the identifier used to access that data value is now self._NumStr__num. Adding the class name to the newly mangled result will prevent it from clashing with the same name in either ancestor or descendant classes. Python为类元素(属性和方法)的私有性提供初步的形式。由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许的。实际上,会在名字前面加上下划线和类名。比如,以例13.6(numstr.py)中的self.__num属性为例,被“混淆”后,用于访问这个数据值的标识就变成了self._NumStr__num。把类名加上后形成的新的“混淆”结果将可以防止在祖先类或子孙类中的同名冲突。  Although this provides some level of privacy, the algorithm is also in the public domain and can be defeated easily. It is more of a protective mechanism for importing modules that do not have direct access to the source code or for other code within the same module. 尽管这样做提供了某种层次上的私有化,但算法处于公共域中并且很容易被“击败”。这更多的是一种对导入源代码无法获得的模块或对同一模块中的其他代码的保护机制.  The other purpose of this type of name-mangling is to protect __XXX vari- ables from conflicting with derived class namespaces. If you have an __XXX attribute in a class, it will not be overridden by a child class’s ___XXX attribute.(Recall that if a parent has just an XXX attribute and a child defines one, then the child’s XXX overrides the parents, and the reason why you have to do PARENT.XXX to call the base class method of the same name.) By using __XXX, the code for the child class can safely use __XXX without worrying that it will use or affect __XXX in the parent. 这种名字混淆的另一个目的,是为了保护__XXX 变量不与父类名字空间相冲突。如果在类中有一个__XXX属性,它将不会被其子类中的__XXX属性覆盖。(回忆一下,如果父类仅有一个XXX属性,子类也定义了这个,这时,子类的XXX就是覆盖了父类的XXX,这就是为什么你必须使用PARENT.XXX来调用父类的同名方法。) 使用__XXX,子类的代码就可以安全地使用__XXX,而不必担心它会影响到父类中的__XXX。 Single Underscore ( _ ) 单下划线(_) As we discovered in Chapter 12, simple module-level privacy is provided by using a single underscore ( _ ) character prefixing an attribute name. This prevents a module attribute from being imported with “from mymodule import *”. This is strictly scope-based, so it will work with functions too. 与我们在第十二章发现的那样,简单的模块级私有化只需要在属性名前使用一个单下划线字符。这就防止模块的属性用“from mymodule import *”来加载。这是严格基于作用域的,所以这同样适合于函数。 With Python’s new-style classes introduced in 2.2, a whole new set of features was added to give programmers a significant amount of control over how much protection is offered class and instance attributes. Although Python does not have syntax built into the language that has the flavors of private, protected, friend, or protected friend, you can customize access in the exact way that fits your needs. We cannot cover all of those possibilities but will give you an idea of the new-style attribute access later in this chapter. 在Python 2.2中引进的新式类,增加了一套全新的特征,让程序员在类及实例属性提供保护的多少上拥有大量重要的控制权。尽管Python没有在语法上把private,protected,friend或protected friend等特征内建于语言中,但是可以按你的需要严格地定制访问权。我们不可能涵盖所有的内容,但会在本章后面给你一些有关新式类属性访问的建议。 13.15 *Delegation 13.15 *授权 13.15.1 Wrapping 13.15.1 包装 “Wrapping” is a term you will hear often in the Python programming world. It is a generic moniker to describe the packaging of an existing object, whether it be a data type or a piece of code, adding new, removing undesired, or other- wise modifying existing functionality to the existing object. “包装”在Python编程世界中经常会被提到的一个术语。它是一个通用的名字,意思是对一个已存在的对象进行包装,不管它是数据类型,还是一段代码,可以是对一个已存在的对象,增加新的,删除不要的,或者修改其它已存在的功能。  Before Python 2.2, the subclassing or derivation of a standard type in Python was not allowed. Even though you can do that now with the new-style classes, there is a concept that is still popular. You can always wrap any type as the core member of a class so that the new object’s behavior mimics all existing behavior of the data type that you want and does not do what you do not want it to do; and perhaps it will do something a little extra. This is called “wrapping a type.” In the Appendix, we will discuss how to extend Python, another form of wrapping. 在Python 2.2版本前,从Python标准类型子类化或派生类都是不允许的。即使你现在可以对新式类这样做,这一观念仍然很流行。你可以包装任何类型作为一个类的核心成员,以使新对象的行为模仿你想要的数据类型中已存在的行为,并且去掉你不希望存在的行为;它可能会要做一些额外的事情。这就是“包装类型”。在附录中,我们还将讨论如何扩充Python,包装的另一种形式。 Wrapping consists of defining a class whose instances have the core behavior of a standard type. In other words, it not only sings and dances now, but also walks and talks like our original type. Figure 15-4 illustrates what a type wrapped in a class looks like. The core behavior of a standard type is in the center of the figure, but it is also enhanced by new or updated functionality, and perhaps even by different methods of accessing the actual data. 包装包括定义一个类,它的实例拥有标准类型的核心行为。换句话说,它现在不仅能唱能跳,还能够像原类型一样步行,说话。图15-4举例说明了在类中包装的类型看起像个什么样子。在图的中心为标准类型的核心行为,但它也通过新的或最新的功能,甚至可能通过访问实际数据的不同方法得到提高。 Class Object (Which Behaves Like a Type) 类对象(其表现像类型) You may also wrap classes, but this does not make as much sense because there is already a mechanism for taking an object and wrapping it in a manner as described above for a standard type. How would you take an existing class, mimic the behavior you desire, remove what you do not like, and per- haps tweak something to make the class perform differently from the original class? That process, as we discussed recently, is derivation. 你还可以包装类,但这不会有太多的用途,因为已经有用于操作对象的机制,并且在上面已描述过,对标准类型有对其进行包装的方式。你如何操作一个已存的类,模拟你需要的行为,删除你不喜欢的,并且可能让类表现出与原类不同的行为呢?我们前面已讨论过,就是采用派生。  ———————————————————————— |     Customization Wrapper   | | | |                   | |     _____________________   | |    |  Standard Type   |  | |    |____________________|  | |___________________________________| Figure 13-4 Wrapping a Type 图13-4 包装类型 13.15.2 Implementing Delegation 13.15.2 实现授权 Delegation is a characteristic of wrapping that simplifies the process with regard to dictating functionality by taking advantage of pre-existing functionality to maximize code reuse. 授权是包装的一个特性,可用于简化处理有关dictating功能,采用已存在的功能以达到最大限度的代码重用。  Wrapping a type generally consists of some sort of customization to the existing type. As we mentioned before, this tweaking comes in the form of new, modified, or removed functionality compared to the original product. Everything else should remain the same, or keep its existing functionality and behavior. Delegation is the process whereby all the updated functionality is handled as part of the new class, but the existing functionality is delegated to the default attributes of the object.  包装一个类型通常是对已存在的类型的一些定制。我们在前面提到过,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样,或者保留已存功能和行为。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。  The key to implementing delegation is to override the __getattr__() method with code containing a call to the built-in getattr() function. Spe- cifically, getattr() is invoked to obtain the default object attribute (data attribute or method) and return it for access or invocation. The way the spe- cial method __getattr__() works is that when an attribute is searched for, any local ones are found first (the customized ones). If the search fails, then __getattr__() is invoked, which then calls getattr() to obtain an object’s default behavior.  实现授权的关键点就是覆盖__getattr__()方法,在代码中包含一个对getattr()内建函数的调用。特别地,调用getattr()以得到默认对象属性(数据属性或者方法)并返回它以便访问或调用。特殊方法__getattr__()的工作方式是,当搜索一个属性时,任何局部对象首先被找到(定制的对象)。如果搜索失败了,则__getattr__()会被调用,然后调用getattr()得到一个对象的默认行为。  In other words, when an attribute is referenced, the Python interpreter will attempt to find that name in the local namespace, such as a customized method or local instance attribute. If it is not found in the local dictionary, then the class namespace is searched, just in case a class attribute was accessed. Finally, if both searches fail, the hunt begins to delegate the request to the original object, and that is when __getattr__() is invoked.  换言之,当引用一个属性时,Python解释器将试着在局部名称空间中查找那个名字,比如一个自定义的方法或局部实例属性。如果没有在局部字典中找到,则搜索类名称空间,以防一个类属性被访问。最后,如果两类搜索都失败了,搜索则对原对象开始授权请求,此时,__getattr__()会被调用。 Simple Example Wrapping Any Object 包装对象的简例  Let us take a look at an example. Here we present a class that wraps nearly any object, providing such basic functionality as string representations with repr() and str(). Additional customization comes in the form of the get() method, which removes the wrapping and returns the raw object. All remaining functionality is delegated to the object’s native attributes as retrieved by __getattr__() when necessary.  看一个例子。这个类已乎可以包装任何对象,提供基本功能,比如使用repr()和str()来处理字符串表示法。另外定制由get()方法处理,它删除包装并且返回原始对象。所以保留的功能都授权给对象的本地属性,在必要时,可由__getattr__()获得。 Here is an example of a wrapping class: 下面是包装类的例子: class WrapMe(object): def __init__(self, obj): self.__data = obj def get(self): return self.__data def __repr__(self): return ‘self.__data‘ def __str__(self): return str(self.__data) def __getattr__(self, attr): return getattr(self.__data, attr)  In our first example, we will use complex numbers, because of all Python’s numeric types, complex numbers are the only one with attributes: data attributes as well as its conjugate() built-in method. Remember that attributes can be both data attributes as well as functions or methods:  在第一个例子中,我们将用到复数,因为所有Python数值类型,只有复数拥有属性:数据属性,及conjugate()内建方法(求共轭复数,译者注!)。记住,属性可以是数据属性,还可以是函数或方法: >>> wrappedComplex = WrapMe(3.5+4.2j) >>> wrappedComplex # wrapped object: repr() 包装的对象:repr() (3.5+4.2j) >>> wrappedComplex.real # real attribute 实部属性 3.5 >>> wrappedComplex.imag # imaginary attribute 虚部属性 42.2 >>> wrappedComplex.conjugate() # conjugate() method conjugate()方法 (3.5-4.2j) >>> wrappedComplex.get() # actual object 实际对象 (3.5+4.2j)  Once we create our wrapped object type, we obtain a string representation, silently using the call to repr() by the interactive interpreter. We then proceed to access all three complex number attributes, none of which is defined for our class. Confirm this by looking for real, imag, and conjugate in our class definition . . . they are not there! 一旦我们创建了包装的对象类型,只要由交互解释器调用repr(),就可以得到一个字符串表示。然后我们继续访问了复数的三种属性,我们的类中一种都没有定义。在例子中,寻找实部,虚部及共轭复数的定义...they are not there!  The accessing of these attributes is delegated to the object via the get- attr() method. The final call to get() is not delegated because it is defined for our object―it returns the actual data object that we wrapped.  对这些属性的访问,是通过getattr()方法,授权给对象.最终调用get()方法没有授权,因为它是为我们的对象定义的----它返回包装的真实的数据对象。  Our next example using our wrapping class uses a list. We will create the object, then perform multiple operations, delegating each time to list methods.  下一个使用我们的包装类的例子用到一个列表。我们将会创建对象,然后执行多种操作,每次授权给列表方法。 >>> wrappedList = WrapMe([123, 'foo', 45.67]) >>> wrappedList.append('bar') >>> wrappedList.append(123) >>> wrappedList [123, 'foo', 45.67, 'bar', 123] >>> wrappedList.index(45.67) 2 >>> wrappedList.count(123) 2 >>> wrappedList.pop() 123 >>> wrappedList [123, 'foo', 45.67, 'bar']  Notice that although we are using a class instance for our examples, they exhibit behavior extremely similar to the data types they wrap. Be aware, however, that only existing attributes are delegated in this code.  注意,尽管我们正在我们的例子中使用实例,它们展示的行为与它们包装的数据类型非常相似。然后,需要明白,只有已存在的属性是在此代码中授权的。  Special behaviors that are not in a type’s method list will not be accessible since they are not attributes. One example is the slicing operations of lists which are built-in to the type and not available as an attribute like the append() method, for example. Another way of putting it is that the slice operator ( [ ] ) is part of the sequence type and is not implemented through the __getitem__() special method.  特殊行为没有在类型的方法列表中,不能被访问,因为它们不是属性。一个例子是,对列表的切片操作,它是内建于类型中的,而不是像append()方法那样作为属性存在的。从另一个角度来说,切片操作符是序列类型的一部分,并不是通过__getitem__()特殊方法来实现的。 >>> wrappedList[3] Traceback (innermost last): File "<stdin>", line 1, in ? File "wrapme.py", line 21, in __getattr__ return getattr(self.data, attr) AttributeError: __getitem__  The AttributeError exception results from the fact that the slice opera- tor invokes the __getitem__() method, and __getitem__() is not defined as a class instance method nor is it a method of list objects. Recall that getattr() is called only when an exhaustive search through an instance’s or class’s dictionaries fails to find a successful match. As you can see above, the call to getattr() is the one that fails, triggering the exception.  AttributeError异常出现的原因是切片操作调用了__getitem__()方法,且__getitme__()没有作为一个类实例方法进行定义,也不是列表对象的方法。回忆一下,什么时候调用getattr()呢?当在实例或类字典中的完整搜索失败后,就调用它来查找一个成功的匹配。你在上面可以看到,对getattr()的调用就是失败的那个,触发了异常。 However, we can always cheat by accessing the real object [with our get() method] and its slicing ability instead: 然而,我们还有一种"作弊"的方法,访问实际对象[通过我们的get()方法]和它的切片能力. >>> realList = wrappedList.get() >>> realList[3] 'bar' You probably have a good idea now why we implemented the get() method―just for cases like this where we need to obtain access to the origi- nal object. We can bypass assigning local variable (realList) by accessing the attribute of the object directly from the access call: 你现在可能知道为什么我们实现get()方法了----仅仅是为了我们需要取得对原对象进行访问这种情况,我们可以从访问调用中直接访问对象的属性,而忽略局部变量(realList): >>> wrappedList.get()[3] 'bar' The get() method returns the object, which is then immediately indexed to obtain the sliced subset. get()方法返回一个对象,随后被索引以得到切片片断。 >>> f = WrapMe(open('/etc/motd')) >>> f <wrapMe.WrapMe object at 0x40215dac> >>> f.get() <open file '/etc/motd', mode 'r' at 0x40204ca0> >>> f.readline() 'Have a lot of fun...\012' >>> f.tell() 21 >>> f.seek(0) >>> print f.readline(), Have a lot of fun... >>> f.close() >>> f.get() <closed file '/etc/motd', mode 'r' at 0x40204ca0>  Once you become familiar with an object’s attributes, you begin to under- stand where certain pieces of information originate and are able to duplicate functionality with your newfound knowledge:  一旦你熟悉了对象的属性,你就能够开始理解一些信息片断从何而来,能够利用新得到的知识来重复功能: >>> print "" % \ ... (f.closed and 'closed' or 'open', 'f.name', 'f.mode', id(f.get())) <closed file '/etc/motd', mode 'r' at 80e95e0>  This concludes the sampling of our simple wrapping class. We have only just begun to touch on class customization with type emulation. You will discover that you can an infinite number of enhancements make to further increase the usefulness of your code. One such enhancement is to add times- tamps to objects. In the next subsection, we will add another dimension to our wrapping class: time.  这总结了我们的简单包装类的例子。我们还刚开始接触使用类型模拟来进行类自定义。你将会发现你可以进行无限多的改进,来进一步增加你的代码的用途。一种改进方法是为对象添加时间戳。在下一小节中,我们将对我们的包装类增加另一个维度(dimension): Updating Our Simple Wrapping Class 更新简单的包裹类 Creation time, modification time, and access time are familiar attributes of files, but nothing says that you cannot add this type of information to objects. After all, certain applications may benefit from these additional pieces of information. 创建时间,修改时间,及访问时间是文件的几个常见属性,但没人说,你不能为对象加上这类信息。毕竟,一些应用能因有这些额外信息而受益。  If you are unfamiliar with using these three pieces of chronological data, we will attempt to clarify them. The creation time (or “ctime”) is the time of instantiation, the modification time (or “mtime”) refers to the time that the core data was updated [accomplished by calling the new set() method], and the access time (or “atime”) is the timestamp of when the data value of the object was last retrieved or an attribute was accessed. 如果你对使用这三类时间顺序(chronological)数据还不熟,我们将会对它们进行解释。创建时间(或'ctime')是实例化的时间,修改时间(或'mtime')指的是核心数据升级的时间[通常会调用新的set()方法],而访问时间(或'atime')是最后一次对象的数据值被获取或者属性被访问时的时间戳。 Proceeding to updating the class we defined earlier, we create the module twrapme.py, given in Example 13.7. 更新我们前面定义的类,可以创建一个模块twrapme.py,看例13.7。 How did we update the code? Well, first, you will notice the addition of three new methods: gettimeval(), gettimestr(), and set(). We also added lines of code throughout which update the appropriate timestamps based on the type of access performed. 如何更新这些代码呢?好,首先,你将会发现增加了三个新方法:gettimeval(),gettimestr(),及set()。我们还增加数行代码,根据所执行的访问类型,更新相应的时间戳。 Example 13.7 Wrapping Standard Types (twrapme.py) 例13.7 包装标准类型(twrapme.py) Class definition that wraps any built-in type, adding time attributes; get(), set(), and string representation methods; and delegating all remaining attribute access to those of the standard type. 类定义包装了任何内建类型,增加时间属性;get(),set(),还有字符串表示的方法;并授权所有保留的属性,访问这些标准类型。 1 #!/usr/bin/env python 2 3 from time import time, ctime 4 5 class TimedWrapMe(object): 6 7 def __init__(self, obj): 8 self.__data = obj 9 self.__ctime = self.__mtime = \ 10 self.__atime = time() 11 12 def get(self): 13 self.__atime = time() 14 return self.__data 15 16 def gettimeval(self, t_type): 17 if not isinstance(t_type, str) or \ 18 t_type[0] not in 'cma': 19 raise TypeError, \ 20 "argument of 'c', 'm', or 'a' req'd" 21 return getattr(self, '_%s__%stime' % \ 22 (self.__class__.__name__, t_type[0])) 23 24 def gettimestr(self, t_type): 25 return ctime(self.gettimeval(t_type)) 26 27 def set(self, obj): 28 self.__data = obj 29 self.__mtime = self.__atime = time() 30 31 def __repr__(self): # repr() 32 self.__atime = time() 33 return ‘self.__data‘ 34 35 def __str__(self): # str() 36 self.__atime = time() 37 return str(self.__data) 38 39 def __getattr__(self, attr): # delegate 40 self.__atime = time() 41 return getattr(self.__data, attr) The gettimeval() method takes a single character argument, either “c,” “m,” or “a,” for create, modify, or access time, respectively, and returns the corresponding time that is stored as a float value. gettimestr() simply returns a pretty-printable string version of the time as formatted by the time.ctime() function. gettimeval()方法带一个简单的字符参数,“c”,“m”或“a”,相应地,对应于创建,修改或访问时间,并返回相应的时间,以一个浮点值保存。gettimestr()仅仅返回一个经time.ctime()函数格式化的打印良好的字符串形式的时间。 Let us take a test drive of our new module. We have already seen how delegation works, so we are going to wrap objects without attributes to highlight the new functionality we just added. In our example, we will wrap an integer, then change it to a string. 为新的模块作一个测试驱动。我们已看到授权是如何工作的,所以,我们将包装没有属性的对象,来突出刚加入的新的功能。在例子中,我们包装了一个整数,然后,将其改为字符串。 >>> timeWrappedObj = TimedWrapMe(932) >>> timeWrappedObj.gettimestr('c') ‘Wed Apr 26 20:47:41 2006' >>> timeWrappedObj.gettimestr('m') 'Wed Apr 26 20:47:41 2006' >>> timeWrappedObj.gettimestr('a') 'Wed Apr 26 20:47:41 2006' >>> timeWrappedObj 932 >>> timeWrappedObj.gettimestr('c') 'Wed Apr 26 20:47:41 2006' >>> timeWrappedObj.gettimestr('m') 'Wed Apr 26 20:47:41 2006' >>> timeWrappedObj.gettimestr('a') 'Wed Apr 26 20:48:05 2006' You will notice that when an object is first wrapped, the creation, modifica- tion, and last access times are all the same. Once we access the object, the access time is updated, but not the others. If we use set() to replace the object, the modification and last access times are updated. One final read access to our object concludes our example. 你将注意到,一个对象在第一次被包装时,创建,修改,及最后一次访问时间都是一样的。一旦对象被访问,访问时间即被更新,但其它的没有动。如果使用set()来置换对象,则修改和最后一次访问时间会被更新。例子中,最后是对对象的读访问操作。 >>> timeWrappedObj.set('time is up!') >>> timeWrappedObj.gettimestr('m') 'Wed Apr 26 20:48:35 2006' >>> timeWrappedObj 'time is up!' >>> timeWrappedObj.gettimestr('c') 'Wed Apr 26 20:47:41 2006' >>> timeWrappedObj.gettimestr('m') 'Wed Apr 26 20:48:35 2006' >>> timeWrappedObj.gettimestr('a') 'Wed Apr 26 20:48:46 2006' Wrapping a Specific Object with Enhancements 改进包装一个特殊对象 The next example represents a class that wraps a file object. Our class will behave in the exact same manner as a regular file object with one exception:in write mode, only strings in all capital letters are written to the file. 下一个例子,描述了一个包装文件对象的类。我们的类与一般带一个异常的文件对象行为完全一样:在写模式中,字符串只有全部为大写时,才写入文件。  The problem we are trying to solve here is for a case where you are writing text files whose data is to be read by an old mainframe computer. Many older style machines are restricted to uppercase letters for processing, so we want to implement a file object where all text written to the file is automatically converted to uppercase without the programmer’s having to worry about it.  这里,我们要解决的问题是,当你正在写一个文本文件,其数据将会被一台旧电脑读取。很多老式机器在处理时,严格要求大写字母,所以,我们要实现一个文件对象,其中所有写入文件的文本会自动转化为大写,程序员就不必担心了。 In fact, the only noticeable difference is that rather than using the open() built-in function, a call is made to instantiate the CapOpen class. Even the parameters are exactly the same as for open(). 事实上,唯一值得注意的不同点是并不使用open()内建函数,而是调用CapOpen类时行初始化。尽管,参数同open()完全一样。  Example 13.8 represents that code, written as capOpen.py. Let us take a look at an example of how to use this class:  例13.8展示那段代码,文件名是capOpen.py。下面看一下例子中是如何使用这个类的: >>> f = CapOpen('/tmp/xxx', 'w') >>> f.write('delegation example\n') >>> f.write('faye is good\n') >>> f.write('at delegating\n') >>> f.close() >>> f <closed file '/tmp/xxx', mode 'w' at 12c230> Example 13.8 Wrapping a File Object (capOpen.py) 例13.8 包装文件对象(capOpen.py) This class extends on the example from one of the Python FAQs, providing a file-like object that customizes the write() method while delegating the rest of the functionality to the file object. 这个类扩充了Python FAQs中的一个例子,提供一个文件类对象,定制write()方法,同时,给文件对象授权其它的功能。 1 #!/usr/bin/env python 2 3 class CapOpen(object): 4 def __init__(self, fn, mode='r', buf=-1): 5 self.file = open(fn, mode, buf) 6 7 def __str__(self): 8 return str(self.file) 9 10 def __repr__(self): 11 return 'self.file' 12 13 def write(self, line): 14 self.file.write(line.upper()) 15 16 def __getattr__(self, attr): 17 return getattr(self.file, attr)  As you can see, the only call out of the ordinary is the first one to CapOpen() rather than open(). All other code is identical to what you would do if you were interacting with a real file object rather than a class instance that behaves like a file object. All attributes other than write() have been dele- gated to the file object. To confirm the success of our code, we load up the file and display its contents. (Note that we can use either open() or CapOpen(), but chose only CapOpen() because we have been working with it here in this example.)  可以看到,唯一不同的是第一次对CapOpen()的调用,而不是open()。如果你正与一个实际文件对象,而非行为像文件对象的类实例进行交互,那么其它所有代码与你本该做的是一样的。除了write(),所有属性都已授权给文件对象。为了确定代码是否正确,我们加载文件,并显示其内容。(注:可以使用open()或CapOpen(),这里因在本例中用到,所以选用CapOpen()。) >>> f = CapOpen('/tmp/xxx', 'r') >>> for eachLine in f: ... print eachLine, ... DELEGATION EXAMPLE FAYE IS GOOD AT DELEGATING 13.16 Advanced Features of New-Style Classes (Python 2.2+) 13.16 新式类的高级特性 (Python 2.2+) 13.16.1 General Features of New-Style Classes 13.16.1 新式类的通用特性 We have already discussed some of the features tied to new-style classes. With the unification of types and classes, the most significant of these features is the ability to subclass Python data types. One side effect of this is that all of the Python “casting” or conversion built-in functions are now factory functions. When you call them, you are really instantiating an instance of the corresponding type. 我们已提讨论过有关新式类的一些特性。由于类型和类的统一,这些特性中最重要的是能够子类化Python数据类型。其中一个副作用是,所有的Python内建的 “casting” 或转换函数现在都是工厂函数。当这些函数被调用时,你实际上是对相应的类型进行实例化。  The following built-in function, which have been around Python for a while, have been quietly (or perhaps not) converted to factory functions:  下面的内建函数,跟随Python多日,都已“悄悄地”(也许没有)转化为工厂函数: int(), long(), float(), complex() str(), unicode() list(), tuple() type() In addition, several new ones have been added to round out the posse: 还有,加入了一些新的函数来管理这些“散兵游勇”: basestring()1 dict() bool() set(),2 frozenset()2 object() classmethod() staticmethod() super() property() file()  These class names and factory functions have flexible usage. In addition to creating new objects of those types, they can be used as base classes when subclassing types, and they can now be used with the isinstance() built-in function. Using is instance() can help replace tired old idioms with one that requires fewer functions calls resulting in cleaner code. For example, to test if an object is an integer, we had to call type() twice or import the types module and use its attributes; but now we can just use isinstance() and even gain in performance:  这些类名及工厂函数使用起来,很灵活。不仅能够创建这些类型的新对象,它们还可以用来作为基类,去子类化类型,现在还可以用于isinstance()内建函数。使用isinstance()能够用于替换用烦了的旧风格,而使用只需少量函数调用就可以得到清晰代码的新风格。比如,为测试一个对象是否是一个整数,旧风格中,我们必须调用type()两次或者import相关的模块并使用其属性;但现在只需要使用isinstance(),甚至在性能上也有所超越: OLD (not as good): if type(obj) == type(0)… if type(obj) == types.IntType… BETTER: if type(obj) is type(0)… EVEN BETTER: if isinstance(obj, int)… if isinstance(obj, (int, long))… if type(obj) is int… Keep in mind that although isinstance() is flexible, it does not perform an “exact match” comparison―it will also return True if obj is an instance of the given type or an instance of a subclass of the given type. You will still need to use the is operator if you want an exact class match. 记住:尽管isinstance()很灵活,但它没有执行“严格匹配”比较----如果obj是一个给定类型的实例或其子类的实例,也会返回True。但如果想进行严格匹配,你仍然需要使用is操作符。 1. New in Python 2.3. 1. Python 2.3中新增。 2. New in Python 2.4. 2. Python 2.4中新增。  Please review Section 13.12.2 above for a deeper explanation of isin- stance() as well as its introduction in Chapter 4 and how these calls evolved along with Python.  请复习13.12.2节中有关isinstance()的深入解释,还有在第4章中介绍这些调用是如何随同Python的变化而变化的。 13.16.2 __slots__ Class Attribute 13.16.2 __slots__类属性 A dictionary is at the heart of all instances. The __dict__ attribute keeps track of all instance attributes. For example, when you have an instance inst with an attribute foo, recognize that accessing it with inst.foo is the same as doing it with inst.__dict__['foo']. 字典位于实例的“心脏”。__dict__属性跟踪所有实例属性。举例来说,你有一个实例inst.它有一个属性foo,那使用inst.foo来访问它与使用inst.__dict__['foo']来访问是一致的。 This dictionary takes up a good amount of memory, and if you have a class with very few attributes but a significant number of instances of this object, then you are taking a substantial hit. To combat this, users are now able to use the __slots__ attribute to substitute for __dict__. 字典会占据大量内存,如果你有一个属性数量很少的类,但有很多实例,那么正好是这种情况。为内存上的考虑,用户现在可以使用__slots__属性来替代__dict__。 Basically, __slots__ is a class variable consisting of a sequence-like object representing the set of valid identifiers that make up all of an instance’s attributes. This can be a list, tuple, or iterable. It can also be a single string identifying the single attribute that an instance can have. Any attempt to create an instance attribute with a name not in __slots__ will result in an AttributeError exception: 基本上,__slots__是一个类变量,由一序列型对象组成,由所有合法标识构成的实例属性的集合来表示。它可以是一个列表,元组或可迭代对象。也可以是标识实例能拥有的唯一的属性的简单字符串。任何试图创建一个其名不在__slots__中的名字的实例属性都将导致AttributeError异常: class SlottedClass(object): __slots__ = ('foo', 'bar') >>> c = SlottedClass() >>> >>> c.foo = 42 >>> c.xxx = "don't think so" Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'SlottedClass' object has no attribute 'xxx'  The primary reason for this feature is the conservation of memory. A side effect is a type of security preventing users from adding instances attributes dynamically in an ad hoc manner. A class defined with a __slots__ attribute will not have a __dict__ (unless you add '__dict__' as an element of __slots__). For more information on __slots__, see the Data Model chapter of the Python (Language) Reference Manual. 这种特性的主要目的是节约内存。其副作用是某种类型的"安全",它能防止用户随心所欲的动态增加实例属性。带__slots__属性的类定义不会存在__dict__了(除非你在__slots__中增加'__dict__'元素)。更多有关__slots__的信息,请参见Python(语言)参考手册中有关数据模型章节。 13.16.3 __getattribute__() Special Method 13.16.3 特殊方法__getattribute__() Python classes have a special method named __getattr__(), which is called only when an attribute cannot be found in an instance’s __dict__ or its class (class’s __dict__), or ancestor class (its __dict__). One place where we saw __getattr__() used was for implementing delegation. Python类有一个名为__getattr__()的特殊方法,它仅当属性不能在实例的__dict__或它的类(类的__dict__),或者祖先类(其__dict__)中找到时,才被调用。我们曾在实现授权中看到过使用__getattr__()。  The problem that many users encountered was that they wanted a certain function to execute for every attribute access, not just when one cannot be found. This is where __getattribute__() comes in. It works just like __getattr__() except that it is always called when an attribute is accessed, not just when it cannot be found.  很多用户碰到的问题是,他们想要一个适当的函数来执行每一个属性访问,不光是当属性不能找到的情况。这就是__getattribute__()用武之处了。它使用起来,类似__getattr__(),不同之处在于,当属性被访问时,它就一直都可以被调用,而不局限于不能找到的情况。  If a class has both __getattribute__() and __getattr__() defined, the latter will not be called unless explicitly called from __get- attribute__() or if __getattribute__() raises AttributeError. 如果类同时定义了__getattribute__()及__getattr__()方法,除非明确从__get-attribute__()调用,或__getattribute__()引发了AttributeError异常,否则后者不会被调用.  Be very careful if you are going to access attributes in here … attributes of this class or an ancestor. If you cause __getattribute__() to somehow call __getattribute__()again, you will have infinite recursion. To avoid infinite recursion using this method, you should always call an ancestor class method that shares the same name in order to access any attributes it needs safely; for example, super(obj, self).__getattribute__(attr). This special method is only valid with new-style classes. As with __slots__, you can get more information on __getattribute__() by referring to the Data Model chapter of the Python (Language) Reference Manual. 如果你将要在此(译者注:__getattribute__()中)访问这个类或其祖先类的属性,请务必小心。如果你在__getattribute__()中不知何故再次调用了__getattribute__(),你将会进入无穷递归。为避免在使用此方法时引起无穷递归,为了安全地访问任何它所需要的属性,你总是应该调用祖先类的同名方法;比如,super(obj,self).__getattribute__(attr)。此特殊方法只在新式类中有效。同__slots__一样,你可以参考Python(语言)参考手册中数据模型章节,以得到更多有关__getattribute__()的信息。 13.16.4 Descriptors 13.16.4 描述符 Descriptors are one of the keys behind Python’s new-style classes. They pro- vide a powerful API to object attributes. You can think of a descriptor as an agent that presents object attributes. Depending on which situation you encounter when you need an attribute, you can get to it via its descriptor (if there is one for it) or in the normal way (dotted attribute notation). 描述符是Python新式类中的关键点之一。它为对象属性提供强大的API。你可以认为描述符是表示对象属性的一个代理。当需要属性时,可根据你遇到的情况,通过描述符(如果有)或者采用常规方式(句点属性标识法)来访问它。 If there is an agent for your object and it has a “get” attribute (really spelled __get__), it is invoked, and you get back all you need to access the object in question. The same thing applies if you are attempting to assign a value to an object with a descriptor (set) or removing an attribute (delete). 如你的对象有代理,并且这个代理有一个“get”属性(实际写法为__get__),当这个代理被调 用时,你就可以访问这个对象了。当你试图使用描述符(set)给一个对象赋值或删除一个属性(delete)时,这同样适用。 __get__(), __set__(),__delete__() Special Methods __get__(),__set__(),__delete__()特殊方法 Strictly speaking, a descriptor is really any (new-style) class that implements at least one of three special methods that serve as the descriptor protocol: __get__(), __set__(), and __delete__(). As mentioned just above, __get__() is used to get the value of an attribute, __set__() is used to assign a value to an attribute, and __delete__() is called when an attribute is explicitly removed using the del statement (or rather, its reference count decremented). Of the three, the latter is rarely implemented. 严格来说,描述符实际上可以是任何(新式)类,这种类至少实现了三个特殊方法__get__(),__set__()及__delete__()中的一个,这三个特殊方法充当描述符协议的作用。刚才提到过,__get__()可用于得到一个属性的值,__set__()是为一个属性进行赋值的,在采用del语句(或其它,其引用计数递减)明确删除掉某个属性时会调__delete__()方法。三者中,后者很少被实现。  Also, not all descriptors implement the __set__() method either. These are referred to as method descriptors, or more accurately, non-data descrip- tors. Those that override both __get__() and __set__() are called data descriptors, and they are more powerful than non-data descriptors. 还有,也不是所有的描述符都实现了__set__()方法。它们被当作方法描述符,或更准确来说是,非数据描述符来被引用。那些同时覆盖__get__()及__set__()的类被称作数据描述符,它比非数据描述符要强大些。  The signatures for __get__(), __set__(), and __delete__() look like this: __get__(),__set__()及__delete__()的原型,如下: def __get__(self, obj, typ=None) ==> value def __set__(self, obj, val) ==> None def __delete__(self, obj) ==> None  When you want to use an agent for an attribute, you install it as a class attribute, and let the agent handle all the dirty work. Anytime you try to do something to an attribute with that name, you will get the descriptor that proxies all functionality. We covered wrapping in the previous section. This is just more wrapping going on. Instead of just delegating everything to objects in your class, we are delegating slightly more complex attribute access here. 如果你想要为一个属性写个代理,必须把它作为一个类的属性,让这个代理来为我们做所有的工作。当你用这个代理来处理对一个属性的操作时,你会得到一个描述符来代理所有的函数功能。我们在前面的一节中已经讲过封装的概念。这里我们会进一步来探讨封装的问题。现在让我们来处理更加复杂的属性访问问题,而不是将所有任务都交给你所写的类中的对象们。 __getattribute__() Special Method (again) __getattribute__() 特殊方法(二) Ordering matters when using descriptors, and certain aspects have precedence over others. The heart of the entire system is __getattribute__() since that special method is called for every attribute instance. It is the one that finds a class attribute or an agent to call on your behalf to access an attribute, etc. 使用描述符的顺序很重要,有一些描述符的级别要高于其它的。整个描述符系统的心脏是__getattribute__(),因为对每个属性的实例都会调用到这个特殊的方法。这个方法被用来查找类的属性,同时也是你的一个代理,调用它可以进行属性的访问等操作。  Reviewing the signatures just above, if __get__() is called for an instance, the object is passed in and perhaps a type or class. For example, given a class X and an instance x, x.foo is translated by __getattribute__() to: 回顾一下上面的原型,如果一个实例调用了__get__()方法,这就可能传入了一个类型或类的对象。举例来说,给定类X和实例x, x.foo由__getattribute__()转化成: type(x).__dict__['foo'].__get__(x, type(x)) If __get__() is called for a class, then None is passed in as the object (which would be self for an instance): 如果类调用了__get__()方法,那么None将作为对象被传入(对于实例, 传入的是self): X.__dict__['foo'].__get__(None, X)  Finally, if super() is called, for example given Y as a subclass of X, then super(Y, obj).foo looks in obj.__class__.__mro__ for the class right next to Y going up the tree to find class X, and then calls: 最后,如果super()被调用了,比如,给定Y为X的子类,然后用super(Y,obj).foo在obj.__class__.__mro__中紧接类Y沿着继承树来查找类X,然后调用: X.__dict__['foo'].__get__(obj, X) Then it is up to that descriptor to return the desired object. 然后,描述符会负责返回需要的对象。 Precedence 优先级别 The way __getattribute__() works needs to be covered, as it was implemented to behave in a very specific way. Thus it is very important to recognize this ordering: 由于__getattribute__()的实现方式很特别,我们在此对__getattribute__()方法的执行方式做一个介绍。因此了解以下优先级别的排序就非常重要了: Class attributes 类属性 Data descriptors 数据描述符 Instance attributes 实例属性 Non-data descriptors 非数据描述符 Defaulting to __getattr__() 默认为__getattr__()  A descriptor is a class attribute, so all class attributes have the highest priority. You can even replace a descriptor by simply reassigning its original reference to other objects. They are followed closely behind by descriptors with __get__() and __set__() implemented. If you have an agent, it will do all your work for you! 描述符是一个类属性,因此所有的类属性皆具有最高的优先级。你其实可以通过把一个描述符的引用赋给其它对象来替换这个描述符。比它们优先级别低一等的是实现了__get__()和__set__()方法的描述符。如果你实现了这个描述符,它会像一个代理那样帮助你完成所有的工作!  Otherwise, it should just default to the local object’s __dict__, meaning that it will be an instance attribute. The non-data descriptors come next. This may sound surprising because on first glance, one would think that these should be higher up in the food chain than instance attributes, but that is not the case. The purpose of non-data descriptors is only to provide a value if one is not already part of an instance, sort of how __getattr__() is only called if an attribute cannot be found in an instance’s __dict__! 否则,它就默认为局部对象的__dict__的值,也就是说,它可以是一个实例属性。接下来是非数据描述符。可能第一次听起来会吃惊,有人可能认为在这条“食物链”上非数据描述符应该比实例属性的优先级更高,但事实并非如此。非数据描述符的目的只是当实例属性值不存在时,提供一个值而已。这与以下情况类似:当在一个实例的__dict__中找不到某个属性时,才去调用__getattr__()。 Speaking of __getattr__(), if no non-data descriptor is found, then __getattribute__() raises an AttributeError, and that in turn causes __getattr__() to be invoked as the last stand before AttributeError is raised to the user. 关于__getattr__()的说明,如果没有找到非数据描述符,那么__getattribute__()将会抛出一个AttributeError异常,接着会调用__getattr__()做为最后一步操作,否则AttributeError会 返回给用户。 Descriptor Examples 描述符举例 Let us start with a very boring example… a descriptor that just discards any attempt to retrieve or set a value from and to an attribute, respectively. Actually, all of the examples here just ignore all requests, but they are incremental, and we hope that you can figure out a little more about descriptors for each one: 让我们来看一个简单的例子...用一个描述符禁止对属性进行访问或赋值的请求。事实上,以下所有示例都忽略了全部请求,但它们的功能逐步增多,我们希望你通过每个示例逐步掌握描述符的使用: class DevNull1(object): def __get__(self, obj, typ=None): pass def __set__(self, obj, val): pass  We create a class that uses this descriptor and try to assign something to it as well as display its value: 我们建立一个类,这个类使用了这个描述符,给它赋值并显示其值: >>> class C1(object): ... foo = DevNull1() ... >>> c1 = C1() >>> c1.foo = 'bar' >>> print 'c1.foo contains:', c1.foo c1.foo contains: None  That was not too terribly exciting … how about one where the descriptor methods at least give some output to show what is going on? 这并没有什么有趣的 ... 让我们来看看在这个描述符中写一些输出语句会怎么样? class DevNull2(object): def __get__(self, obj, typ=None): print 'Accessing attribute... ignoring' def __set__(self, obj, val): print 'Attempt to assign %r... ignoring' % (val) Now let us see this one in action: 现在我们来看看修改后的结果: >>> class C2(object): ... foo = DevNull2() ... >>> c2 = C2() >>> c2.foo = 'bar' Attempt to assign 'bar'... ignoring >>> x = c2.foo Accessing attribute... ignoring >>> print 'c2.foo contains:', x c2.foo contains: None  For our final example, let us add a placeholder in our descriptor class that holds some useful information about the descriptor: 最后,我们在描述符所在的类中添加一个占位符,占位符包含有关于这个描述符的有用信息: class DevNull3(object): def __init__(self, name=None): self.name = name def __get__(self, obj, typ=None): print 'Accessing [%s]... ignoring' % self.name) def __set__(self, obj, val): print 'Assigning %r to [%s]... ignoring' % val, self.name)  In the output below, we show you the importance of the hierarchy mentioned above, especially where we state that a full data descriptor has precedence over an instance attribute: 下面的输出结果表明我们前面提到的优先级层次结构的重要性,尤其是我们说过,一个完整的数据描述符比实例的属性具有更高的优先级: >>> class C3(object): ... foo = DevNull3('foo') ... >>> c3 = C3() >>> c3.foo = 'bar' Assigning 'bar' to [foo]... ignoring >>> x = c3.foo Accessing [foo]... ignoring >>> print 'c3.foo contains:', x c3.foo contains: None >>> print 'Let us try to sneak it into c3 instance...' Let us try to sneak it into c3 instance... >>> c3.__dict__['foo'] = 'bar' >>> x = c3.foo Accessing [foo]... ignoring >>> print 'c3.foo contains:', x c3.foo contains: None >>> print "c3.__dict__['foo'] contains: %r" % \ c3.__dict__['foo'], "... why?!?" c3.__dict__['foo'] contains: 'bar' ... why?!?  Notice how we were able to sneak in an attribute to our instance. We were able to assign the string “bar” to c3.foo, but because the data descriptor is more important, it overrides or effectively hides our assignment. 请注意我们是如何给实例的属性赋值的。给实例属性c3.foo赋值为一个字符串“bar”。但由于数据描述符比实例属性的优先级高,所赋的值“bar”被隐藏或覆盖了。  Likewise, because instance attributes have a higher precedence than non-data attributes, you can also hide such a descriptor, just as you can hide a class attribute, by assigning an instance attribute with the same name: 同样地,由于实例属性比非数据描述符的优先级高,你也可以将非数据描述符隐藏。这就和你给一个实例属性赋值,将对应类的同名属性隐藏起来是同一个道理: >>> class FooFoo(object): ... def foo(self): ... print 'Very important foo() method.' ... >>> >>> bar = FooFoo() >>> bar.foo() Very important foo() method. >>> >>> bar.foo = 'It is no longer here.' >>> bar.foo 'It is no longer here.' >>> >>> del bar.foo >>> bar.foo() Very important foo() method.  This was a pretty transparent example because we called it as a function, then accessed it as a string, but we could have used another function and kept the same calling mechanism, too: 这是一个直白的示例。我们将foo做为一个函数调用,然后又将它作为一个字符串访问,但我们也可以使用另一个函数,而且保持相同的调用机制: >>> def barBar(): ... print 'foo() hidden by barBar()' ... >>> bar.foo = barBar >>> bar.foo() foo() hidden by barBar() >>> >>> del bar.foo >>> bar.foo() Very important foo() method.  The point was to emphasize that because functions are non-data descriptors, instance attributes are ranked higher, and we can shadow any non-data descriptor simply by assigning an object to the instance (of the same name). 要强调的是:函数是非数据描述符,实例属性有更高的优先级,我们可以遮蔽任一个非数据描述符,只需简单的把一个对象赋给实例(使用相同的名字)就可以了。  Our final example does a little bit more. It is a crude attempt at using the filesystem as a means of storing the contents of an attribute. 我们最后这个示例完成的功能更多一些,它尝试用文件系统保存一个属性的内容,这是个雏形版本。 Lines 1-10 After the usual setup, we create our descriptor class with a class attribute(saved) that keeps track of all attributes with descriptor access. When a descriptor is created, it registers and saves the name of the attribute (passed in from the user). 第1-10行 在引入相关模块后,我们编写一个描述符类,类中有一个类属性(saved), 它用来记录描述符访问的所有属性。描述符创建后,它将注册并且记录所有从用户处接收的属性名。 Lines 12-26  When fetching an attribute, we need to ensure that users do not use it before they have even assigned a value to it. If it passes that test, then we attempt to open the pickle file to read in the saved value. An exception is raised if somehow the file cannot be opened, either because it was erased (or never created), or if it was corrupted or somehow cannot be unserialized by the pickle module. 第12-26行 在获取描述符的属性之前,我们必须确保用户给它们赋值后才能使用。如果上述条件成立,接着我们将尝试打开pickle文件以读取其中所保存的值。如果文件打开失败,将引发一个异常。文件打开失败的原因可能有以下几种:文件已被删除了(或从未创建过),或是文件已损坏,或是由于某种原因,不能被pickle模块反串行化。 Lines 28-38  Saving the attribute takes several steps: open the pickle file for write (either creating it for the first time or wiping out one that was already there), serializing the object to disk, and registering the name so users can retrieve the value. An exception is thrown if the object cannot be pickled. Note that if you are using Python older than 2.5, you cannot merge the try-except and try-finally statements together (lines 30-38). 第18-38行 将属性保存到文件中需要经过以下几个步骤:打开用于写入的pickle文件(可能是首次创建一个新的文件,也可能是删掉旧的文件),将对象串行化到磁盘,注册属性名,使用户可以读取这些属性值。如果对象不能被pickle{待统一命名},将引发一个异常。注意,如果你使用的是Python2.5以前的版本,你就不能合并try-except和try-finally语句(第30-38行)。 Example 13.9 Using a File to Store an Attribute (descr.py) 例13.9 使用文件来存储属性(descr.py) This class is crude but represents an interesting use of descriptors―being able to store the contents of an attribute on the filesystem. 这个类是一个雏形,但它展示了描述符的一个有趣的应用--可以在一个文件系统上保存属性的内容。 1 #!/usr/bin/env python 2 3 import os 4 import pickle 5 6 class FileDescr(object): 7 saved = [] 8 9 def __init__(self, name=None): 10 self.name = name 11 12 def __get__(self, obj, typ=None): 13 if self.name not in FileDescr.saved: 14 raise AttributeError, \ 15 "%r used before assignment" % self.name 16 17 try: 18 f = open(self.name, 'r') 19 val = pickle.load(f) 20 f.close() 21 return val 22 except(pickle.InpicklingError, IOError, 23 EOFError, AttributeError, 24 ImportError, IndexError), e: 25 raise AttributeError, \ 26 "could not read %r: %s" % self.name 27 28 def __set__(self, obj, val): 29 f = open(self.name, 'w') 30 try: 31 try: 32 pickle.dump(val, f) 33 FileDescr.saved.append(self.name) 34 except (TypeError, pickle.PicklingError), e: 35 raise AttributeError, \ 36 "could not pickle %r" % self.name 37 finally: 38 f.close() 39 40 def __delete__(self, obj): 41 try: 42 os.unlink(self.name) 43 FileDescr.saved.remove(self.name) 44 except (OSError, ValueError), e: 45 pass Lines 40-45 第40-45行  Finally, if the attribute is explicitly deleted, the file is removed, and the name unregistered. Here is some sample usage of this class: 最后,如果属性被删除了,文件会被删除,属性名字也会被注销。以下是这个类的用法示例: >>> class MyFileVarClass(object): ... foo = FileDescr('foo') ... bar = FileDescr('bar') ... >>> fvc = MyFileVarClass() >>> print fvc.foo Traceback (most recent call last): File "<stdin>", line 1, in ? File "descr.py", line 14, in __get__ raise AttributeError, \ AttributeError: 'foo' used before assignment >>> >>> fvc.foo = 42 >>> fvc.bar = 'leanna' >>> >>> print fvc.foo, fvc.bar 42 leanna >>> >>> del fvc.foo >>> print fvc.foo, fvc.bar Traceback (most recent call last): File "<stdin>", line 1, in ? File "descr.py", line 14, in __get__ raise AttributeError, \ AttributeError: 'foo' used before assignment >>> >>> fvc.foo = __builtins__ Traceback (most recent call last): File "<stdin>", line 1, in ? File "descr.py", line 35, in __set__ raise AttributeError, \ AttributeError: could not pickle 'foo'  Attribute access appears normal, and the programmer cannot really tell that an object is pickled and stored to the filesystem (except in the last example where we tried to pickle a module, a no-no). We also put in a handler for cases when the pickle file gets corrupted. This is also the first descriptor where we have implemented __delete__(). 属性访问没有什么特别的,程序员并不能准确判断一个对象是否能被打包后存储到文件系统中(除非如最后示例所示,将模块pickle,我们不该这样做)。我们也编写了异常处理的语句来处理文件损坏的情况。在本例中,我们第一次在描述符中实现__delete__()方法。 One thing to keep in mind with all of our examples is that we did not use the instance obj at all. Do not confuse obj with self as the latter is the instance of the descriptor, not the instance of the original class. 请注意,在示例中,我们并没有用到obj的实例。别把obj和self搞混淆,这个self是指描述符的实例,而不是类的实例。 Descriptor Summary 描述符总结 Believe it or not, you have already seen descriptors at work. Static methods, class methods, properties (see next section below), and even functions themselves are all descriptors. Think about this: functions are very generic objects in Python. There are built-in ones, user-defined ones, methods defined in classes, static methods, and class methods. Those are all examples of functions. The only difference between them is how they are called. Functions are normally unbound. So are static methods, even though they are defined in classes. But methods need to be bound to an instance, and class methods need to be bound to a class, right? The descriptor for a function object knows all this, so depending on what type of function it is, a descriptor can “wrap up” a function object along with whatever it needs to be bound to, if applicable, and then returns that back to the caller. The way it works is that the function itself is a descriptor, and its __get__() method is what puts together the callable that it returns for you. It is quite an amazing generality, which does not break the way Python has been working all this time! 你已经看到描述符是怎么工作的。静态方法、类方法、属性(见下面一节),甚至所有的函数都是描述符。想一想:函数是Python中常见的对象。有内置的函数、用户自定义的函数、类中定义的方法、静态方法、类方法。这些都是函数的例子。 它们之间唯一的区别在于调用方式的不同。通常,函数是非绑定的。虽然静态方法是在类中被定义的,它也是非绑定的。但方法必须绑定到一个实例上,类方法必须绑定到一个类上,对不?一个函数对象的描述符可以处理这些问题,描述符会根据函数的类型确定如何“封装”这个函数和函数被绑定的对象,然后返回调用对象。它的工作方式是这样的:函数本身就是一个描述符,函数的__get__()方法用来处理调用对象,并将调用对象返回给你。描述符具有非常棒的适用性,因此从来不会对Python自己的工作方式产生影响。 Properties and property() Built-in Function 属性和property()内建函数 Properties are a very useful and specific type of descriptor. They were meant to handle all accesses to instance attributes in a similar manner that we described for descriptors, above. When you access an instance attribute “normally,” you use the dotted-attribute notation. You were updating an instance’s __dict__ attribute. 属性是一种有用的特殊类型的描述符。它们是用来处理所有对实例属性的访问,其工作方式和我们前面说过的描述符相似。“一般”情况下,当你使用点属性符号来处理一个实例属性时,其实你是在修改这个实例的__dict__属性。 With properties, although your usage resembles normal attribute access, the actual implementation of such access uses function (or method) calls. In earlier versions of Python, as seen earlier this chapter, you could use __getattr__() and __setattr__() to play with attributes in general. The problem is that all attribute access goes through those special methods(and __getattribute__()), but with properties, you can give a property specific functions to execute for getting, setting, and deleting instance attributes, so you no longer have to use those other special methods (which became quite large actually if you had many instance attributes you were trying to manage). 表面上来看,你使用property()访问和一般的属性访问方法没有什么不同,但实际上这种访问的实现是不同的 - 它使用了函数(或方法)。在本章的前面,你已看到在Python的早期版本中,我们一般用__getattr__() 和 __setattr__() 来处理和属性相关的问题。属性的访问会涉及到以上特殊的方法(和__getattribute__()),但是如果我们用property()来处理这些问题,你就可以写一个和属性有关的函数来处理实例属性的获取(getting),赋值(setting),和删除(deleting)操作,而不必再使用那些特殊的方法了(如果你要处理大量的实例属性,使用那些特殊的方法将使代码变得很臃肿)。  The property() built-in function can take up to four arguments. Its signature is: property()内建函数有四个参数,它们是 : property(fget=None, fset=None, fdel=None, doc=None)  Keep in mind that although normal usage of property() is within a class definition where the functions passed in are actually methods, property() can accept functions. In fact, at the time that property() is called when a class is declared, those methods are unbound and thus really are functions! 请注意property()的一般用法是,将它写在一个类定义中,property()接受一些传进来的函数(其实是方法)作为参数。实际上,property()是在它所在的类被创建时被调用的,这些传进来的(作为参数的)方法是非绑定的,所以这些方法其实就是函数!  Here is a simple example that creates a read-only integer attribute but hides it within the class by barely encrypting it using the bitwise XOR operator: 下面的一个例子:在类中建立一个只读的整数属性,用逐位异或操作符将它隐藏起来: class ProtectAndHideX(object): def __init__(self, x): assert isinstance(x, int), \ '"x" must be an integer!' self.__x = ~x def get_x(self): return ~self.__x x = property(get_x)  If we try it out, we see that it saves the first value we give it but does not allow us to set it again: 我们来运行这个例子,会发现它只保存我们第一次给出的值,而不允许我们对它做第二次修改: >>> inst = ProtectAndHideX('foo') Traceback (most recent call last): File "<stdin>", line 1, in ? File "prop.py", line 5, in __init__ assert isinstance(x, int), \ AssertionError: "x" must be an integer! >>> inst = ProtectAndHideX(10) >>> print 'inst.x =', inst.x inst.x = 10 >>> inst.x = 20 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: can't set attribute Here is another example, but with a setter: 下面是另一个关于setter的例子: class HideX(object): def __init__(self, x): self.x = x def get_x(self): return ~self.__x def set_x(self, x): assert isinstance(x, int), \ '"x" must be an integer!' self.__x = ~x x = property(get_x, set_x) Here is the output of this example: 本示例的输出结果: >>> inst = HideX(20) >>> print inst.x 20 >>> inst.x = 30 >>> print inst.x 30  This property works because by the time the constructor is called to set the initial value of x, the getter already saves it as ~x to self.__x. 属性成功保存到x中并显示出来,是因为在调用构造器给x赋初始值前,在getter中已经将~x赋给了self.__x. You can even stick in a documentation string for your attribute, as shown here in this next example: 你还可以给自己写的属性添加一个文档字符串,参见下面这个例子: from math import pi def get_pi(dummy): return pi class PI(object): pi = property(get_pi, doc='Constant "pi"')   Here we are using a function instead of a method for our property, just to show it can be done. When it is called, however, we have to keep in mind that self is going to be passed in as the first (and only) argument, so we still need to have a dummy variable to discard it. Here is the corresponding output: 为了说明这是可行的实现方法,我们在property中使用的是一个函数而不是方法。注意在调用函数时self作为第一个(也是唯一的)参数被传入,所以我们必须加一个伪变量把self丢弃。下面是本例的输出: >>> inst = PI() >>> inst.pi 3.1415926535897931 >>> print PI.pi.__doc__ Constant "pi" Can you see how properties take your functions (fget, fset, and fdel) and map them as descriptor methods __get__(), __set__(), and __delete__()? You did not have to create a descriptor class and define these callables as methods of your descriptor class. You just created functions(or methods) and gave them all to property(). 你明白properties是如何把你写的函数(fget, fset 和 fdel)影射为描述符的__get__(), __set__(), 和__delete__()方法的吗?你不必写一个描述符类,并在其中定义你要调用的这些方法。只要把你写的函数(或方法)全部传递给property()就可以了。 One drawback to creating your descriptor methods inside your class definition is that it clutters up the class namespace. Not only that, but isn’t the point of having a property to control access to an attribute? But this control does not exist if they are not forced to use the property. Our second example does not enforce this because it allows access to our property methods (since they are part of the class definition): 在你写的类定义中创建描述符方法的一个弊端是它会搞乱类的名字空间。不仅如此,这种做法也不会像property()那样很好地控制属性访问。如果不用property()这种控制属性访问的目的就不可能实现。我们的第二个例子没有强制使用property(),因为它允许对属性方法的访问(由于在类定义中包含属性方法): >>> inst.set_x(40) # can we require inst.x = 40? >>> print inst.x 40  A clever idiom in a recipe in the ActiveState Programmer Network Python Cookbook (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183) solves both of these problems by: APNPC(ActiveState Programmer Network Python Cookbook (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183))上的一条精明的办法解决了以下问题: ? “Borrowing” a function’s namespace, “借用”一个函数的名字空间 ? Creating the methods as inner functions intentionally named as (keyword) arguments to property(), 编写一个用作内部函数的方法作为property()的(关键字)参数 ? Returning all the (function/method) names and corresponding objects in a dictionary (via locals()), (用locals())返回一个包含所有的(函数/方法)名和对应对象的字典 ? Feeding it directly to property(), and 把字典传入property(),然后 ? Blowing away that temporary namespace 去掉临时的名字空间  There is no method clutter in the class’s namespace because the methods were defined as inner functions in someone else’s namespace. The user has no access to the methods because the namespace in which they were defined was destroyed (by going out-of-scope), thus they are compelled to use the property as that is now the one and only way for them to access the attribute. Here is our modified class inspired by the recipe: 这样,方法就不会再把类的名字空间搞乱了,因为定义在内部函数中的这些方法属于其它的名字空间。由于这些方法所属的名字空间已超出作用范围,用户是不能够访问这些方法的,所以通过使用属性property()来访问属性就成为了唯一可行的办法。根据APNPC上方法,我们来修改这个类: class HideX(object): def __init__(self, x): self.x = x @property def x(): def fget(self): return ~self.__x def fset(self, x): assert isinstance(x, int), \ '"x" must be an integer!' self.__x = ~x return locals()  Our code works exactly as before, but there two big differences: (1) the namespace for the class is much smaller and consists (only) of ['__doc__', '__init__', '__module__', 'x'], and (2), the user can no longer use inst.set_x(40) to set the attribute... they have to use init.x = 40. We also use a function decorator (@property) to reassign x from a function to a property object. Since decorators were introduced in Python 2.4, those of you using 2.2.x or 2.3.x need to replace the decorator with the following assignment after the x() function declaration with x = property(**x()). 我们的代码工作如初,但有两点明显不同:(1) 类的名字空间更加简洁,只有 ['__doc__', '__init__', '__module__', 'x'], (2), 用户不能再通过inst.set_x(40) 给属性赋值 ... 必须使用init.x = 40. 我们还使用函数修饰符 (@property) 将函数中的x赋值到一个属性对象。由于修饰符是从Python 2.4版本开始引入的,如果你使用的是Python的早期版本2.2.x 或 2.3.x,请将修饰符@property去掉,在x()的函数声明后添加 x = property(**x())。 13.16.5 Metaclasses and __metaclass__ 13.16.5 Metaclasses和__metaclass__ What Are Metaclasses? 元类(Metaclasses)是什么? Metaclasses are probably the most mind-bending feature that was added with new-style classes. Metaclasses are classes that let you define how certain classes can be constructed, basically giving you a level of control over how classes are created. (You do not even need to think at the instance level.) They have been talked about since before the days of Python 1.5 (when many minds were bent), but they are finally a reality. 元类可能是添加到新风格类中最难以理解的功能了。元类让你来定义某些类是如何被创建的,从根本上说,赋予你如何创建类的控制权。(你甚至不用去想类实例层面的东西。)早在Python1.5的时代,人们就在谈论这些功能(当时很多人都认为不可能实现),但现在终于实现了。  Basically, you can think of a metaclass as the class of a class, or rather, a class whose instances are other classes. Believe it or not, whenever you create a class now, you are actually employing the default metaclass, which is a (or rather, the) type object. (If classic classes are used, the metaclasses for those are types.ClassType.) Take any class and call type() on it, and you will see what it is an instance of: 从根本上说,你可以把元类想成是一个类中类,或是一个类,它的实例是其它的类。实际上,当你创建一个新类时,你就是在使用默认的元类,它是一个类型对象。(对传统的类来说,它们的元类是types.ClassType.)当某个类调用type()函数时,你就会看到它到底是谁的实例: class C(object): pass class CC: pass >>> type(C) <type 'type'> >>> >>> type(CC) <type 'classobj'> >>> >>> import types >>> type(CC) is types.ClassType True When Are Metaclasses Used? 什么时候使用元类? Metaclasses are always used when creating classes. When executing a class definition, the interpreter has to know the correct metaclass to use. It will look for a class attribute named __metaclass__ first, and if it is there, it will use the class that is assigned to that attribute as the metaclass. If that attribute has not been defined, it will go up and search an ancestor class for __metaclass__. All new-style classes must inherit from object or type if there are no other base classes (type (object) is type anyway). 元类一般用于创建类。在执行类定义时,解释器必须要知道这个类的正确的元类。解释器会先寻找类属性__metaclass__,如果此属性存在,就将这个属性赋值给此类作为它的元类。如果此属性没有定义,它会向上查找父类中的__metaclass__. 所有新风格的类如果没有任何父类,会从对象或类型中继承。(type (object) 当然是类型). If that is not found, it checks for a global variable named __metaclass__ and uses it if it exists. Otherwise, the class is a classic class, and types.ClassType is used as the metaclass. (Note you can do some trickery here … if you define a classic class and set __metaclass__ = type, you have parlayed it into a new-style class!) 如果还没有发现__metaclass__属性,解释器会检查名字为__metaclass__的全局变量,如果它存在,就使用它作为元类。否则, 这个类就是一个传统类,并用types.ClassType作为此类的元类。(注意:在这里你可以运用一些技巧... 如果你定义了一个传统类,并且设置它的__metaclass__ = type,其实你是在将它升级为一个新风格的类!) Any time a class declaration is executed, the correct (and usually default) metaclass is determined, and that metaclass (always) passes three arguments (to its constructor): the class name, the tuple of base classes to inherit from, and the (class) attribute dictionary. 在执行类定义的时候,将检查此类正确的(一般是默认的)元类,元类(通常)传递三个参数(到构造器):类名,从基类继承数据的元组,和(类的)属性字典。 Who Are Metaclass Users? 谁在用元类? To many, the subject of metaclasses belongs in the realm of the theoretical or pure object-oriented thinking and has no place in everyday programming. To some extent that is true; however, the most important thing to keep in mind is that the end consumers of metaclasses are programmers themselves, not application users. You can define metaclasses that “force” programmers to implement solution classes in specific ways, which can either simplify their work or make them program to a target specification. 元类这样的话题对大多数人来说属于理论化或纯面向对象思想的范畴,认为它在实际编程中没有什么实际意义。从某种意义上讲这种想法是正确的;但最重要的请铭记在心的是,元类的最终使用者不是用户,正是程序员自己。你通过定义一个元类来“迫使”程序员按照某种方式实现目标类,这将既可以简化他们的工作,也可以使所编写的程序更符合特定标准。 When Are Metaclasses Created? 元类何时被创建? Metaclasses are created for the situations described just above, when you want to change the default behavior of how classes can and are created. Most Python users will not be creating or explicitly using metaclasses. The standard behavior in creating new-style or classic classes is to just take the default behavior by using the system-supplied metaclasses. 前面我们已提到创建的元类用于改变类的默认行为和创建方式。大多数Python用户都无须创建或明确地使用元类。创建一个新风格的类或传统类的通用做法是使用系统自己所提供的元类的默认方式。  In most cases, users will not even be aware that metaclasses are providing the templated default behavior of class creation (or metaclass instantiation). Although metaclasses will not be created on a regular basis, let us take a look at a simple example below. (More examples can be found in the documents listed at the end of this subsection.) 用户一般都不会觉察到元类所提供的创建类(或元类实例化)的默认模板方式。虽然一般我们并不创建元类,还是让我们来看下面一个简单的例子。(关于更多这方面的示例请参见本节末尾的文档列表。) Metaclass Example 1 元类示例1 The first example of metaclasses we are presenting here is (hopefully) very simple. It does not do anything at all except timestamp when a class is created using the metaclass. (As you know now, it happens when the class is created.) 我们第一个关于元类的示例非常简单(希望如此)。它只是在用元类创建一个类时,显示时间标签。(你现在该知道,这发生在类被创建的时候。)  Take a look at the following script. It contains print statements scattered throughout so that you can track the events as they occur: 看下面这个脚本。它包含的print语句散落在代码各个地方,便于我们了解所发生的事情: #!/usr/bin/env python from time import ctime print '*** Welcome to Metaclasses!' print '\tMetaclass declaration first.' class MetaC(type): def __init__(cls, name, bases, attrd): super(MetaC, cls).__init__(name, bases, attrd) print '*** Created class %r at: %s' % (name, ctime()) print '\tClass "Foo" declaration next.' class Foo(object): __metaclass__ = MetaC def __init__(self): print '*** Instantiated class %r at: %s' % ( self.__class__.__name__, ctime()) print '\tClass "Foo" instantiation next.' f = Foo() print '\tDONE' If we run this script, we get the following output: 当我们执行此脚本时,将得到以下输出: *** Welcome to Metaclasses! Metaclass declaration first. Class "Foo" declaration next. *** Created class 'Foo' at: Tue May 16 14:25:53 2006 Class "Foo" instantiation next. *** Instantiated class 'Foo' at: Tue May 16 14:25:53 2006 DONE  Once you are comfortable with the fact that a class declaration actually causes some work to be done, then you are well under way. 当你明白了一个类的定义其实是在完成某些工作的事实以后,你就容易理解这是怎么一回事情了。 Metaclass Example 2 元类示例2 In this second example, we are going to create a metaclass that forces programmers to supply a __str__() method in their classes so that their users can see something more useful than the generic Python object string () we saw earlier in this chapter. 在第二个示例中,我们将创建一个元类,要求程序员在他们写的类中提供一个__str__()方法的实现,这样用户就可以看到比我们在本章前面所见到的一般Python对象字符串()更有用的信息。  Our metaclass will also (strongly) suggest users override __repr__() if they have not done that either, but it is only a warning. Not implementing __str__() will result in a TypeError exception being thrown, forcing users to create a special method with that name. Here is the code for the metaclass: 如果您还没有在类中覆盖__repr__()方法,元类会(强烈)提示您这么做,但这只是个警告。如果未实现__str__()方法,将引发一个TypeError的异常,要求用户编写一个同名方法。以下是关于元类的代码: from warnings import warn class ReqStrSugRepr(type): def __init__(cls, name, bases, attrd): super(ReqStrSugRepr, cls).__init__( name, bases, attrd) if '__str__' not in attrd: raise TypeError("Class requires overriding of __str__()") if '__repr__' not in attrd: warn('Class suggests overriding of __repr__()\n', stacklevel=3)  We will create three example classes that use our metaclass, one that over- rides both __str__() and __repr__() special methods (Foo), one that only implements the __str__() special method (Bar), and one that implements neither (FooBar), an error situation. The full application is presented here as Example 13.10. 我们编写了三个关于元类的示例,其中一个(Foo)重载了特殊方法__str__()和__repr__(),另一个(Bar)只实现了特殊方法__str__(),还有一个(FooBar)没有实现__str__()和 __repr__(),这种情况是错误的。完整的程序见示例13.10. Running this script, we get the following output: 执行此脚本,我们得到如下输出: $ python meta.py *** Defined ReqStrSugRepr (meta)class *** Defined Foo class sys:1: UserWarning: Class suggests overriding of __repr__() *** Defined Bar class Traceback (most recent call last): File "meta.py", line 43, in ? class FooBar(object): File "meta.py", line 12, in __init__ raise TypeError( TypeError: Class requires overriding of __str__() Example 13.10 Metaclass Example (meta.py) 示例13.10 元类示例 (meta.py) This module features a metaclass and three classes under the jurisdiction of the metaclass. After each class is created, you will see a print statement. 这个模块有一个元类和三个受此元类限定的类。每创建一个类,将打印一条输出语句。 1 #!/usr/bin/env python 2 3 from warnings import warn 4 5 class ReqStrSugRepr(type): 6 7 def __init__(cls, name, bases, attrd): 8 super(ReqStrSugRepr, cls).__init__( 9 name, bases, attrd) 10 11 if '__str__' not in attrd: 12 raise TypeError( 13 "Class requires overriding of __str__()") 14 15 if '__repr__' not in attrd: 16 warn( 17 'Class suggests overriding of __repr__()\n', 18 stacklevel=3) 19 20 print '*** Defined ReqStrSugRepr (meta)class\n' 21 22 class Foo(object): 23 __metaclass__ = ReqStrSugRepr 24 25 def __str__(self): 26 return 'Instance of class:', \ 27 self.__class__.__name__ 28 29 def __repr__(self): 30 return self.__class__.__name__ 31 32 print '*** Defined Foo class\n' 33 34 class Bar(object): 35 __metaclass__ = ReqStrSugRepr 36 37 def __str__(self): 38 return 'Instance of class:', \ 39 self.__class__.__name__ 40 41 print '*** Defined Bar class\n' 42 43 class FooBar(object): 44 __metaclass__ = ReqStrSugRepr 45 46 print '*** Defined FooBar class\n'  Note how we got past declaring Foo without incident. With Bar, we received the warning for not implementing __repr__(), and FooBar did not pass the security check, hence the reason why the application failed to make it to the (final) print statement. Another important thing to note is that we did not even create any instances of the test classes… they are not even part of our picture. However, keep in mind that those classes them- selves are instances of our metaclass. This is but one example of the power of metaclasses. 注意我们是如何成功声明Foo定义的;定义Bar时,提示警告__repr__()未实现;FooBar的创建没有通过安全检查,以致程序最后没有打印出关于FooBar的语句。另外要注意的是我们并没有创建任何测试类的实例... 这些甚至根本不包括在我们的设计中。但别忘了这些类本身就是我们自己的元类的实例。这个示例只显示了元类强大功能的一方面。  There are many more examples online and in the Python documentation, PEPs 252 and 253, the What’s New in Python 2.2 document, and Guido van Rossum’s essay entitled, “Unifying Types and Classes in Python 2.2.” You can find a link to that document from the main Python release page for 2.2.3. 关于元类的在线文档众多,包括Python文档PEPs 252和253,《What’s New in Python 2.2》文档,Guido van Rossum所写的名为“Unifying Types and Classes in Python 2.2”的文章。在Python 2.2.3发布的主页上你也可以找到相关文档的链接地址。 13.17 Related Modules and Documentation 13.17 相关模块和文档 Python has several classic classes that extend the existing functionality of the core language that we have described in this chapter. These classes were provided as a convenience before it was possible to subclass Python data types. 我们在本章已经对核心语言做了讲述,而Python语言中有几个扩展了核心语言功能的经典类。这些类为Python数据类型的子类化提供了方便。  The User* modules are like precooked meals, ready to eat. We mentioned how classes have special methods that, if implemented, can customize classes so that when wrapped around a standard type, they can give instances type-like qualities. User*模块好比速食品,方便即食。我们曾提到类可以有特殊的方法,如果实现了这些特殊方法,就可以对类进行定制,这样当对一个标准类型封装时,可以给实例带来和类型一样的使用效果。 UserList and UserDict, along with the new UserString (introduced in Python 1.6), represent modules that define classes that act as wrappers around list, dictionary, and string objects, respectively. The primary objective of these modules is to provide the desired functionality for you so that you do not have to implement them yourself, and to serve as base classes that are appropriate for subclassing and further customization. Python already provides an abundance of useful built-in types, but the added ability to perform “build it yourself” typing makes it an even more powerful language. UserList和UserDict,还有新的UserString(从Python1.6版本开始引入)分别代表对列表、字典、字符串对象进行封装的类定义模块。这些模块的主要用处是提供给用户所需要的功能,这样你就不必自己动手去实现它们了,同时还可以作为基类,提供子类化和进一步定制的功能。Python语言已经为我们提供了大量有用的内建类型,但这种"由你自己定制"类型的附加功能使得Python语言更加强大。  In Chapter 4, we introduced Python’s standard as well as other built-in types. The types module is a great place to learn more about Python’s types as well as those that are beyond the scope of this text. The types module also defines type objects that can be used to make comparisons. (Such comparisons are popular in Python because they do not support method overloading―this keeps the language simple, yet there are tools that add functionality to a part of the language where it had appeared to be lacking.) 在第四章里,我们介绍了Python语言的标准类型和其它内建类型。types模块是进一步学习Python类型方面知识的好地方,其中的一些内容已超出了本书的讨论范围。types模块还定义了一些可以用于进行比较操作的类型对象。(这种比较操作在Python中很常见,因为它不支持方法的重载 - 这简化的语言本身,同时又提供了一些工具,为貌似欠缺的地方添加功能.)  The following piece of code checks to see if the object data is passed into the foo function as an integer or string, and does not allow any other type(raises an exception): 下面的代码检查传递到foo函数的数据对象是否是一个整数或一个字符串,不允许其他类型出现(否则会引发一个异常): def foo(data): if isinstance(data, int): print 'you entered an integer' elif isinstance(data, str): print 'you entered a string' else: raise TypeError, 'only integers or strings!'  The last related module is the operator module. This module provides functional versions of most of Python’s standard operators. There may be occasions where this type of interface proves more versatile than hard-coding use of the standard operators. 最后一个相关模块是operator模块。这个模块提供了Python中大多数标准操作符的函数版本。在某些情况下,这种接口类型比标准操作符的硬编码方式更通用。  Given below is one example. As you look through the code, imagine the extra lines of code that would have been required if individual operators had been part of the implementation: 请看下边的示例。在你阅读代码时,请设想一下如果此实现中使用的是一个个操作符的话,那会多写多少行代码啊? >>> from operator import * # import all operators >>> vec1 = [12, 24] >>> vec2 = [2, 3, 4] >>> opvec = (add, sub, mul, div) # using +, -, *, / >>> for eachOp in opvec: # loop thru operators ... for i in vec1: ... for j in vec2: ... print '%s(%d, %d) = %d' % \ ... (eachOp.__name__, i, j, eachOp(i, j)) ... add(12, 2) = 14 add(12, 3) = 15 add(12, 4) = 16 add(24, 2) = 26 add(24, 3) = 27 add(24, 4) = 28 sub(12, 2) = 10 sub(12, 3) = 9 sub(12, 4) = 8 sub(24, 2) = 22 sub(24, 3) = 21 sub(24, 4) = 20 mul(12, 2) = 24 mul(12, 3) = 36 mul(12, 4) = 48 mul(24, 2) = 48 mul(24, 3) = 72 mul(24, 4) = 96 div(12, 2) = 6 div(12, 3) = 4 div(12, 4) = 3 div(24, 2) = 12 div(24, 3) = 8 div(24, 4) = 6  The code snippet above defines three vectors, two containing operands and the last representing the set of operations the programmer wants to perform on each pair of available operands. The outermost loop iterates through each operation while the inner pair of loops creates every possible combination of ordered pairs from elements of each operand vector. Finally, the print statement simply applies the current operator with the given arguments. 上面这段代码定义了三个向量,前两个包含着操作数,最后一个代表程序员打算对两个操作数进行的一系列操作。最外层循环遍历每个操作运算,而最内层的两个循环用每个操作数向量中的元素组成各种可能的有序数据对。最后,print语句打印出将当前操作符应用在给定参数上所得的运算结果。 A list of the modules we described above is given in Table 13.5. 我们前面介绍过的模块都列在表13.5中 Table 13.5 Class Related Modules 表13.5 与类相关的模块 Module Description 模块 说明 UserList Provides a class wrapper around list objects UserList 提供一个列表对象的封装类 UserDict Provides a class wrapper around dictionary objects UserDict 提供一个字典对象的封装类 UserString a Provides a class wrapper around string objects; also included is a MutableString subclass, which provides that kind of functionality, if so desired UserString 提供一个字符串对象的封装类;它又包括一个MutableString子类,如果有需要,可以提供有关功能 types Defines names for all Python object types as used by the standard Python interpreter types 定义所有Python对象的类型在标准Python解释器中的名字 operator Functional interface to the standard operators operator 标准操作符的函数接口 a. New in Python 1.6. a. 新出现于Python 1.6版本  There are plenty of class, and object-oriented, programming-related questions in the Python FAQ. It makes excellent supplementary material to the Python Library and Language Reference manual. For new-style classes, see PEPs 252 and 253, and the related documents from the Python 2.2 release. 在Python FAQ中,有许多与类和面向对象编程有关的问题。它对Python类库以及语言参考手册都是很好的补充材料。关于新风格的类,请参考PEPs252和253和Python2.2以后的相关文档。 13.18 Exercises 练习 13-1. Programming. Name some benefits of object-oriented programming over older forms of programming. 13-1. 程序设计。请列举一些面向对象编程与传统旧的程序设计形式相比的先进之处。 13-2. Functions versus Methods. What are the differences between functions and methods? 13-2. 函数和方法的比较。函数和方法之间的区别是什么? 13-3. Customizing Classes. Create a class to format floating point values to monetary amounts. In this exercise, we will use United States currency, but feel free to implement your own. Preliminary work: Create a function called dollarize() which takes a floating point value and returns that value as a string properly formatted with symbols and rounded to obtain a financial amount. For example: dollarize(1234567.8901) ==> ‘$1,234,567.89. The dollarize() function should allow for commas, such as 1,000,000, and dollar signs. Any negative sign should appear to the left of the dollar sign. Once you have completed this task, then you are ready to convert it into a useful class called MoneyFmt. The MoneyFmt class contains a single data value, the monetary amount, and has five methods (feel free to create your own outside of this exercise). The __init__() constructor method initializes the data value, the update() method replaces the data value with a new one, the __nonzero__() method is Boolean, returning True if the data value is non-zero, the __repr__() method returns the amount as a float, and the __str__() method displays the value in the string-formatted manner that dollarize() does. (a) Fill in the code to the update() method so that it will update the data value. (b) Use the work you completed for dollarize() to fill in the code for the __str__() method. (c) Fix the bug in the __nonzero__() method, which currently thinks that any value less than one, i.e., fifty cents ($0.50), has a false value. (d) Extra credit: Allow the user to optionally specify an argument indicating the desire to see less-than and greater-than pairs for negative values rather than the negative sign. The default argument should use the standard negative sign. Example 13.11 Money Formatter (moneyfmt.py) 示例13.11 金额转换程序 (moneyfmt.py) String format class designed to “wrap” floating point values to appear as monetary amounts with the appropriate symbols. 字符串格式类用来对浮点数值进行"打包",使这个数值显示为带有正确符号的金额。 1 #!/usr/bin/env python 2 3 class MoneyFmt(object): 4 def __init__(self, value=0.0): # constructor #构造器 5 self.value = float(value) 6 7 def update(self, value=None): # allow updates #允许修改 8 ### 9 ### (a) complete this function 10 ### 11 12 def __repr__(self): # display as a float #显示为浮点数 13 return 'self.value' 14 15 def __str__(self): # formatted display #格式化显示 16 val = '' 17 18 ### 19 ### (b) complete this function... do NOT 20 ### forget about negative numbers!! 21 ### 22 23 return val 24 25 def __nonzero__(self): # boolean test 26 ### 27 ### (c) find and fix the bug 28 ### 29 30 return int(self.value) You will find the code skeleton for moneyfmt.py presented as Example 13.11. You will find a fully documented (yet incomplete) version of moneyfmt.py on the Web site. If we were to import the completed class within the interpreter, execution should behave similar to the following: >>> import moneyfmt >>> >>> cash = moneyfmt.MoneyFmt(123.45) >>> cash 123.45 >>> print cash $123.45 >>> >>> cash.update(100000.4567) >>> cash 100000.4567 >>> print cash $100,000.46 >>> >>> cash.update(-0.3) >>> cash -0.3 >>> print cash -$0.30 >>> repr(cash) '-0.3' >>> 'cash' '-0.3' >>> str(cash) '-$0.30' 13-3. 对类进行定制。写一个类,用来将浮点数值转换为金额。在本练习里,我们使用美国货币,但读者也可以自选任意货币。 基本任务: 编写一个dollarize()函数,它以一个浮点数值作为输入,返回一个字符串形式的金额数。比如说: dollarize(1234567.8901) ==> ‘$1,234,567.89. dollarize()返回的金额数里应该允许有逗号(比如1,000,000),和美元的货币符号。如果有负号,它必须出现在美元符号的左边。完成这项工作后,你就可以把它转换成一个有用的类,名为MoneyFmt。 MoneyFmt类里只有一个数据值(即,金额),和五个方法(你可以随意编写其他方法)。__init__()构造器对数据进行初始化,update()方法把数据值替换成一个新值,__nonzero__()是布尔型的,当数据值非零时返回True,__repr__()方法以浮点数的形式返回金额;而__str__()方法采用和dollarize()一样的字符格式显示该值。 (a) 编写update()方法,以实现数据值的修改功能。 (b) 以你已经编写的 dollarize()的代码为基础,编写__str__()方法的代码 (c) 纠正__nonzero__()方法中的错误,这个错误认为所有小于1的数值,例如,50美分($0.50),返回假值(False)。 (d) 附加题: 允许用户通过一个可选参数指定是把负数数值显示在一对尖括号里还是显示一个负号。默认参数是使用标准的负号。 13-4. User Registration. Create a user database (login, password, and last login timestamp) class (see problems 7-5 and 9-12) that manages a system requiring users to log in before access to resources is allowed. This database class manages its users, loading any previously saved user information on instantiation and providing accessor functions to add or update database information. If updated, the database will save the new information to disk as part of its deallocation (see __del__()). 13-4. 用户注册。建立一个用户数据库(包括登录名、密码和上次登录时间戳)类(参考练习7-5和9-12),来管理一个系统,该系统要求用户在登录后才能访问某些资源。这个数据库类对用户进行管理,并在实例化操作时加载之前保存的用户信息,提供访问函数来添加或更新数据库的信息。在数据修改后,数据库会在垃圾回收时将新信息保存到磁盘。(参见__del__()). 13-5. Geometry. Create a Point class that consists of an ordered pair (x, y) representing a point’s location on the X and Y axes. X and Y coordinates are passed to the constructor on instantiation and default to the origin for any missing coordinate. 13-5. 几何. 创建一个由有序数值对(x, y) 组成的Point类,它代表某个点的X坐标和Y坐标。X坐标和Y坐标在实例化时被传递给构造器,如果没有给出它们的值,则默认为坐标的原点。 13-6. Geometry. Create a line/line segment class that has length and slope behaviors in addition to the main data attributes: a pair of points (see previous problem). You should override the __repr__() method (and __str__(), if you want) so that the string representing a line (or line segment) is a pair of tuples, ((x1, y1), (x2, y2)). Summary: __repr__ Display points as pair of tuples length Return length of line segment―do not use “len” since that is supposed to be an integer slope Return slope of line segment (or None if applicable) 13-6. 几何. 创建一个直线/直线段类。除主要的数据属性:一对坐标值(参见上一个练习)外,它还具有长度和斜线属性。你需要覆盖__repr__()方法(如果需要的话,还有__str__()方法),使得代表那条直线(或直线段)的字符串表示形式是由一对元组构成的元组,即,((x1, y1), (x2, y2)). 总结: __repr__ 将直线的两个端点(始点和止点)显示成一对元组 length 返回直线段的长度 - 不要使用"len", 因为这样使人误解它是整数。 slope 返回此直线段的斜率(或在适当的时候返回None) 13-7. Date Class. Provide an interface to a time module where users can request dates in a few (given) date formats such as “MM/DD/YY,” “MM/DD/YYYY,” “DD/MM/YY,” “DD/MM/ YYYY,” “Mon DD, YYYY,” or the standard Unix date of “Day Mon DD, HH:MM:SS YYYY.” Your class should maintain a single value of date and create an instance with the given time. If not given, default to the current time at execution. Additional methods: update() changes the data value to reflect time given or current time as a default display() takes format indicator and displays date in requested format: 'MDY' ==> MM/DD/YY 'MDYY' ==> MM/DD/YYYY 'DMY' ==> DD/MM/YY 'DMYY' ==> DD/MM/YYYY 'MODYY' ==> Mon DD, YYYY If no format is given, default to system/ctime() format. Extra Credit: Merge the use of this class into Exercise 6-15. 13-7. 数据类。提供一个time模块的接口,允许用户按照自己给定时间的格式,比如: “MM/DD/YY,” “MM/DD/YYYY,” “DD/MM/YY,” “DD/MM/ YYYY,” “Mon DD, YYYY,” 或是标准的Unix日期格式:“Day Mon DD, HH:MM:SS YYYY” 来查看日期。你的类应该维护一个日期值,并用给定的时间创建一个实例。如果没有给出时间值,程序执行时会默认采用当前的系统时间。还包括另外一些方法: update() 按给定时间或是默认的当前系统时间修改数据值 display() 以代表时间格式的字符串做参数,并按照给定时间的格式显示: 'MDY' ==> MM/DD/YY 'MDYY' ==> MM/DD/YYYY 'DMY' ==> DD/MM/YY 'DMYY' ==> DD/MM/YYYY 'MODYY' ==> Mon DD, YYYY 如果没有提供任何时间格式,默认使用系统时间或ctime()的格式。附加题: 把这个类和练习6-15结合起来。 13-8. Stack Class. A stack is a data structure with last-in-first-out (LIFO) characteristics. Think of a stack of cafeteria trays. The first one in the spring-loaded device is the last one out, and the last one in is the first one out. Your class will have the expected push() (add an item to the stack) and pop() (remove an item from the stack) methods. Add an isempty() Boolean method that returns True if the stack is empty and False otherwise, and a peek() method that returns the item at the top of the stack without popping it off.  Note that if you are using a list to implement your stacks, the pop() method is already available as of Python 1.5.2. Create your new class so that it contains code to detect if the pop() method is available. If so, call the built-in one; otherwise, it should execute your implementation of pop(). You should probably use a list object; if you do, do not worry about implementing any list functionality (i.e., slicing). Just make sure that your Stack class can perform both of the operations above correctly. You may subclass a real list object or come up with your own list-like object, as in Example 6.2. 13-8. 堆栈类。一个堆栈(Stack)是一种具有后进先出(last-in-first-out,LIFO)特性的数据结构。我们可以把它想象成一个餐盘架。最先放上去的盘子将是最后一个取下来的,而最后一个放上去的盘子是最先被取下来的。你的类中应该有push()方法(向堆栈中压入一个数据项)和pop()方法(从堆栈中移出一个数据项)。还有一个叫isempty()的布尔方法,如果堆栈是空的,返回布尔值1,否则返回0;一个名叫peek()的方法,取出堆栈顶部的数据项,但并不移除它。 注意,如果你使用一个列表来实现堆栈,那么pop()方法从Python1.5.2版本起已经存在了。那就在你编写的新类里,加上一段代码检查pop()方法是否已经存在。如果经检查pop()方法存在,就调用这个内建的方法;否则就执行你自己编写的pop()方法。你很可能要用到列表对象;如果用到它时,不需要担心实现列表的功能(例如,切片)。只要确保你写的堆栈类能够正确实现上面的两项功能就可以了。你可以用列表对象的子类或自己写个类似列表的对象,请参考示例6.2. 13-9. Queue Class. A queue is a data structure that has first-in-first-out (FIFO) characteristics. A queue is like a line where items are removed from the front and added to the rear. The class should support the following methods: enqueue() adds a new element to the end of a list dequeue() returns the first element and removes it from the list. See the previous problem and Example 6.3 for motivation. 13-9. 队列类。一个队列(queue)是一种具有先进先出(first-in-first-out,FIFO)特性的数据结构。一个队列就像是一行队伍,数据从前端被移除,从后端被加入。这个类必须支持下面几种方法: enqueue() 在列表的尾部加入一个新的元素 dequeue() 在列表的头部取出一个元素,返回它并且把它从列表中删除。 请参见上面的练习和示例6.3. 13-10. Stacks and Queues. Write a class which defines a data structure that can behave as both a queue (FIFO) or a stack (LIFO), somewhat similar in nature to arrays in PERL. There are four methods that should be implemented: 13-10. 堆栈和队列。编写一个类,定义一个能够同时具有堆栈(FIFO)和队列(LIFO)操作行为的数据结构。这个类和Perl语言中数组相像。需要实现四个方法: shift() returns the first element and removes it from the list, similar to the earlier dequeue() function unshift() “pushes” a new element to the front or head of the list push() adds a new element to the end of a list, similar to the enqueue() and push() methods from previous problems pop() returns the last element and removes it from the list; it works exactly the same way as pop() from before shift() 返回并删除列表中的第一个元素,类似于前面的dequeue()函数。 unshift() 在列表的头部"压入"一个新元素 push() 在列表的尾部加上一个新元素,类似于前面的enqueue()和push()方法。 pop() 返回并删除列表中的最后一个元素,与前面的pop()方法完全一样。 See also Exercises 13-8 and 13-9. 请参见练习13-8和13-9. 13-11. Electronic Commerce. You need to create the foundations of an e-commerce engine for a B2C (business-to-consumer) retailer. You need to have a class for a customer called User, a class for items in inventory called Item, and a shopping cart class called Cart. Items go in Carts, and Users can have multiple Carts. Also, multiple items can go into Carts, including more than one of any single item. 13-11. 电子商务。 你需要为一家B2C(商业到消费者)零售商编写一个基础的电子商务引擎。你需要写一个针对顾客的类User, 一个对应存货清单的类Item, 还有一个对应购物车的类叫Cart. 货物放到购物车里,顾客可以有多个购物车。同时购物车里可以有多个货物,包括多个同样的货物。 13-12. Chat Rooms. You have been pretty disappointed at the current quality of chat room applications and vow to create your own, start up a new Internet company, obtain venture capital funding, integrate advertisement into your chat program, quintuple revenues in a six-month period, go public, and retire. However, none of this will happen if you do not have a pretty cool chat application. 13-12 聊天室. 你对目前的聊天室程序感到非常失望,并决心要自己写一个,创建一家新的因特网公司,获得风险投资,把广告集成到你的聊天室程序中,争取在6个月的时间里让收入翻五倍,股票上市,然后退休。但是,如果你没有一个非常酷的聊天软件,这一切都不会发生。 There are three classes you will need: a Message class containing a message string and any additional information such as broadcast or single recipient, and a User class that contains all the information for a person entering your chat rooms. To really wow the VCs to get your start-up capital, you add a class Room that represents a more sophisticated chat system where users can create separate “rooms” within the chat area and invite others to join. Extra credit: Develop graphical user interface (GUI) applications for the users. 你需要三个类: 一个Message类,它包含一个消息字符串以及诸如广播、单方收件人等其他信息,一个User类, 包含了进入你聊天室的某个人的所有信息。为了从风险投资者那里拿到启动资金,你加了一个Room类,它体现了一个更加复杂的聊天系统,用户可以在聊天时创建单独的“聊天屋”,并邀请其他人加入。附加题: 请为用户开发一个图形化用户界面应用程序。 13-13. Stock Portfolio Class. For each company, your database tracks the name, ticker symbol, purchase date, purchase price, and number of shares. Methods include: add new symbol (new purchase), remove symbol (all shares sold), and YTD or Annual Return performance for any or all symbols given a current price (and date). See also Exercise 7-6. 13-13. 股票投资组合类.你的数据库中记录了每个公司的名字,股票代号,购买日期,购买价格和持股数量。需要编写的方法包括:添加新代号(新买的股票)、删除代号(所有卖出股票),根据当前价格(及日期)计算出的YTD或年回报率。请参见练习7-6。 13-14. DOS. Write a Unix interface shell for DOS machines. You present the user a command line where he or she can type in Unix commands, and you interpret them and output accordingly, i.e., the “ls” command calls “dir” to give a list of filenames in a directory, “more” uses the same command (paginating through a text file), “cat” calls “type,” “cp” calls “copy,” “mv” calls “ren,” and “rm” invokes “del,” etc. 13-14. DOS. 为DOS机器编写一个UNIX操作界面的shell。你向用户提供一个命令行,使得用户可以在那里输入Unix命令,你可以对这些命令进行解释,并返回相应的输出,例如:“ls”命令调用“dir”来显示一个目录中的文件列表,“more”调用同名命令(分页显示一个文件),“cat” 调用 “type,” “cp” 调用“copy,” “mv” 调用 “ren,” “rm” 调用 “del,” 等. 13-15. Delegation. In our final comments regarding the CapOpen class of Example 13.8 where we proved that our class wrote out the data successfully, we noted that we could use either CapOpen() or open() to read the file text. Why? Would anything change if we used one or the other? 13-15. 授权。示例13.8的执行结果表明我们的类CapOpen能成功完成数据的写入操作。在我们的最后评论中,提到可以使用CapOpen() 或 open()来读取文件中的文本。为什么呢?这两者使用起来有什么差异吗? 13-16. Delegation and Functional Programming. 13-16. 授权和函数编程。 (a) Implement a writelines() method for the CapOpen class of Example 13.8. Your new function will take a list of lines and write them out converted to uppercase, similar to the way the regular writelines() method differs from write(). Note that once you are done, writelines() is no longer “delegated” to the file object. (a) 请为示例13.8中的CapOpen类编写一个writelines()方法。这个新函数将可以一次读入多行文本,然后将文本数据转换成大写的形式,它与write()方法的区别和通常意思上的writelines()与write()方法之间的区别相似。注意:编写完这个方法后,writelines()将不再由文件对象"代理"。 (b) Add an argument to the writelines() method that determines whether a NEWLINE should be added to every line of the list. This argument should default to a value of False for no NEWLINEs. (b) 在writelines()方法中添加一个参数,用这个参数来指明是否需要为每行文本加上一个换行符。此参数的默认值是False,表示不加换行符。 13-17. Subclassing Numeric Types. Take your final moneyfmt.py script as seen in Example 13.3 as Example 13.8 and recast it so that it extends Python’s float type. Be sure all operations are supported, but that it is still immutable. 13-17. 数值类型子类化。在示例13.3中所看到的moneyfmt.py脚本基础上修改它,使得它可以扩展Python的浮点类型。请确保它支持所有操作,而且是不可变的。 13-18. Subclassing Sequence Types. Create a subclass similar to your solution of the previous problem to your user registration class as seen earlier in Exercise 13-4. Allow users to change their passwords, but do not allow them to reuse the same password within a 12-month period. Extra credit: Add support for determining “similar passwords” (any algorithm is fine) and do not even allow passwords similar to any used within the last 12 months. 13-18. 序列类型子类化。模仿前面练习13-4中的用户注册类的解决方案,编写一个子类。要求允许用户修改密码,但密码的有效期限是12个月,过期后不能重复使用。附加题:支持“相似密码”检测的功能(任何算法皆可),不允许用户使用与之前12个月期间所使用的密码相似的任何密码。 13-19. Subclassing Mapping Types. As speculated for the dictionary subclass in Section 13.11.3, what if the keys() method were (re)written as: 13-19. 映射类型子类化。假设在13.11.3节中字典的子类,若将keys()方法重写为: def keys(self): return sorted(self.keys()) (a) What happens when keys() is called for a method? (a) 当方法keys()被调用,结果如何? (b) Why is this, and what makes our original solution work? (b) 为什么会有这样的结果?如何使我们的原解决方案顺利工作? 13-20. Class Customization. Improve on the time60.py script as seen in Section 13.13.2, Example 13.3. 13-20. 类的定制。改进脚本time60.py,见13.13.2节,示例13.3. (a) Allow “empty” instantiation: If hours and minutes are not passed in, then default to zero hours and zero minutes. (a) 允许“空”实例化: 如果小时和分钟的值没有给出,默认为零小时、零分钟。 (b) Zero-fill values to two digits because the current formatting is undesirable. In the case below, displaying wed should output “12:05.” (b) 用零占位组成两位数的表示形式,因为当前的时间格式不符合要求。如下面的示例,wed应该输出为“12:05.” >>> wed = Time60(12, 5) >>> wed 12:5 (c) In addition to instantiating with hours (hr) and minutes (min), also support time entered as: 除了用hours (hr) 和minutes (min)进行初始化外,还支持以下时间输入格式: A tuple of hours and minutes (10, 30) 一个由小时和分钟组成的元组(10, 30) A dictionary of hours and minutes ({'hr': 10, 'min': 30}) 一个由小时和分钟组成的字典({'hr': 10, 'min': 30}) A string representing hours and minutes ("10:30") 一个代表小时和分钟的字符串("10:30") Extra Credit: Allow for improperly formatted strings like “12:5” as well. 附加题: 允许不恰当的时间字符串表示形式,如 “12:5”. (d) Do we need to implement __radd__()? Why or why not? If not, when would or should we override it? (d) 我们是否需要实现__radd__()方法? 为什么? 如果不必实现此方法,那我们什么时候可以或应该覆盖它? 13.18 Exercises 625 13.18 练习 625 (e) The implementation of __repr__() is flawed and misguided. We only overrode this function so that it displays nicely in the interactive interpreter without having to use the print statement. However, this breaks the charter that repr() should always give a (valid) string representation of an evaluatable Python expression. 12:05 is not a valid Python expression, but Time60('12:05') is. Make it so. __repr__()函数的实现是有缺陷而且被误导的。我们只是重载了此函数,这样我们可以省去使用print语句的麻烦,使它在解释器中很好的显示出来。但是,这个违背了一个原则:对于可估值的Python表达式,repr()总是应该给出一个(有效的)字符串表示形式。12:05本身不是一个合法的Python表达式,但Time60('12:05')是合法的。请实现它。 (f ) Add support for ***agesimal (base 60) operations. The output for the following example should be 19:15 not 18:75: (f) 添加六十进制(基数是60)的运算功能。下面示例中的输出应该是19:15,而不是18:75: >>> thu = Time60(10, 30) >>> fri = Time60(8, 45) >>> thu + fri 18:75 13-21. Decorators and Function Call Syntax. Toward the end of Section 13.16.4, we used a function decorator to turn x into a property object, but because decorators were not available until Python 2.4, we gave an alternate syntax for older releases: 13-21.装饰符和函数调用语法。第13.16.4节末尾,我们使用过一个装饰函数符把x转化成一个属性对象,但由于装饰符是Python2.4才有的新功能,我们给出了另一个适用于旧版本的语法: X = property (**x()). Exactly what happens when this assignment is executed, and why is it equivalent to using a decorator? 执行这个赋值语句时到底发生了什么呢?为什么它和使用装饰符语句是等价的?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP