By Brad Miller and David Ranum, Luther College
There is a wonderful collection of YouTube videos recorded by Gerry Jenkins to support all of the chapters in this text.
Gerry Jenkins在YouTube上录制了一组精彩的视频来支持本文的所有章节。
To review the ideas of computer science, programming, and problem-solving.
复习计算机科学、编程和解决问题的思想。
To understand abstraction and the role it plays in the problem-solving process.
理解抽象和它在解决问题的过程中所扮演的角色。
To understand and implement the notion of an abstract data type.
理解和实现抽象数据类型的概念。
To review the Python programming language.
回顾Python编程语言。
The way we think about programming has undergone many changes in the years since the first electronic computers required patch cables and switches to convey instructions from human to machine.
自从第一代电子计算机需要接线和开关来将人的指令传送到机器中,我们思考编程的方式在这些年里经历了许多变化。
As is the case with many aspects of society, changes in computing technology provide computer scientists with a growing number of tools and platforms on which to practice their craft.
正如社会的许多方面一样,计算机技术的变化为计算机科学家提供了越来越多的工具和平台,通过使用这些工具和在这些平台上他们可以实践自己的技术。
Advances such as faster processors, high-speed networks, and large memory capacities have created a spiral of complexity through which computer scientists must navigate.
更快的处理器、高速的网络和更大的存储容量等技术进步创造了一个复杂的螺旋,计算机科学家必须在其中穿行。
Throughout all of this rapid evolution, a number of basic principles have remained constant.
在计算机的整个快速发展过程中,一些基本原则始终不变。
The science of computing is concerned with using computers to solve problems.
计算科学与使用计算机解决有关问题。
You have no doubt spent considerable time learning the basics of problem-solving and hopefully feel confident in your ability to take a problem statement and develop a solution.
毫无疑问,你已经花了相当多的时间学习解决问题的基础知识,并希望对自己的问题陈述和开发解决方案的能力有信心。
You have also learned that writing computer programs is often hard.
你也知道编写计算机程序通常是很困难的。
The complexity of large problems and the corresponding complexity of the solutions can tend to overshadow the fundamental ideas related to the problem-solving process.
大问题的复杂性和解决办法的相应复杂性可能会掩盖与解决问题过程有关的基本思想。
This chapter emphasizes two important areas for the rest of the text.
这一章为正文的其余部分强调了两个重要方面。
First, it reviews the framework within which computer science and the study of algorithms and data structures must fit, in particular, the reasons why we need to study these topics and how understanding these topics helps us to become better problem solvers.
首先,它回顾了计算机科学和算法和数据结构的研究必须适应的框架,特别是,为什么我们需要学习这些主题,以及理解这些主题如何帮助我们来成为更好的问题解决者。
Second, we review the Python programming language.
其次,回顾Python编程语言。
Although we cannot provide a detailed, exhaustive reference, we will give examples and explanations for the basic constructs and ideas that will occur throughout the remaining chapters.
虽然我们不能提供一个详细的,详尽的参考,我们将给出例子和解释的基本结构和思想,将出现在其余章节。
Computer science is difficult to define.
计算机科学很难定义。
This is probably due to the unfortunate use of the word “computer” in the name.
这可能是由于名字中不恰当地使用了“computer”这个词。
As you are perhaps aware, computer science is not simply the study of computers.
正如你可能意识到的,计算机科学不仅仅是对计算机的研究。
Although computers play an important supporting role as a tool in the discipline, they are just that–tools.
虽然计算机作为一种工具在学科中起着重要的辅助作用,但它们只是工具而已。
Computer science is the study of problems, problem-solving, and the solutions that come out of the problem-solving process.
计算机科学是一门研究问题、解决问题以及解决问题过程的解决方法的科学。
Given a problem, a computer scientist’s goal is to develop an algorithm, a step-by-step list of instructions for solving any instance of the problem that might arise.
给定一个问题,计算机科学家的目标是开发一种算法,一种解决任何可能出现的问题实例的一步一步的指令列表。
Algorithms are finite processes that if followed will solve the problem.
算法是有限的过程,如果遵循它就能解决问题。
Algorithms are solutions.
算法就是解决方案。
Computer science can be thought of as the study of algorithms.
计算机科学可以被认为是对算法的研究。
However, we must be careful to include the fact that some problems may not have a solution.
然而,我们必须谨慎地考虑到这样一个事实,即有些问题可能没有解决办法。
Although proving this statement is beyond the scope of this text, the fact that some problems cannot be solved is important for those who study computer science.
虽然证明这一说法超出了本文的范围,但对于学习计算机科学的人来说,有些问题无法解决这一事实是重要的。
We can fully define computer science, then, by including both types of problems and stating that computer science is the study of solutions to problems as well as the study of problems with no solutions.
我们可以完全定义计算机科学,通过包括这两种类型的问题,并指出计算机科学是对问题的解决方案的研究,以及对没有解决方案的问题的研究。
It is also very common to include the word computable when describing problems and solutions.
在描述问题和解决方案时,包含可计算的这个词也是很常见的。
We say that a problem is computable if an algorithm exists for solving it.
我们说一个问题是可计算的,如果存在解决它的算法。
An alternative definition for computer science, then, is to say that computer science is the study of problems that are and that are not computable, the study of the existence and the nonexistence of algorithms.
那么,计算机科学的另一种定义是,计算机科学是对可计算和不可计算问题的研究,即对算法存在和不存在的研究。
In any case, you will note that the word “computer” did not come up at all.
无论如何,你会注意到“computer”这个词根本就没有出现。
Solutions are considered independent from the machine.
解决方案是独立于机器的。
Computer science, as it pertains to the problem-solving process itself, is also the study of abstraction.
计算机科学,由于它与解决问题的过程本身有关,也是一门抽象学。
Abstraction allows us to view the problem and solution in such a way as to separate the so-called logical and physical perspectives.
抽象允许我们以一种分离所谓的逻辑和物理视角的方式来看待问题和解决方案。
The basic idea is familiar to us in a common example.
在一个常见的例子中,基本思想是我们所熟悉的。
Consider the automobile that you may have driven to school or work today.
考虑一下你今天开去学校或工作的汽车。
As a driver, a user of the car, you have certain interactions that take place in order to utilize the car for its intended purpose.
作为一名司机,一名汽车的使用者,为了使用汽车达到预定的目的,你需要进行一定的交互。
You get in, insert the key, start the car, shift, brake, accelerate, and steer in order to drive.
你坐进车里,插入钥匙,发动汽车,换挡,刹车,加速,转向,以便驾驶。
From an abstraction point of view, we can say that you are seeing the logical perspective of the automobile.
从抽象的角度来看,我们可以说你看到的是汽车的逻辑视角。
You are using the functions provided by the car designers for the purpose of transporting you from one location to another.
你正在使用汽车设计师提供的功能,以便将你从一个地点运送到另一个地点。
These functions are sometimes also referred to as the interface.
这些函数有时也称为接口。
On the other hand, the mechanic who must repair your automobile takes a very different point of view.
另一方面,要修理你汽车的机修工却有着很是不同的看法。
She not only knows how to drive but must know all of the details necessary to carry out all the functions that we take for granted.
她不仅知道如何驾驶,而且必须知道执行我们认为理所当然的所有功能所必需的所有细节。
She needs to understand how the engine works, how the transmission shifts gears, how temperature is controlled, and so on.
她需要了解发动机是如何工作的,变速器是如何换挡的,温度是如何控制的,等等。
This is known as the physical perspective, the details that take place “under the hood.”
这就是所谓的物理视角,即发生在“引擎盖下”的细节。
The same thing happens when we use computers.
同样的事情也发生在我们使用电脑的时候。
Most people use computers to write documents, send and receive email, surf the web, play music, store images, and play games without any knowledge of the details that take place to allow those types of applications to work.
大多数人使用计算机编写文档、发送和接收电子邮件、浏览网页、播放音乐、存储图像和玩游戏,而不知道这些类型的应用程序工作时所发生的细节。
They view computers from a logical or user perspective.
他们从逻辑或用户的角度来看待计算机。
Computer scientists, programmers, technology support staff, and system administrators take a very different view of the computer.
计算机科学家、程序员、技术支持人员和系统管理员对计算机有不同的看法。
They must know the details of how operating systems work, how network protocols are configured, and how to code various scripts that control function.
他们必须知道操作系统是如何工作的细节,网络协议是如何配置的,以及如何编写控制功能的各种脚本。
They must be able to control the low-level details that a user simply assumes.
它们必须能够控制用户简单假设的底层细节。
The common point for both of these examples is that the user of the abstraction, sometimes also called the client, does not need to know the details as long as the user is aware of the way the interface works.
这两个例子的共同点是,抽象的用户,有时也称为客户端,只要用户知道接口的工作方式,就不需要知道细节。
This interface is the way we as users communicate with the underlying complexities of the implementation.
这个接口是我们作为用户与实现的底层复杂性进行通信的方式。
As another example of abstraction, consider the Python math module.
作为抽象的另一个例子,考虑Python数学模块。
Once we import the module, we can perform computations such as
导入模块后,就可以执行诸如此类的计算
>>> import math
>>> math.sqrt(16)
4.0
>>>
This is an example of procedural abstraction.
这是程序性抽象的一个例子。
We do not necessarily know how the square root is being calculated, but we know what the function is called and how to use it.
我们不一定知道如何计算平方根,但我们知道这个函数名以及如何使用它。
If we perform the import correctly, we can assume that the function will provide us with the correct results.
如果我们正确地执行导入,我们可以假定函数将为我们提供正确的结果。
We know that someone implemented a solution to the square root problem but we only need to know how to use it.
我们知道有人解决了平方根问题但我们只需要知道如何使用它。
This is sometimes referred to as a “black box” view of a process.
这有时被称为流程的“黑盒”视图。
We simply describe the interface: the name of the function, what is needed (the parameters), and what will be returned.
我们只是简单地描述接口:函数的名称、需要什么(参数)以及将返回什么。
The details are hidden inside (see Figure 1).
详细信息隐藏在内部(参见图1)。
Figure 1: Procedural Abstraction
图1:过程抽象
Programming is the process of taking an algorithm and encoding it into a notation, a programming language, so that it can be executed by a computer.
编程是将一种算法编码成一种符号,一种编程语言,这样它就可以被计算机执行的过程。
Although many programming languages and many different types of computers exist, the important first step is the need to have the solution.
尽管存在许多编程语言和许多不同类型的计算机,但重要的第一步是需要有解决方案。
Without an algorithm there can be no program.
没有算法就没有程序。
Computer science is not the study of programming.
计算机科学不是关于编程的研究。
Programming, however, is an important part of what a computer scientist does.
然而,编程是计算机科学家工作的一个重要部分。
Programming is often the way that we create a representation for our solutions.
编程通常是我们为解决方案创建表示的方式。
Therefore, this language representation and the process of creating it becomes a fundamental part of the discipline.
因此,这种语言表征及其创造过程成为该学科的一个基本组成部分。
Algorithms describe the solution to a problem in terms of the data needed to represent the problem instance and the set of steps necessary to produce the intended result.
算法根据表示问题实例所需的数据以及产生预期结果所需的步骤集来描述问题的解决方案。
Programming languages must provide a notational way to represent both the process and the data.
编程语言必须提供一种符号方式来表示过程和数据。
To this end, languages provide control constructs and data types.
为此,语言提供了控制结构和数据类型。
Control constructs allow algorithmic steps to be represented in a convenient yet unambiguous way.
控制结构允许以一种方便而明确的方式表示算法步骤。
At a minimum, algorithms require constructs that perform sequential processing, selection for decision-making, and iteration for repetitive control.
至少,算法需要执行顺序处理、决策选择和重复控制迭代的构造。
As long as the language provides these basic statements, it can be used for algorithm representation.
只要语言提供了这些基本语句,它就可以用于算法表示。
All data items in the computer are represented as strings of binary digits.
计算机中的所有数据项都用二进制数字串表示。
In order to give these strings meaning, we need to have data types.
为了赋予这些字符串意义,我们需要数据类型。
Data types provide an interpretation for this binary data so that we can think about the data in terms that make sense with respect to the problem being solved.
数据类型为这个二进制数据提供了一种解释,这样我们就可以根据要解决的问题来考虑数据。
These low-level, built-in data types (sometimes called the primitive data types) provide the building blocks for algorithm development.
这些低级的内置数据类型(有时称为原始数据类型)为算法开发提供了构建块。
For example, most programming languages provide a data type for integers.
例如,大多数编程语言都提供整数的数据类型。
Strings of binary digits in the computer’s memory can be interpreted as integers and given the typical meanings that we commonly associate with integers (e.g. 23, 654, and -19).
计算机内存中的二进制数字串可以被解释为整数,并给出我们通常与整数相联系的典型含义(例如23,654和-19)。
In addition, a data type also provides a description of the operations that the data items can participate in.
此外,数据类型还提供了数据项可以参与的操作的描述。
With integers, operations such as addition, subtraction, and multiplication are common.
对于整数,加法、减法和乘法等操作是常见的。
We have come to expect that numeric types of data can participate in these arithmetic operations.
我们期望数字类型的数据可以参与这些算术运算。
The difficulty that often arises for us is the fact that problems and their solutions are very complex.
我们经常遇到的困难是,问题及其解决办法是非常复杂的。
These simple, language-provided constructs and data types, although certainly sufficient to represent complex solutions, are typically at a disadvantage as we work through the problem-solving process.
这些简单的、由语言提供的结构和数据类型,虽然对于表示复杂的解决方案来说当然足够了,但在我们解决问题的过程中,它们通常处于不利地位。
We need ways to control this complexity and assist with the creation of solutions.
我们需要控制这种复杂性的方法,并协助创造解决方案。
To manage the complexity of problems and the problem-solving process, computer scientists use abstractions to allow them to focus on the “big picture” without getting lost in the details.
为了管理问题的复杂性和解决问题的过程,计算机科学家使用抽象使他们能够专注于“大局”而不迷失在细节中。
By creating models of the problem domain, we are able to utilize a better and more efficient problem-solving process.
通过创建问题域的模型,我们能够利用一个更好、更有效的问题解决过程。
These models allow us to describe the data that our algorithms will manipulate in a much more consistent way with respect to the problem itself.
这些模型允许我们描述我们的算法将以一种更一致的方式处理问题本身的数据。
Earlier, we referred to procedural abstraction as a process that hides the details of a particular function to allow the user or client to view it at a very high level.
在前面,我们将过程抽象称为一种过程,它隐藏了特定功能的细节,以允许用户或客户端在非常高的层次上查看它。
We now turn our attention to a similar idea, that of data abstraction.
现在我们将注意力转向一个类似的概念,即数据抽象。
An abstract data type, sometimes abbreviated ADT, is a logical description of how we view the data and the operations that are allowed without regard to how they will be implemented.
抽象数据类型,有时缩写为ADT,是一种逻辑描述,描述我们如何查看数据和允许的操作,而不考虑它们将如何实现。
This means that we are concerned only with what the data is representing and not with how it will eventually be constructed.
这意味着我们只关心数据表示的是什么,而不关心它最终将如何构造。
By providing this level of abstraction, we are creating an encapsulation around the data.
通过提供这种级别的抽象,我们正在围绕数据创建一个封装。
The idea is that by encapsulating the details of the implementation, we are hiding them from the user’s view.
其思想是,通过封装实现的细节,我们将它们隐藏在用户的视图之外。
This is called information hiding.
这被称为信息隐藏。
Figure 2 shows a picture of what an abstract data type is and how it operates.
图2显示了什么是抽象数据类型以及它是如何操作的。
The user interacts with the interface, using the operations that have been specified by the abstract data type.
用户使用抽象数据类型指定的操作与接口进行交互。
The abstract data type is the shell that the user interacts with.
抽象数据类型是用户与之交互的shell。
The implementation is hidden one level deeper.
实现隐藏在更深一层。
The user is not concerned with the details of the implementation.
用户并不关心实现的细节。
Figure 2: Abstract Data Type
图2:抽象数据类型
The implementation of an abstract data type, often referred to as a data structure, will require that we provide a physical view of the data using some collection of programming constructs and primitive data types.
抽象数据类型(通常称为数据结构)的实现需要我们使用一些编程结构和原始数据类型集合提供数据的物理视图。
As we discussed earlier, the separation of these two perspectives will allow us to define the complex data models for our problems without giving any indication as to the details of how the model will actually be built.
正如我们前面所讨论的,这两个透视图的分离将允许我们为我们的问题定义复杂的数据模型,而不提供任何关于模型将如何实际构建的细节的指示。
This provides an implementation-independent view of the data.
这提供了与实现无关的数据视图。
Since there will usually be many different ways to implement an abstract data type, this implementation independence allows the programmer to switch the details of the implementation without changing the way the user of the data interacts with it.
由于通常有许多不同的方法来实现抽象数据类型,这种实现独立性允许程序员在不改变数据用户与数据交互的方式的情况下切换实现的细节。
The user can remain focused on the problem-solving process.
用户可以专注于解决问题的过程。
Computer scientists learn by experience.
计算机科学家从经验中学习。
We learn by seeing others solve problems and by solving problems by ourselves.
我们通过看到别人解决问题和自己解决问题来学习。
Being exposed to different problem-solving techniques and seeing how different algorithms are designed helps us to take on the next challenging problem that we are given.
接触到不同的解决问题的技术,看看不同的算法是如何设计的,有助于我们处理下一个遇到的挑战问题。
By considering a number of different algorithms, we can begin to develop pattern recognition so that the next time a similar problem arises, we are better able to solve it.
通过考虑许多不同的算法,我们可以开始发展模式识别,以便下次出现类似的问题时,我们能够更好地解决它。
Algorithms are often quite different from one another.
算法之间往往有很大的不同。
Consider the example of sqrt seen earlier.
考虑前面看到的sqrt示例。
It is entirely possible that there are many different ways to implement the details to compute the square root function.
完全有可能有许多不同的方法来实现计算平方根函数的细节。
One algorithm may use many fewer resources than another.
一种算法可能比另一种算法使用更少的资源。
One algorithm might take 10 times as long to return the result as the other.
一种算法返回结果的时间可能是另一种算法的10倍。
We would like to have some way to compare these two solutions.
我们想用某种方法来比较这两个解决方案。
Even though they both work, one is perhaps “better” than the other.
即使这两种方法都有效,但其中一种可能比另一种“更好”。
We might suggest that one is more efficient or that one simply works faster or uses less memory.
我们可能会建议一种更高效的方式,或者一种工作更快或使用更少的内存的方式。
As we study algorithms, we can learn analysis techniques that allow us to compare and contrast solutions based solely on their own characteristics, not the characteristics of the program or computer used to implement them.
当我们学习算法时,我们可以学习分析技术,使我们能够仅根据解决方案本身的特征来进行比较和对比,而不是根据执行它们的程序或计算机的特征。
In the worst case scenario, we may have a problem that is intractable, meaning that there is no algorithm that can solve the problem in a realistic amount of time.
在最坏的情况下,我们可能会遇到一个棘手的问题,这意味着没有算法可以在现实的时间内解决这个问题。
It is important to be able to distinguish between those problems that have solutions, those that do not, and those where solutions exist but require too much time or other resources to work reasonably.
重要的是要能够区分那些有解决方案的问题,那些没有解决方案的问题,以及那些有解决方案但需要太多时间或其他资源才能合理工作的问题。
There will often be trade-offs that we will need to identify and decide upon.
经常会有我们需要识别和决定的权衡。
As computer scientists, in addition to our ability to solve problems, we will also need to know and understand solution evaluation techniques.
作为计算机科学家,除了我们解决问题的能力,我们还需要知道和理解解决方案评估技术。
In the end, there are often many ways to solve a problem.
最后,往往有许多方法可以解决一个问题。
Finding a solution and then deciding whether it is a good one are tasks that we will do over and over again.
找到一个解决方案,然后决定它是否是一个好方案,是我们将一遍又一遍地做的任务。
In this section, we will review the programming language Python and also provide some more detailed examples of the ideas from the previous section.
在本节中,我们将回顾编程语言Python,并提供一些更详细的例子来说明上一节的思想。
If you are new to Python or find that you need more information about any of the topics presented, we recommend that you consult a resource such as the Python Language Reference or a Python Tutorial.
如果您是Python新手,或者发现您需要更多关于本文主题的信息,我们建议您参考Python语言参考或Python教程等资源。
Our goal here is to reacquaint you with the language and also reinforce some of the concepts that will be central to later chapters.
我们在这里的目标是让你重新熟悉这门语言,并加强一些概念,这些概念将在后面的章节中成为核心。
Python is a modern, easy-to-learn, object-oriented programming language.
Python是一种现代的、易于学习的、面向对象的编程语言。
It has a powerful set of built-in data types and easy-to-use control constructs.
它有一组强大的内置数据类型和易于使用的控制结构。
Since Python is an interpreted language, it is most easily reviewed by simply looking at and describing interactive sessions.
由于Python是一种解释性语言,它最容易通过简单地查看和描述交互式会话来检查。
You should recall that the interpreter displays the familiar >>> prompt and then evaluates the Python construct that you provide.
您应该记得,解释器显示熟悉的>>>提示符,然后计算您提供的Python结构。
For example,
例如,
>>> print("Algorithms and Data Structures")
Algorithms and Data Structures
>>>
shows the prompt, the print function, the result, and the next prompt.
显示提示符、打印函数、结果和下一个提示符。
We stated above that Python supports the object-oriented programming paradigm.
我们在上面说过Python支持面向对象编程范式。
This means that Python considers data to be the focal point of the problem-solving process.
这意味着Python将数据视为解决问题过程的焦点。
In Python, as well as in any other object-oriented programming language, we define a class to be a description of what the data look like (the state) and what the data can do (the behavior).
在Python中,以及在任何其他面向对象编程语言中,我们定义类来描述数据的样子(状态)和数据可以做什么(行为)。
Classes are analogous to abstract data types because a user of a class only sees the state and behavior of a data item.
类类似于抽象数据类型,因为类的用户只能看到数据项的状态和行为。
Data items are called objects in the object-oriented paradigm.
数据项在面向对象范式中称为对象。
An object is an instance of a class.
对象是类的一个实例。
We will begin our review by considering the atomic data types.
我们将从考虑原子数据类型开始回顾。
Python has two main built-in numeric classes that implement the integer and floating point data types.
Python有两个主要的内置数值类,它们实现了整数和浮点数据类型。
These Python classes are called int and float.
这些Python类被称为int和float。
The standard arithmetic operations, +, -, *, /, and ** (exponentiation), can be used with parentheses forcing the order of operations away from normal operator precedence.
标准的算术运算,+、-、*、/和**(求幂),可以与括号一起使用,从而使操作的顺序偏离正常操作符的优先级。
Other very useful operations are the remainder (modulo) operator, %, and integer division, //.
其他非常有用的操作是余数(模)运算符%和整型除法//。
Note that when two integers are divided, the result is a floating point.
注意,当两个整数被除时,结果是一个浮点数。
The integer division operator returns the integer portion of the quotient by truncating any fractional part.
整型除法运算符通过截断任何小数部分返回商的整型部分。
示例如下:
print(2+3*4)
print((2+3)*4)
print(2**10)
print(6/3)
print(7/3)
print(7//3)
print(7%3)
print(3/6)
print(3//6)
print(3%6)
print(2**100)
#输出结果如下:
14
20
1024
2.0
2.333333333333333
2
1
0.5
0
3
1267650600228229401496703205376
The boolean data type, implemented as the Python bool class, will be quite useful for representing truth values.
作为Python bool类实现的布尔数据类型对于表示真值非常有用。
The possible state values for a boolean object are True and False with the standard boolean operators, and, or, and not.
对于标准的布尔操作符and、or和not,布尔对象的可能状态值分别为True和False。
>>> True
True
>>> False
False
>>> False or True
True
>>> not (False or True)
False
>>> True and True
True
Boolean data objects are also used as results for comparison operators such as equality (==) and greater than (>).
布尔数据对象也可用作比较运算符的结果,比如equal(==)和greater than(>)。
In addition, relational operators and logical operators can be combined together to form complex logical questions.
此外,关系运算符和逻辑运算符可以组合在一起形成复杂的逻辑问题。
Table 1 shows the relational and logical operators with examples shown in the session that follows.
表1展示了关系操作符和逻辑操作符,并在下面的会话中给出了示例。
Table 1: Relational and Logical Operators
表1:关系操作符和逻辑操作符
print(5==10)
print(10 > 5)
print((5 >= 1) and (5 <= 10))
#输出结果如下
False
True
True
Identifiers are used in programming languages as names.
标识符在编程语言中用作名称。
In Python, identifiers start with a letter or an underscore (_), are case sensitive, and can be of any length.
在Python中,标识符以字母或下划线(_)开头,区分大小写,长度可以任意。
Remember that it is always a good idea to use names that convey meaning so that your program code is easier to read and understand.
请记住,使用具有含义的名称总是一个好主意,这样您的程序代码更容易阅读和理解。
A Python variable is created when a name is used for the first time on the left-hand side of an assignment statement.
当名称第一次在赋值语句的左侧被使用时,将创建Python变量。
Assignment statements provide a way to associate a name with a value.
赋值语句提供了一种将名称与值相关联的方法。
The variable will hold a reference to a piece of data and not the data itself.
变量将保存对一段数据的引用,而不是数据本身。
Consider the following session:
>>> theSum = 0
>>> theSum
0
>>> theSum = theSum + 1
>>> theSum
1
>>> theSum = True
>>> theSum
True
The assignment statement theSum = 0 creates a variable called theSum and lets it hold the reference to the data object 0 (see Figure 3).
赋值语句theSum = 0创建了一个名为theSum的变量,并让它保存对数据对象0的引用(参见图3)。
In general, the right-hand side of the assignment statement is evaluated and a reference to the resulting data object is “assigned” to the name on the left-hand side.
通常,赋值语句的右手边被求值,对结果数据对象的引用被“赋值”给左手边的名称。
At this point in our example, the type of the variable is integer as that is the type of the data currently being referred to by theSum.
在我们的示例中,此时变量的类型是integer,因为它是当前由群集引用的数据的类型。
If the type of the data changes (see Figure 4), as shown above with the boolean value True, so does the type of the variable (theSum is now of the type boolean).
如果数据的类型发生了变化(参见图4),如上图所示,布尔值为True,那么变量的类型也会发生变化(theSum现在是布尔类型)。
The assignment statement changes the reference being held by the variable.
赋值语句改变变量持有的引用。
This is a dynamic characteristic of Python.
这是Python的一个动态特性。
The same variable can refer to many different types of data.
同一个变量可以引用许多不同类型的数据。
Figure 3: Variables Hold References to Data Objects
图3:变量包含对数据对象的引用
Figure 4: Assignment Changes the Reference
图4:赋值改变了引用
2020.12.23今天暂且就到这吧…
In addition to the numeric and boolean classes, Python has a number of very powerful built-in collection classes.
除了数值类和布尔类之外,Python还有许多非常强大的内置集合类。
Lists, strings, and tuples are ordered collections that are very similar in general structure but have specific differences that must be understood for them to be used properly.
列表、字符串和元组都是有序的集合,它们在一般结构上非常相似,但有特定的差异,必须加以理解才能正确使用它们。
Sets and dictionaries are unordered collections.
集合和字典都是无序集合。
A list is an ordered collection of zero or more references to Python data objects.
列表是对Python数据对象的零个或多个引用的有序集合。
Lists are written as comma-delimited values enclosed in square brackets.
列表被写成用逗号分隔的值,用方括号括起来。
The empty list is simply [ ].
空列表就是[]。
Lists are heterogeneous, meaning that the data objects need not all be from the same class and the collection can be assigned to a variable as below.
列表是异构的,这意味着数据对象不必都来自同一个类,集合可以分配给如下所示的变量。
The following fragment shows a variety of Python data objects in a list.
下面的片段在一个列表中显示了各种Python数据对象。
>>> [1,3,True,6.5]
[1, 3, True, 6.5]
>>> myList = [1,3,True,6.5]
>>> myList
[1, 3, True, 6.5]
Note that when Python evaluates a list, the list itself is returned.
请注意,当Python对列表求值时,会返回列表本身。
However, in order to remember the list for later processing, its reference needs to be assigned to a variable.
但是,为了记住这个列表以便以后处理,需要将它的引用赋值给一个变量。
Since lists are considered to be sequentially ordered, they support a number of operations that can be applied to any Python sequence.
由于列表被认为是顺序排列的,所以它们支持许多可应用于任何Python序列的操作。
Table 2 reviews these operations and the following session gives examples of their use.
表2回顾了这些操作,下面的会话给出了它们的使用示例。
Table 2: Operations on Any Sequence in Python
表2:Python中任意序列的操作
Note that the indices for lists (sequences) start counting with 0.
注意,列表(序列)的索引从0开始计数。
The slice operation, myList[1:3], returns a list of items starting with the item indexed by 1 up to but not including the item indexed by 3.
切片操作myList[1:3]返回一个条目列表,从1索引的条目开始,一直到3索引的条目,但不包括3索引的条目。
Sometimes, you will want to initialize a list.
有时,你会想要初始化一个列表。
This can quickly be accomplished by using repetition.
这可以通过重复来快速完成。
For example,
例如,
>>> myList = [0] * 6
>>> myList
[0, 0, 0, 0, 0, 0]
One very important aside relating to the repetition operator is that the result is a repetition of references to the data objects in the sequence.
与重复操作符有关的一个非常重要的方面是,结果是对序列中数据对象的引用的重复。
This can best be seen by considering the following session:
考虑以下情况最能看出这一点:
myList = [1,2,3,4]
A = [myList]*3
print(A)
myList[2]=45
print(A)
#运行结果如下:
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
[[1, 2, 45, 4], [1, 2, 45, 4], [1, 2, 45, 4]]
The variable A holds a collection of three references to the original list called myList.
变量A包含对原始列表myList的三个引用。
Note that a change to one element of myList shows up in all three occurrences in A.
注意,对myList中的一个元素的更改在a中出现了三次。
Lists support a number of methods that will be used to build data structures.
列表支持许多用于构建数据结构的方法。
Table 3 provides a summary.
表3提供了一个摘要。
Examples of their use follow.
下面是它们的用法示例。
Table 3: Methods Provided by Lists in Python
表3:Python中列表提供的方法
myList = [1024, 3, True, 6.5]
myList.append(False)
print(myList)
myList.insert(2,4.5)
print(myList)
print(myList.pop())
print(myList)
print(myList.pop(1))
print(myList)
myList.pop(2)
print(myList)
myList.sort()
print(myList)
myList.reverse()
print(myList)
print(myList.count(6.5))
print(myList.index(4.5))
myList.remove(6.5)
print(myList)
del myList[0]
print(myList)
#输出结果如下:
[1024, 3, True, 6.5, False]
[1024, 3, 4.5, True, 6.5, False]
False
[1024, 3, 4.5, True, 6.5]
3
[1024, 4.5, True, 6.5]
[1024, 4.5, 6.5]
[4.5, 6.5, 1024]
[1024, 6.5, 4.5]
1
2
[1024, 4.5]
[4.5]
You can see that some of the methods, such as pop, return a value and also modify the list.
您可以看到,有些方法(如pop)返回一个值并修改列表。
Others, such as reverse, simply modify the list with no return value.
其他方法,如reverse,只是简单地修改没有返回值的列表。
pop will default to the end of the list but can also remove and return a specific item.
pop将默认设置为列表的末尾,但也可以删除和返回特定的项。
The index range starting from 0 is again used for these methods.
从0开始的索引范围同样用于这些方法。
You should also notice the familiar “dot” notation for asking an object to invoke a method.
您还应该注意到用于请求对象调用方法的熟悉的“点”符号。
myList.append(False) can be read as “ask the object myList to perform its append method and send it the value False.”
myList.append(False)可以被理解为“请求对象myList执行它的append方法并将值发送给它False”。
Even simple data objects such as integers can invoke methods in this way.
即使是简单的数据对象,如整数,也可以以这种方式调用方法。
>>> (54).__add__(21)
75
>>>
In this fragment we are asking the integer object 54 to execute its add method (called add in Python) and passing it 21 as the value to add.
在这个片段中,我们要求整数对象54执行它的add方法(在Python中称为__add__),并将它传递给21作为要添加的值。
The result is the sum, 75.
结果是75。
Of course, we usually write this as 54+21.
当然,我们通常把它写成54+21。
We will say much more about these methods later in this section.
我们将在本节后面详细介绍这些方法。
One common Python function that is often discussed in conjunction with lists is the range function.
一个经常与列表一起讨论的常见Python函数是range函数。
range produces a range object that represents a sequence of values.
range生成一个表示一系列值的range对象。
By using the list function, it is possible to see the value of the range object as a list.
通过使用list函数,可以将range对象的值作为一个列表来查看。
This is illustrated below.
如下图所示。
>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(5,10)
range(5, 10)
>>> list(range(5,10))
[5, 6, 7, 8, 9]
>>> list(range(5,10,2))
[5, 7, 9]
>>> list(range(10,1,-1))
[10, 9, 8, 7, 6, 5, 4, 3, 2]
>>>
The range object represents a sequence of integers.
range对象表示一个整数序列。
By default, it will start with 0.
默认情况下,它将从0开始。
If you provide more parameters, it will start and end at particular points and can even skip items.
如果您提供更多的参数,它将在特定的点开始和结束,甚至可以跳过项目。
In our first example, range(10), the sequence starts with 0 and goes up to but does not include 10.
在我们的第一个示例range(10)中,序列从0开始,一直到10,但不包括10。
In our second example, range(5,10) starts at 5 and goes up to but not including 10.
在我们的第二个示例中,range(5,10)从5开始,一直到但不包括10。
range(5,10,2) performs similarly but skips by twos (again, 10 is not included).
range(5、10、2)的性能类似,但跳过2(同样,10不包括在内)。
Strings are sequential collections of zero or more letters, numbers and other symbols.
字符串是0个或多个字母、数字和其他符号的顺序集合。
We call these letters, numbers and other symbols characters.
我们称这些字母、数字和其他符号为字符。
Literal string values are differentiated from identifiers by using quotation marks (either single or double).
文字字符串值通过使用引号(单引号或双引号)区别于标识符。
>>> "David"
'David'
>>> myName = "David"
>>> myName[3]
'i'
>>> myName*2
'DavidDavid'
>>> len(myName)
5
>>>
Since strings are sequences, all of the sequence operations described above work as you would expect.
因为字符串是序列,所以上面描述的所有序列操作都可以按照您的预期工作。
In addition, strings have a number of methods, some of which are shown in Table 4.
此外,字符串有许多方法,其中一些如表4所示。
For example,
例如,
>>> myName
'David'
>>> myName.upper()
'DAVID'
>>> myName.center(10)
' David '
>>> myName.find('v')
2
>>> myName.split('v')
['Da', 'id']
Of these, split will be very useful for processing data.
其中,split对于处理数据非常有用。
split will take a string and return a list of strings using the split character as a division point.
split将获取一个字符串,并使用分隔字符作为分割点返回一个字符串列表。
In the example, v is the division point.
在这个例子中,v是分割点。
If no division is specified, the split method looks for whitespace characters such as tab, newline and space.
如果没有指定分隔符,split方法将查找空白字符,如制表符、换行符和空格。
Table 4: Methods Provided by Strings in Python
表4:Python中字符串提供的方法
A major difference between lists and strings is that lists can be modified while strings cannot.
列表和字符串的主要区别在于列表可以被修改,而字符串不能。
This is referred to as mutability.
这被称为易变性。
Lists are mutable; strings are immutable.
列表是可变的;字符串是不可变的。
For example, you can change an item in a list by using indexing and assignment.
例如,您可以通过使用索引和赋值来更改列表中的项。
With a string that change is not allowed.
字符串是不允许被更改的。
>>> myList
[1, 3, True, 6.5]
>>> myList[0]=2**10
>>> myList
[1024, 3, True, 6.5]
>>>
>>> myName
'David'
>>> myName[0]='X'
Traceback (most recent call last):
File "" , line 1, in -toplevel-
myName[0]='X'
TypeError: object doesn't support item assignment
>>>
Tuples are very similar to lists in that they are heterogeneous sequences of data.
元组与列表非常相似,因为它们是异构的数据序列。
The difference is that a tuple is immutable, like a string.
不同的是,元组是不可变的,就像字符串一样。
A tuple cannot be changed.
元组是不可被修改的。
Tuples are written as comma-delimited values enclosed in parentheses.
元组是用括号括起来的逗号分隔的值来写的。
As sequences, they can use any operation described above.
作为序列,它们可以使用上面描述的任何操作。
For example,
例如,
>>> myTuple = (2,True,4.96)
>>> myTuple
(2, True, 4.96)
>>> len(myTuple)
3
>>> myTuple[0]
2
>>> myTuple * 3
(2, True, 4.96, 2, True, 4.96, 2, True, 4.96)
>>> myTuple[0:2]
(2, True)
>>>
However, if you try to change an item in a tuple, you will get an error.
但是,如果试图更改元组中的项,则会出现错误。
Note that the error message provides location and reason for the problem.
注意,错误消息提供了问题的位置和原因。
>>> myTuple[1]=False
Traceback (most recent call last):
File "" , line 1, in -toplevel-
myTuple[1]=False
TypeError: object doesn't support item assignment
>>>
A set is an unordered collection of zero or more immutable Python data objects.
集合是零个或多个不可变Python数据对象的无序集合。
Sets do not allow duplicates and are written as comma-delimited values enclosed in curly braces.
集合不允许重复,并且以逗号分隔的值写在花括号中。
The empty set is represented by set().
空集合由set()表示。
Sets are heterogeneous, and the collection can be assigned to a variable as below.
集合是异构的,可以将集合赋给如下所示的变量。
>>> {
3,6,"cat",4.5,False}
{
False, 4.5, 3, 6, 'cat'}
>>> mySet = {
3,6,"cat",4.5,False}
>>> mySet
{
False, 4.5, 3, 6, 'cat'}
>>>
Even though sets are not considered to be sequential, they do support a few of the familiar operations presented earlier.
尽管集合不被认为是连续的,但它们确实支持前面介绍的一些熟悉的操作。
Table 5 reviews these operations and the following session gives examples of their use.
表5回顾了这些操作,下面的会话给出了它们的使用示例。
Table 5: Operations on a Set in Python
表5:Python中集合的操作
>>> mySet
{
False, 4.5, 3, 6, 'cat'}
>>> len(mySet)
5
>>> False in mySet
True
>>> "dog" in mySet
False
>>>
Sets support a number of methods that should be familiar to those who have worked with them in a mathematics setting.
集合支持许多方法,在数学设置中使用过集合的人应该熟悉这些方法。
Table 6 provides a summary.
表6提供了一个摘要。
Examples of their use follow.
下面是它们的用法示例。
Note that union, intersection, issubset, and difference all have operators that can be used as well.
注意,并、交、issubset和差都有可以使用的运算符。
Table 6: Methods Provided by Sets in Python
表6:Python中集合提供的方法
>>> mySet
{
False, 4.5, 3, 6, 'cat'}
>>> yourSet = {
99,3,100}
>>> mySet.union(yourSet)
{
False, 4.5, 3, 100, 6, 'cat', 99}
>>> mySet | yourSet
{
False, 4.5, 3, 100, 6, 'cat', 99}
>>> mySet.intersection(yourSet)
{
3}
>>> mySet & yourSet
{
3}
>>> mySet.difference(yourSet)
{
False, 4.5, 6, 'cat'}
>>> mySet - yourSet
{
False, 4.5, 6, 'cat'}
>>> {
3,100}.issubset(yourSet)
True
>>> {
3,100}<=yourSet
True
>>> mySet.add("house")
>>> mySet
{
False, 4.5, 3, 6, 'house', 'cat'}
>>> mySet.remove(4.5)
>>> mySet
{
False, 3, 6, 'house', 'cat'}
>>> mySet.pop()
False
>>> mySet
{
3, 6, 'house', 'cat'}
>>> mySet.clear()
>>> mySet
set()
>>>
Our final Python collection is an unordered structure called a dictionary.
我们最后的Python集合是一个称为字典的无序结构。
Dictionaries are collections of associated pairs of items where each pair consists of a key and a value.
字典是相互关联的项对的集合,其中每对项由一个键和一个值组成。
This key-value pair is typically written as key:value.
这个键-值对通常被写成key:value。
Dictionaries are written as comma-delimited key:value pairs enclosed in curly braces.
字典被写成逗号分隔的键:值对,用花括号括起来。
For example,
例如,
>>> capitals = {
'Iowa':'DesMoines','Wisconsin':'Madison'}
>>> capitals
{
'Wisconsin': 'Madison', 'Iowa': 'DesMoines'}
>>>
We can manipulate a dictionary by accessing a value via its key or by adding another key-value pair.
我们可以通过字典的键来访问一个值,或者通过添加另一个键-值对来操作字典。
The syntax for access looks much like a sequence access except that instead of using the index of the item we use the key value.
access的语法看起来很像序列访问,除了使用键值而不是项的索引之外。
To add a new value is similar.
添加新值的方法与此类似。
capitals = {
'Iowa':'DesMoines','Wisconsin':'Madison'}
print(capitals['Iowa'])
capitals['Utah']='SaltLakeCity'
print(capitals)
capitals['California']='Sacramento'
print(len(capitals))
for k in capitals:
print(capitals[k]," is the capital of ", k)
#输出结果如下:
DesMoines
{
'Iowa': 'DesMoines', 'Wisconsin': 'Madison', 'Utah': 'SaltLakeCity'}
4
DesMoines is the capital of Iowa
Madison is the capital of Wisconsin
SaltLakeCity is the capital of Utah
Sacramento is the capital of California
It is important to note that the dictionary is maintained in no particular order with respect to the keys.
需要注意的是,字典对键没有特定的顺序。
The first pair added (‘Utah’: ‘SaltLakeCity’) was placed first in the dictionary and the second pair added (‘California’: ‘Sacramento’) was placed last.
第一对被添加的词(犹他:盐湖城)在字典中排名第一,第二对被添加的词(加州:萨克拉门托)排名最后。
The placement of a key is dependent on the idea of “hashing,” which will be explained in more detail in Chapter 4.
键的位置取决于“哈希”的概念,这将在第4章中更详细地解释。
We also show the length function performing the same role as with previous collections.
我们还展示了与前面的集合执行相同任务的length函数。
Dictionaries have both methods and operators.
字典既有方法也有操作符。
Table 7 and Table 8 describe them, and the session shows them in action.
表7和表8描述了它们,会话展示了它们的实际操作。
The keys, values, and items methods all return objects that contain the values of interest.
keys、values和items方法都返回包含感兴趣的值的对象。
You can use the list function to convert them to lists.
您可以使用list函数将它们转换为列表。
You will also see that there are two variations on the get method.
您还将看到get方法有两种命令。
If the key is not present in the dictionary, get will return None.
如果该键在字典中不存在,get将返回None。
However, a second, optional parameter can specify a return value instead.
但是,第二个可选参数可以指定返回值。
Table 7: Operators Provided by Dictionaries in Python
表7:Python中字典提供的运算符
>>> phoneext={
'david':1410,'brad':1137}
>>> phoneext
{
'brad': 1137, 'david': 1410}
>>> phoneext.keys()
dict_keys(['brad', 'david'])
>>> list(phoneext.keys())
['brad', 'david']
>>> phoneext.values()
dict_values([1137, 1410])
>>> list(phoneext.values())
[1137, 1410]
>>> phoneext.items()
dict_items([('brad', 1137), ('david', 1410)])
>>> list(phoneext.items())
[('brad', 1137), ('david', 1410)]
>>> phoneext.get("kent")
>>> phoneext.get("kent","NO ENTRY")
'NO ENTRY'
>>>
Table 8: Methods Provided by Dictionaries in Python
表8:Python中字典提供的方法
We often have a need to interact with users, either to get data or to provide some sort of result.
我们经常需要与用户进行交互,要么获取数据,要么提供某种结果。
Most programs today use a dialog box as a way of asking the user to provide some type of input.
现在的大多数程序都使用对话框来请求用户提供某种类型的输入。
While Python does have a way to create dialog boxes, there is a much simpler function that we can use.
虽然Python有创建对话框的方法,但我们可以使用一个简单得多的函数。
Python provides us with a function that allows us to ask a user to enter some data and returns a reference to the data in the form of a string.
Python为我们提供了一个函数,它允许我们请求用户输入一些数据,并以字符串的形式返回对数据的引用。
The function is called input.
这个函数被称为input。
Python’s input function takes a single parameter that is a string.
Python的输入函数只接受一个字符串形参。
This string is often called the prompt because it contains some helpful text prompting the user to enter something.
这个字符串通常被称为提示符,因为它包含一些有用的文本,提示用户输入某些内容。
For example, you might call input as follows:
例如,你可以这样调用input:
aName = input('Please enter your name: ')
Now whatever the user types after the prompt will be stored in the aName variable.
现在,无论用户在提示符之后键入什么,都将存储在aName变量中。
Using the input function, we can easily write instructions that will prompt the user to enter data and then incorporate that data into further processing.
使用input函数,我们可以很容易地编写指令,提示用户输入数据,然后将数据合并到进一步的处理中。
For example, in the following two statements, the first asks the user for their name and the second prints the result of some simple processing based on the string that is provided.
例如,在下面的两个语句中,第一个请求用户提供他们的名字,第二个根据提供的字符串打印一些简单处理的结果。
aName = input("Please enter your name ")
print("Your name in all capitals is",aName.upper(),
"and has length", len(aName))
运行结果如下:
It is important to note that the value returned from the input function will be a string representing the exact characters that were entered after the prompt.
需要注意的是,input函数返回的值将是一个字符串,表示在提示符之后输入的确切字符。
If you want this string interpreted as another type, you must provide the type conversion explicitly.
如果希望将此字符串解释为另一种类型,则必须显式提供类型转换。
In the statements below, the string that is entered by the user is converted to a float so that it can be used in further arithmetic processing.
在下面的语句中,用户输入的字符串被转换为浮点数,以便在进一步的算术处理中使用它。
sradius = input("Please enter the radius of the circle ")
radius = float(sradius)
diameter = 2 * radius
We have already seen that the print function provides a very simple way to output values from a Python program.
我们已经看到print函数提供了一种从Python程序输出值的非常简单的方法。
print takes zero or more parameters and displays them using a single blank as the default separator.
print接受零个或多个参数,并使用单个空白作为默认分隔符显示它们。
It is possible to change the separator character by setting the sep argument.
可以通过设置sep参数来更改分隔符。
In addition, each print ends with a newline character by default.
另外,默认情况下,每个打印都以换行符结束。
This behavior can be changed by setting the end argument.
可以通过设置end参数来更改此行为。
These variations are shown in the following session:
这些变化将在下一节中展示:
>>> print("Hello")
Hello
>>> print("Hello","World")
Hello World
>>> print("Hello","World", sep="***")
Hello***World
>>> print("Hello","World", end="***")
Hello World***>>>
It is often useful to have more control over the look of your output.
对输出的外观有更多的控制通常是有用的。
Fortunately, Python provides us with an alternative called formatted strings.
幸运的是,Python为我们提供了一种称为格式化字符串的替代方法。
A formatted string is a template in which words or spaces that will remain constant are combined with placeholders for variables that will be inserted into the string.
格式化字符串是一个模板,其中保持不变的单词或空格与将插入到字符串中的变量的占位符组合在一起。
For example, the statement
例如,语句
print(aName, "is", age, "years old.")
contains the words is and years old, but the name and the age will change depending on the variable values at the time of execution.
包含单词is和years old,但名称和age将根据执行时的变量值而变化。
Using a formatted string, we write the previous statement as
使用格式化字符串,我们将前面的语句写成
print("%s is %d years old." % (aName, age))
This simple example illustrates a new string expression.
这个简单的例子演示了一个新的字符串表达式。
The % operator is a string operator called the format operator.
%操作符是一个名为格式操作符的字符串操作符。
The left side of the expression holds the template or format string, and the right side holds a collection of values that will be substituted into the format string.
表达式的左侧保存模板或格式字符串,右侧保存将被替换到格式字符串中的值集合。
Note that the number of values in the collection on the right side corresponds with the number of % characters in the format string.
注意,右边集合中的值的数量与格式字符串中的%字符的数量相对应。
Values are taken—in order, left to right—from the collection and inserted into the format string.
值按照从集合从左到右的顺序接受,并插入到格式字符串中。
Let’s look at both sides of this formatting expression in more detail.
让我们更详细地看看这个格式化表达式的两个方面。
The format string may contain one or more conversion specifications.
格式字符串可以包含一个或多个转换规范。
A conversion character tells the format operator what type of value is going to be inserted into that position in the string.
转换字符告诉格式操作符将把什么类型的值插入到字符串中的那个位置。
In the example above, the %s specifies a string, while the %d specifies an integer.
在上面的例子中,%s指定了一个字符串,而%d指定了一个整数。
Other possible type specifications include i, u, f, e, g, c, or %.
其他可能的类型规格包括i, u, f, e, g, c,或%。
Table 9 summarizes all of the various type specifications.
表9总结了所有的类型规范。
Table 9: String Formatting Conversion Characters
表9:字符串格式转换字符
In addition to the format character, you can also include a format modifier between the % and the format character.
除了格式字符之外,您还可以在%和格式字符之间包含一个格式修饰符。
Format modifiers may be used to left-justify or right-justifiy the value with a specified field width.
格式修饰符可用于用指定的字段宽度对值进行左对齐或右对齐。
Modifiers can also be used to specify the field width along with a number of digits after the decimal point.
修饰符还可以用来指定字段的宽度以及小数点后的数字。
Table 10 explains these format modifiers
表10解释了这些格式修饰符
Table 10: Additional formatting options
表10:其他格式化选项
The right side of the format operator is a collection of values that will be inserted into the format string.
格式操作符的右侧是将插入格式字符串的值集合。
The collection will be either a tuple or a dictionary.
集合将是元组或字典。
If the collection is a tuple, the values are inserted in order of position.
如果集合是一个元组,则按位置顺序插入值。
That is, the first element in the tuple corresponds to the first format character in the format string.
也就是说,元组中的第一个元素对应于格式字符串中的第一个格式字符。
If the collection is a dictionary, the values are inserted according to their keys.
如果集合是一个字典,则根据它们的键插入值。
In this case all format characters must use the (name) modifier to specify the name of the key.
在这种情况下,所有格式字符都必须使用(name)修饰符来指定键的名称。
>>> price = 24
>>> item = "banana"
>>> print("The %s costs %d cents"%(item,price))
The banana costs 24 cents
>>> print("The %+10s costs %5.2f cents"%(item,price))
The banana costs 24.00 cents
>>> print("The %+10s costs %10.2f cents"%(item,price))
The banana costs 24.00 cents
>>> itemdict = {
"item":"banana","cost":24}
>>> print("The %(item)s costs %(cost)7.1f cents"%itemdict)
The banana costs 24.0 cents
>>>
In addition to format strings that use format characters and format modifiers, Python strings also include a format method that can be used in conjunction with a new Formatter class to implement complex string formatting.
除了使用格式字符和格式修饰符的格式字符串之外,Python字符串还包括一个格式方法,可以与新的Formatter类一起使用,以实现复杂的字符串格式化。
More about these features can be found in the Python library reference manual.
关于这些特性的更多信息可以在Python库参考手册中找到。
2020.12.24今天暂且就到这吧…
As we noted earlier, algorithms require two important control structures: iteration and selection.
正如我们前面提到的,算法需要两个重要的控制结构:迭代和选择。
Both of these are supported by Python in various forms.
Python以各种形式支持这两者。
The programmer can choose the statement that is most useful for the given circumstance.
程序员可以选择对给定环境最有用的语句。
For iteration, Python provides a standard while statement and a very powerful for statement.
对于迭代,Python提供了一个标准的while语句和一个非常强大的for语句。
The while statement repeats a body of code as long as a condition is true.
只要条件为真,while语句就会重复一段代码。
For example,
例如,
>>> counter = 1
>>> while counter <= 5:
... print("Hello, world")
... counter = counter + 1
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
prints out the phrase “Hello, world” five times.
打印出短语“Hello,world”五次。
The condition on the while statement is evaluated at the start of each repetition.
while语句的条件在每次重复开始时计算。
If the condition is True, the body of the statement will execute.
如果条件为真,则执行语句体。
It is easy to see the structure of a Python while statement due to the mandatory indentation pattern that the language enforces.
由于Python语言强制的缩进模式,很容易看出Python while语句的结构。
The while statement is a very general purpose iterative structure that we will use in a number of different algorithms.
while语句是一个非常通用的迭代结构,我们将在许多不同的算法中使用它。
In many cases, a compound condition will control the iteration.
在许多情况下,复合条件将控制迭代。
A fragment such as
一个片段,例如
while counter <= 10 and not done:
...
would cause the body of the statement to be executed only in the case where both parts of the condition are satisfied.
只在条件的两个部分都满足的情况下才执行语句体。
The value of the variable counter would need to be less than or equal to 10 and the value of the variable done would need to be False (not False is True) so that True and True results in True.
变量counter的值必须小于或等于10,完成的变量的值必须为False(不是False是True),这样True和True的结果就会为True。
Even though this type of construct is very useful in a wide variety of situations, another iterative structure, the for statement, can be used in conjunction with many of the Python collections.
尽管这种类型的构造在各种各样的情况下都非常有用,但另一种迭代结构for语句可以与许多Python集合一起使用。
The for statement can be used to iterate over the members of a collection, so long as the collection is a sequence.
for语句可用于遍历集合的成员,只要集合是序列。
So, for example,
举个例子,
>>> for item in [1,3,6,2,5]:
... print(item)
...
1
3
6
2
5
assigns the variable item to be each successive value in the list [1,3,6,2,5].
将变量item赋值为列表[1,3,6,2,5]中的每个连续值。
The body of the iteration is then executed.
然后执行迭代体。
This works for any collection that is a sequence (lists, tuples, and strings).
这适用于任何序列集合(列表、元组和字符串)。
A common use of the for statement is to implement definite iteration over a range of values.
for语句的一个常见用法是实现对一定范围的值的确定迭代。
The statement
声明如下
>>> for item in range(5):
... print(item**2)
...
0
1
4
9
16
>>>
will perform the print function five times.
将执行打印功能五次。
The range function will return a range object representing the sequence 0,1,2,3,4 and each value will be assigned to the variable item.
range函数将返回一个表示序列0、1、2、3、4的range对象,并将每个值赋给变量item。
This value is then squared and printed.
然后将该值平方并打印。
The other very useful version of this iteration structure is used to process each character of a string.
这个迭代结构的另一个非常有用的版本用于处理字符串中的每个字符。
The following code fragment iterates over a list of strings and for each string processes each character by appending it to a list.
下面的代码片段遍历一个字符串列表,对于每个字符串,通过将其追加到一个列表来处理每个字符。
The result is a list of all the letters in all of the words.
结果是所有单词中的所有字母的列表。
wordlist = ['cat','dog','rabbit']
letterlist = [ ]
for aword in wordlist:
for aletter in aword:
letterlist.append(aletter)
print(letterlist)
#运行结果如下
['c', 'a', 't', 'd', 'o', 'g', 'r', 'a', 'b', 'b', 'i', 't']
Selection statements allow programmers to ask questions and then, based on the result, perform different actions.
选择语句允许程序员提出问题,然后根据结果执行不同的操作。
Most programming languages provide two versions of this useful construct: the ifelse and the if.
大多数编程语言都提供了这个有用构造的两个版本:ifelse和if。
A simple example of a binary selection uses the ifelse statement.
一个简单的二进制选择示例使用了ifelse语句。
if n<0:
print("Sorry, value is negative")
else:
print(math.sqrt(n))
In this example, the object referred to by n is checked to see if it is less than zero.
在这个例子中,检查n引用的对象是否小于0。
If it is, a message is printed stating that it is negative.
如果是,则打印一条消息,说明它是负数。
If it is not, the statement performs the else clause and computes the square root.
如果不是,则执行else子句并计算平方根。
Selection constructs, as with any control construct, can be nested so that the result of one question helps decide whether to ask the next.
与任何控制构造一样,选择构造可以嵌套,以便一个问题的结果有助于决定是否问下一个问题。
For example, assume that score is a variable holding a reference to a score for a computer science test.
例如,假设score是一个变量,它包含对计算机科学测试分数的引用。
if score >= 90:
print('A')
else:
if score >=80:
print('B')
else:
if score >= 70:
print('C')
else:
if score >= 60:
print('D')
else:
print('F')
This fragment will classify a value called score by printing the letter grade earned.
该片段将通过打印获得的等级字母来对名为score的值进行分类。
If the score is greater than or equal to 90, the statement will print A.
如果分数大于或等于90分,语句将打印A。
If it is not (else), the next question is asked.
如果不是(else),则会问下一个问题。
If the score is greater than or equal to 80 then it must be between 80 and 89 since the answer to the first question was false.
如果分数大于或等于80,那么分数必须在80到89之间,因为第一个问题的答案是错误的。
In this case print B is printed.
在本例中打印B。
You can see that the Python indentation pattern helps to make sense of the association between if and else without requiring any additional syntactic elements.
您可以看到,Python缩进模式有助于理解if和else之间的关联,而不需要任何额外的语法元素。
An alternative syntax for this type of nested selection uses the elif keyword.
这种嵌套选择的另一种语法使用elif关键字。
The else and the next if are combined so as to eliminate the need for additional nesting levels.
else和next if组合在一起,这样就不需要额外的嵌套级别了。
Note that the final else is still necessary to provide the default case if all other conditions fail.
注意,如果所有其他条件都失败,仍然需要最后的else来提供默认情况。
if score >= 90:
print('A')
elif score >=80:
print('B')
elif score >= 70:
print('C')
elif score >= 60:
print('D')
else:
print('F')
Python also has a single way selection construct, the if statement.
Python还有一种单路选择构造,即if语句。
With this statement, if the condition is true, an action is performed.
使用此语句,如果条件为真,则执行操作。
In the case where the condition is false, processing simply continues on to the next statement after the if.
在条件为false的情况下,处理只是继续到if语句之后的下一个语句。
For example, the following fragment will first check to see if the value of a variable n is negative.
例如,下面的片段将首先检查变量n的值是否为负。
If it is, then it is modified by the absolute value function.
如果是,那么它就被绝对值函数修正了。
Regardless, the next action is to compute the square root.
不管怎样,下一个操作是计算平方根。
if n<0:
n = abs(n)
print(math.sqrt(n))
Returning to lists, there is an alternative method for creating a list that uses iteration and selection constructs known as a list comprehension.
回到列表,有一种使用迭代和选择构造来创建列表的替代方法,称为列表综合。
A list comprehension allows you to easily create a list based on some processing or selection criteria.
列表理解允许您基于某些处理或选择标准轻松创建列表。
For example, if we would like to create a list of the first 10 perfect squares, we could use a for statement:
例如,如果我们想创建一个前10个完全正方形的列表,我们可以使用For语句:
>>> sqlist=[]
>>> for x in range(1,11):
sqlist.append(x*x)
>>> sqlist
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>>
Using a list comprehension, we can do this in one step as
使用列表推导式,我们可以一步完成
>>> sqlist=[x*x for x in range(1,11)]
>>> sqlist
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>>
The variable x takes on the values 1 through 10 as specified by the for construct.
变量x的值为1到10,这是由for构造指定的。
The value of xx is then computed and added to the list that is being constructed.
然后计算xx的值并将其添加到正在构造的列表中。
The general syntax for a list comprehension also allows a selection criteria to be added so that only certain items get added.
列表理解的通用语法还允许添加选择标准,以便只添加某些项。
For example,
例如,
>>> sqlist=[x*x for x in range(1,11) if x%2 != 0]
>>> sqlist
[1, 9, 25, 49, 81]
>>>
This list comprehension constructed a list that only contained the squares of the odd numbers in the range from 1 to 10.
这个列表推导构造了一个仅包含1到10范围内奇数的平方的列表。
Any sequence that supports iteration can be used within a list comprehension to construct a new list.
任何支持迭代的序列都可以在列表推导式中使用来构造新的列表。
>>>[ch.upper() for ch in 'comprehension' if ch not in 'aeiou']
['C', 'M', 'P', 'R', 'H', 'N', 'S', 'N']
>>>
There are two types of errors that typically occur when writing programs.
在编写程序时,通常会发生两种类型的错误。
The first, known as a syntax error, simply means that the programmer has made a mistake in the structure of a statement or expression.
第一种称为语法错误,它仅仅意味着程序员在语句或表达式的结构中犯了错误。
For example, it is incorrect to write a for statement and forget the colon.
例如,写for语句时忘记冒号是不正确的。
>>> for i in range(10)
SyntaxError: invalid syntax (<pyshell#61>, line 1)
In this case, the Python interpreter has found that it cannot complete the processing of this instruction since it does not conform to the rules of the language.
在这种情况下,Python解释器发现它无法完成这条指令的处理,因为它不符合该语言的规则。
Syntax errors are usually more frequent when you are first learning a language.
语法错误通常在你第一次学习一门语言时更常见。
The other type of error, known as a logic error, denotes a situation where the program executes but gives the wrong result.
另一种类型的错误,称为逻辑错误,表示程序执行但给出错误结果的情况。
This can be due to an error in the underlying algorithm or an error in your translation of that algorithm.
这可能是由于底层算法的错误或算法的翻译错误造成的。
In some cases, logic errors lead to very bad situations such as trying to divide by zero or trying to access an item in a list where the index of the item is outside the bounds of the list.
在某些情况下,逻辑错误会导致非常糟糕的情况,比如试图除零,或者试图访问列表中某项的索引超出列表边界的项。
In this case, the logic error leads to a runtime error that causes the program to terminate.
在这种情况下,逻辑错误将导致导致程序终止的运行时错误。
These types of runtime errors are typically called exceptions.
这些类型的运行时错误通常被称为异常。
Most of the time, beginning programmers simply think of exceptions as fatal runtime errors that cause the end of execution.
大多数时候,刚开始的程序员只是把异常看作是导致执行结束的致命运行时错误。
However, most programming languages provide a way to deal with these errors that will allow the programmer to have some type of intervention if they so choose.
然而,大多数编程语言都提供了一种方法来处理这些错误,如果程序员愿意的话,这种方法允许他们进行某种类型的干预。
In addition, programmers can create their own exceptions if they detect a situation in the program execution that warrants it.
此外,如果程序员在程序执行过程中检测到有必要的情况,他们可以创建自己的异常。
When an exception occurs, we say that it has been “raised.”
当异常发生时,我们说它已被“引发”。
You can “handle” the exception that has been raised by using a try statement.
您可以使用try语句“处理”已引发的异常。
For example, consider the following session that asks the user for an integer and then calls the square root function from the math library.
例如,考虑下面的会话,它向用户请求整数,然后从math库调用平方根函数。
If the user enters a value that is greater than or equal to 0, the print will show the square root.
如果用户输入大于或等于0的值,则打印将显示平方根。
However, if the user enters a negative value, the square root function will report a ValueError exception.
但是,如果用户输入一个负数,平方根函数将报告一个ValueError异常。
>>> anumber = int(input("Please enter an integer "))
Please enter an integer -23
>>> print(math.sqrt(anumber))
Traceback (most recent call last):
File "" , line 1, in <module>
print(math.sqrt(anumber))
ValueError: math domain error
>>>
We can handle this exception by calling the print function from within a try block.
我们可以通过在try块中调用print函数来处理这个异常。
A corresponding except block “catches” the exception and prints a message back to the user in the event that an exception occurs.
相应的except块“捕获”异常,并在异常发生时向用户输出消息。
For example:
例如:
>>> try:
print(math.sqrt(anumber))
except:
print("Bad Value for square root")
print("Using absolute value instead")
print(math.sqrt(abs(anumber)))
Bad Value for square root
Using absolute value instead
4.79583152331
>>>
will catch the fact that an exception is raised by sqrt and will instead print the messages back to the user and use the absolute value to be sure that we are taking the square root of a non-negative number.
将捕获由sqrt引发的异常,并将消息打印回给用户,并使用绝对值来确保我们正在取非负数的平方根。
This means that the program will not terminate but instead will continue on to the next statements.
这意味着程序不会终止,而是继续执行下一个语句。
It is also possible for a programmer to cause a runtime exception by using the raise statement.
程序员也可以使用raise语句导致运行时异常。
For example, instead of calling the square root function with a negative number, we could have checked the value first and then raised our own exception.
例如,我们可以先检查值,然后引发自己的异常,而不是调用带有负数的平方根函数。
The code fragment below shows the result of creating a new RuntimeError exception.
下面的代码片段显示了创建一个新的RuntimeError异常的结果。
Note that the program would still terminate but now the exception that caused the termination is something explicitly created by the programmer.
注意,程序仍然会终止,但是现在导致终止的异常是由程序员显式创建的。
>>> if anumber < 0:
... raise RuntimeError("You can't use a negative number")
... else:
... print(math.sqrt(anumber))
...
Traceback (most recent call last):
File "" , line 2, in <module>
RuntimeError: You can't use a negative number
>>>
There are many kinds of exceptions that can be raised in addition to the RuntimeError shown above.
除了上面显示的RuntimeError之外,还可以引发许多类型的异常。
See the Python reference manual for a list of all the available exception types and for how to create your own.
请参阅Python参考手册获取所有可用异常类型的列表,并了解如何创建自己的异常类型。
The earlier example of procedural abstraction called upon a Python function called sqrt from the math module to compute the square root.
前面的过程抽象示例调用了math模块中的一个名为sqrt的Python函数来计算平方根。
In general, we can hide the details of any computation by defining a function.
一般来说,我们可以通过定义一个函数来隐藏任何计算的细节。
A function definition requires a name, a group of parameters, and a body.
函数定义需要一个名称、一组参数和一个函数体。
It may also explicitly return a value.
它也可以显式地返回一个值。
For example, the simple function defined below returns the square of the value you pass into it.
例如,下面定义的简单函数返回传递给它的值的平方。
>>> def square(n):
... return n**2
...
>>> square(3)
9
>>> square(square(3))
81
>>>
The syntax for this function definition includes the name, square, and a parenthesized list of formal parameters.
这个函数定义的语法包括名称、方框和一个括起来的形式参数列表。
For this function, n is the only formal parameter, which suggests that square needs only one piece of data to do its work.
对于这个函数,n是唯一的形式参数,这意味着square只需要一个数据就可以完成它的工作。
The details, hidden “inside the box,” simply compute the result of n**2 and return it.
隐藏在“框内”的细节只是简单地计算n**2的结果并返回它。
We can invoke or call the square function by asking the Python environment to evaluate it, passing an actual parameter value, in this case, 3.
我们可以调用或调用square函数,方法是让Python环境对其求值,并传递一个实际的参数值,在本例中是3。
Note that the call to square returns an integer that can in turn be passed to another invocation.
请注意,对square的调用返回一个整数,该整数可以反过来传递给另一个调用。
We could implement our own square root function by using a well-known technique called “Newton’s Method.”
我们可以使用一种叫做“牛顿法”的著名技术来实现我们自己的平方根函数。
Newton’s Method for approximating square roots performs an iterative computation that converges on the correct value.
牛顿近似平方根的方法是一种迭代计算,它收敛于正确的值。
The equation
这个方程
takes a value n and repeatedly guesses the square root by making each newguess the oldguess in the subsequent iteration.
取一个值n,通过在后续迭代中将每个新猜测变成旧猜测,反复猜测平方根。
The initial guess used here is n/2.
这里最初的猜测是n/2。
Listing 1 shows a function definition that accepts a value n and returns the square root of n after making 20 guesses.
清单1显示了一个函数定义,它接受值n,并在进行20次猜测后返回n的平方根。
Again, the details of Newton’s Method are hidden inside the function definition and the user does not have to know anything about the implementation to use the function for its intended purpose.
同样,Newton方法的细节隐藏在函数定义中,用户不需要知道任何关于实现的信息,就可以将函数用于其预期目的。
Listing 1 also shows the use of the # character as a comment marker.
清单1还展示了#字符作为注释标记的用法。
Any characters that follow the # on a line are ignored.
任何跟在#后面的字符都会被忽略。
Listing 1
清单1
def squareroot(n):
root = n/2 #initial guess will be 1/2 of n
for k in range(20):
root = (1/2)*(root + (n / root))
return root
>>>squareroot(9)
3.0
>>>squareroot(4563)
67.549981495186216
>>>
Self Check
Here’s a self check that really covers everything so far.
这是一个自我检查,它涵盖了到目前为止的所有内容。
You may have heard of the infinite monkey theorem?
你可能听说过猴子无限定理?
The theorem states that a monkey hitting keys at random on a typewriter keyboard for an infinite amount of time will almost surely type a given text, such as the complete works of William Shakespeare.
该定理指出,一只猴子在打字机键盘上任意敲击键盘上无限长时间,几乎肯定会键入给定的文本,比如威廉·莎士比亚的全集。
Well, suppose we replace a monkey with a Python function.
那么,假设我们用Python函数替换猴子。
How long do you think it would take for a Python function to generate just one sentence of Shakespeare?
你认为一个Python函数生成莎士比亚的一句话需要多长时间?
The sentence we’ll shoot for is: “methinks it is like a weasel”
我们要争取的句子是:“我认为它像黄鼠狼”
You’re not going to want to run this one in the browser, so fire up your favorite Python IDE.
您可能不希望在浏览器中运行这个程序,所以启动您最喜欢的Python IDE。
The way we’ll simulate this is to write a function that generates a string that is 28 characters long by choosing random letters from the 26 letters in the alphabet plus the space.
我们模拟这一过程的方法是编写一个函数,通过从字母表中的26个字母加上空格中随机选择字母,生成一个长度为28个字符的字符串。
We’ll write another function that will score each generated string by comparing the randomly generated string to the goal.
我们将编写另一个函数,通过将随机生成的字符串与目标进行比较来对每个生成的字符串进行评分。
A third function will repeatedly call generate and score, then if 100% of the letters are correct we are done.
第三个函数将重复调用generate和score,然后如果100%的字母是正确的,我们就完成了。
If the letters are not correct then we will generate a whole new string.
如果字母不正确,我们将生成一个全新的字符串。
To make it easier to follow your program’s progress this third function should print out the best string generated so far and its score every 1000 tries.
为了更容易地跟踪程序的进程,第三个函数应该打印到目前为止生成的最好的字符串及其每1000次尝试的得分。
Self Check Challenge
See if you can improve upon the program in the self check by keeping letters that are correct and only modifying one character in the best string so far.
看看你能否在self check中改进程序,保持正确的字母,只修改迄今为止最好的字符串中的一个字符。
This is a type of algorithm in the class of ‘hill climbing’ algorithms, that is we only keep the result if it is better than the previous one.
这是“爬山”算法中的一种,也就是我们只保留比前一种更好的结果。
2020.12.25今天暂且就到这吧…
We stated earlier that Python is an object-oriented programming language.
我们前面说过,Python是一种面向对象的编程语言。
So far, we have used a number of built-in classes to show examples of data and control structures.
到目前为止,我们已经使用了许多内置类来展示数据和控件结构的示例。
One of the most powerful features in an object-oriented programming language is the ability to allow a programmer (problem solver) to create new classes that model data that is needed to solve the problem.
面向对象编程语言中最强大的特性之一是允许程序员(问题解决者)创建新类,为解决问题所需的数据建模。
Remember that we use abstract data types to provide the logical description of what a data object looks like (its state) and what it can do (its methods).
请记住,我们使用抽象数据类型来提供数据对象的逻辑描述(它的状态)和它能做什么(它的方法)。
By building a class that implements an abstract data type, a programmer can take advantage of the abstraction process and at the same time provide the details necessary to actually use the abstraction in a program.
通过构建一个实现抽象数据类型的类,程序员可以利用抽象过程,同时提供在程序中实际使用抽象所必需的细节。
Whenever we want to implement an abstract data type, we will do so with a new class.
无论何时我们想要实现一个抽象数据类型,我们都将使用一个新类来实现。
A very common example to show the details of implementing a user-defined class is to construct a class to implement the abstract data type Fraction.
展示实现用户定义类的细节的一个非常常见的例子是构造一个类来实现抽象数据类型Fraction。
We have already seen that Python provides a number of numeric classes for our use.
我们已经看到Python提供了许多可供我们使用的数字类。
There are times, however, that it would be most appropriate to be able to create data objects that “look like” fractions.
但是,有时候,创建“看起来像”分数的数据对象是最合适的。
A fraction such as 3/5 consists of two parts.
像3/5这样的分数由两部分组成。
The top value, known as the numerator, can be any integer.
上面的值称为分子,可以是任何整数。
The bottom value, called the denominator, can be any integer greater than 0 (negative fractions have a negative numerator).
底部的值称为分母,可以是任何大于0的整数(负分数的分子为负)。
Although it is possible to create a floating point approximation for any fraction, in this case we would like to represent the fraction as an exact value.
虽然可以为任何分数创建浮点近似值,但在这种情况下,我们希望将分数表示为精确值。
The operations for the Fraction type will allow a Fraction data object to behave like any other numeric value.
分数类型的操作将允许分数数据对象像任何其他数值一样行为。
We need to be able to add, subtract, multiply, and divide fractions.
我们需要会做分数的加、减、乘、除。
We also want to be able to show fractions using the standard “slash” form, for example 3/5.
我们还希望能够使用标准的“斜线”形式来表示分数,例如3/5。
In addition, all fraction methods should return results in their lowest terms so that no matter what computation is performed, we always end up with the most common form.
此外,所有的分数方法都应该以它们的最低项返回结果,这样无论执行什么计算,我们总是以最常见的形式结束。
In Python, we define a new class by providing a name and a set of method definitions that are syntactically similar to function definitions.
在Python中,我们通过提供一个名称和一组在语法上类似于函数定义的方法定义来定义一个新类。
For this example,
例如,
class Fraction:
#the methods go here
provides the framework for us to define the methods.
为我们提供了定义方法的框架。
The first method that all classes should provide is the constructor.
所有类都应该提供的第一个方法是构造函数。
The constructor defines the way in which data objects are created.
构造函数定义了创建数据对象的方式。
To create a Fraction object, we will need to provide two pieces of data, the numerator and the denominator.
要创建一个分数对象,我们需要提供两部分数据,分子和分母。
In Python, the constructor method is always called init (two underscores before and after init) and is shown in Listing 2.
在Python中,构造函数方法总是被调用__init__(在init之前和之后有两个下划线),如清单2所示。
Listing 2
清单2
class Fraction:
def __init__(self,top,bottom):
self.num = top
self.den = bottom
Notice that the formal parameter list contains three items (self, top, bottom).
注意,形式参数列表包含三个项(self、top、bottom)。
self is a special parameter that will always be used as a reference back to the object itself.
self是一个特殊的参数,它总是被用作对象本身的引用。
It must always be the first formal parameter; however, it will never be given an actual parameter value upon invocation.
它必须始终是第一个形式参数;但是,在调用时不会给它一个实际的参数值。
As described earlier, fractions require two pieces of state data, the numerator and the denominator.
如前所述,分数需要两部分状态数据,分子和分母。
The notation self.num in the constructor defines the fraction object to have an internal data object called num as part of its state.
构造函数中的符号self.num定义了分数对象,将一个名为num的内部数据对象作为其状态的一部分。
Likewise, self.den creates the denominator.
同样,self.den创建分母。
The values of the two formal parameters are initially assigned to the state, allowing the new fraction object to know its starting value.
这两个形式参数的值最初被赋给状态,允许新的fraction对象知道它的起始值。
To create an instance of the Fraction class, we must invoke the constructor.
要创建Fraction类的实例,必须调用构造函数。
This happens by using the name of the class and passing actual values for the necessary state (note that we never directly invoke init).
这是通过使用类名并传递必要状态的实际值来实现的(请注意,我们从未直接调用__init__)。
For example,
例如,
myfraction = Fraction(3,5)
creates an object called myfraction representing the fraction 3/5 (three-fifths).
创建一个名为myfraction的对象,表示分数3/5 (3 -fifths)。
Figure 5 shows this object as it is now implemented.
图5显示了现在实现的这个对象。
Figure 5: An Instance of the Fraction Class
图5:Fraction类的实例
The next thing we need to do is implement the behavior that the abstract data type requires.
我们需要做的下一件事是实现抽象数据类型需要的行为。
To begin, consider what happens when we try to print a Fraction object.
首先,考虑一下尝试打印分数对象时会发生什么。
>>> myf = Fraction(3,5)
>>> print(myf)
<__main__.Fraction instance at 0x409b1acc>
The fraction object, myf, does not know how to respond to this request to print.
分数对象myf不知道如何响应这个打印请求。
The print function requires that the object convert itself into a string so that the string can be written to the output.
print函数要求对象将自身转换为字符串,以便将字符串写入输出。
The only choice myf has is to show the actual reference that is stored in the variable (the address itself).
myf唯一的选择是显示存储在变量(地址本身)中的实际引用。
This is not what we want.
这不是我们想要的。
There are two ways we can solve this problem.
有两种方法可以解决这个问题。
One is to define a method called show that will allow the Fraction object to print itself as a string.
一种方法是定义一个名为show的方法,允许Fraction对象将自身打印为字符串。
We can implement this method as shown in Listing 3.
我们可以实现如清单3所示的方法。
If we create a Fraction object as before, we can ask it to show itself, in other words, print itself in the proper format.
如果我们像前面一样创建一个分数对象,我们可以要求它显示自身,换句话说,以适当的格式打印自身。
Unfortunately, this does not work in general.
不幸的是,这通常不起作用。
In order to make printing work properly, we need to tell the Fraction class how to convert itself into a string.
为了使打印工作正常,我们需要告诉Fraction类如何将自己转换为字符串。
This is what the print function needs in order to do its job.
这是打印函数完成其工作所需要的。
Listing 3
清单3
def show(self):
print(self.num,"/",self.den)
>>> myf = Fraction(3,5)
>>> myf.show()
3 / 5
>>> print(myf)
<__main__.Fraction instance at 0x40bce9ac>
>>>
In Python, all classes have a set of standard methods that are provided but may not work properly.
在Python中,所有的类都有一组提供的标准方法,但可能不能正常工作。
One of these, str, is the method to convert an object into a string.
其中__str__是将对象转换为字符串的方法。
The default implementation for this method is to return the instance address string as we have already seen.
这个方法的默认实现是返回我们已经看到的实例地址字符串。
What we need to do is provide a “better” implementation for this method.
我们需要做的是为这个方法提供一个“更好的”实现。
We will say that this implementation overrides the previous one, or that it redefines the method’s behavior.
我们会说这个实现覆盖了前一个,或者它重新定义了方法的行为。
To do this, we simply define a method with the name str and give it a new implementation as shown in Listing 4.
为此,我们简单地定义一个名为__str__的方法,并给它一个新的实现,如清单4所示。
This definition does not need any other information except the special parameter self.
这个定义除了特殊参数self之外不需要任何其他信息。
In turn, the method will build a string representation by converting each piece of internal state data to a string and then placing a / character in between the strings using string concatenation.
反过来,该方法将通过将每个内部状态数据块转换为字符串,然后使用字符串连接在字符串之间放置一个/字符来构建字符串表示。
The resulting string will be returned any time a Fraction object is asked to convert itself to a string.
当要求分数对象将自身转换为字符串时,将返回结果字符串。
Notice the various ways that this function is used.
请注意使用这个函数的各种方式。
Listing 4
清单4
def __str__(self):
return str(self.num)+"/"+str(self.den)
>>> myf = Fraction(3,5)
>>> print(myf)
3/5
>>> print("I ate", myf, "of the pizza")
I ate 3/5 of the pizza
>>> myf.__str__()
'3/5'
>>> str(myf)
'3/5'
>>>
We can override many other methods for our new Fraction class.
我们可以为新的分数类重写许多其他方法。
Some of the most important of these are the basic arithmetic operations.
其中一些最重要的是基本的算术运算。
We would like to be able to create two Fraction objects and then add them together using the standard “+” notation.
我们希望能够创建两个分数对象,然后使用标准的“+”表示法将它们相加。
At this point, if we try to add two fractions, we get the following:
在这一点上,如果我们尝试把两个分数相加,我们得到以下结果:
>>> f1 = Fraction(1,4)
>>> f2 = Fraction(1,2)
>>> f1+f2
Traceback (most recent call last):
File "" , line 1, in -toplevel-
f1+f2
TypeError: unsupported operand type(s) for +:
'instance' and 'instance'
>>>
If you look closely at the error, you see that the problem is that the “+” operator does not understand the Fraction operands.
如果仔细观察这个错误,就会发现问题在于“+”操作符不理解分数操作数。
We can fix this by providing the Fraction class with a method that overrides the addition method.
我们可以通过为Fraction类提供一个覆盖加法方法的方法来解决这个问题。
In Python, this method is called add and it requires two parameters.
在Python中,该方法被称为__add__,它需要两个参数。
The first, self, is always needed, and the second represents the other operand in the expression.
总是需要第一个操作数self,第二个操作数表示表达式中的另一个操作数。
For example,
例如,
f1.__add__(f2)
would ask the Fraction object f1 to add the Fraction object f2 to itself.
会要求分数对象f1将分数对象f2与自身相加。
This can be written in the standard notation, f1+f2.
这可以写成标准符号f1+f2。
12.27emmm今天没有学多长时间,有点水…
Two fractions must have the same denominator to be added.
两个分数相加的分母必须相同。
The easiest way to make sure they have the same denominator is to simply use the product of the two denominators as a common denominator so that
确保分母相同的最简单的方法就是用两个分母的乘积作为公分母
The implementation is shown in Listing 5.
实现如清单5所示。
The addition function returns a new Fraction object with the numerator and denominator of the sum.
加法函数返回一个新的分数对象,其中包含和的分子和分母。
We can use this method by writing a standard arithmetic expression involving fractions, assigning the result of the addition, and then printing our result.
我们可以使用这种方法,编写一个包含分数的标准算术表达式,对加法的结果赋值,然后打印结果。
Listing 5
清单5
def __add__(self,otherfraction):
newnum = self.num*otherfraction.den + self.den*otherfraction.num
newden = self.den * otherfraction.den
return Fraction(newnum,newden)
>>> f1=Fraction(1,4)
>>> f2=Fraction(1,2)
>>> f3=f1+f2
>>> print(f3)
6/8
>>>
The addition method works as we desire, but one thing could be better.
加法法如我们所愿,但有一件事可以做得更好。
Note that 6/8 is the correct result (1/4+1/2) but that it is not in the “lowest terms” representation.
注意,6/8是正确的结果(1/4+1/2),但它不是在“最低条款”的表示。
The best representation would be 3/4.
最好是3/4。
In order to be sure that our results are always in the lowest terms, we need a helper function that knows how to reduce fractions.
为了确保我们的结果总是最低项,我们需要一个知道如何减少分数的辅助函数。
This function will need to look for the greatest common divisor, or GCD.
这个函数将需要寻找最大公约数,或GCD。(注:GCD为最大公约数)
We can then divide the numerator and the denominator by the GCD and the result will be reduced to lowest terms.
然后,我们可以将分子和分母除以GCD,结果将减少到最低项。
The best-known algorithm for finding a greatest common divisor is Euclid’s Algorithm, which will be discussed in detail in Chapter 8.
最著名的求最大公约数的算法是欧几里得算法,它将在第8章详细讨论。
Euclid’s Algorithm states that the greatest common divisor of two integers m and n is n if n divides m evenly.
欧几里得算法指出,如果n能整除m,两个整数m和n的最大公约数是n。
However, if n does not divide m evenly, then the answer is the greatest common divisor of n and the remainder of m divided by n.
但是,如果n不能整除m,那么答案就是n的最大公约数和m除以n的余数。
We will simply provide an iterative implementation here (see ActiveCode 1).
我们将在这里简单地提供一个迭代实现(参见ActiveCode 1)。
Note that this implementation of the GCD algorithm only works when the denominator is positive.
注意,GCD算法的这种实现只在分母为正时有效。
This is acceptable for our fraction class because we have said that a negative fraction will be represented by a negative numerator.
这对于分数类来说是可以接受的,因为我们已经说过,负的分数可以用负的分子表示。
ActiveCode 1
def gcd(m,n):
while m%n != 0:
oldm = m
oldn = n
m = oldn
n = oldm%oldn
return n
print(gcd(20,10))
#输出结果如下
10
Now we can use this function to help reduce any fraction.
现在我们可以用这个函数来简化任何分数。
To put a fraction in lowest terms, we will divide the numerator and the denominator by their greatest common divisor.
为了把分数化成最低项,我们将分子和分母除以它们的最大公约数。
So, for the fraction 6/8, the greatest common divisor is 2.
对于分数6/8,最大公约数是2。
Dividing the top and the bottom by 2 creates a new fraction, 3/4 (see Listing 6).
将顶部和底部除以2将创建一个新的分数3/4(参见清单6)。
Listing 6
清单 6
def __add__(self,otherfraction):
newnum = self.num*otherfraction.den + self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum,newden)
return Fraction(newnum//common,newden//common)
>>> f2=Fraction(1,2)
>>> f3=f1+f2
>>> print(f3)
3/4
>>>
Figure 6: An Instance of the Fraction Class with Two Methods
图6:带有两个方法的Fraction类的实例
Our Fraction object now has two very useful methods and looks like Figure 6.
我们的分数对象现在有两个非常有用的方法,如图6所示。
An additional group of methods that we need to include in our example Fraction class will allow two fractions to compare themselves to one another.
在我们的示例Fraction类中需要包含的另外一组方法将允许两个分数相互比较。
Assume we have two Fraction objects, f1 and f2.
假设我们有两个分数对象,f1和f2。
f1==f2 will only be True if they are references to the same object.
f1==f2只有当它们是对同一对象的引用时才为真。
Two different objects with the same numerators and denominators would not be equal under this implementation.
在这个实现中,具有相同分子和分母的两个不同对象不会相等。
This is called shallow equality (see Figure 7).
这称为浅相等(参见图7)。
Figure 7: Shallow Equality Versus Deep Equality
图7:浅平等与深平等
We can create deep equality (see Figure 7)–equality by the same value, not the same reference–by overriding the eq method.
我们可以通过覆盖__eq__方法创建深度相等(参见图7)——相同的值而不是相同的引用产生相等。
The eq method is another standard method available in any class.
__eq__方法是在任何类中可用的另一个标准方法。
The eq method compares two objects and returns True if their values are the same, False otherwise.
__eq__方法比较两个对象,如果它们的值相同则返回True,否则返回False。
In the Fraction class, we can implement the eq method by again putting the two fractions in common terms and then comparing the numerators (see Listing 7).
在Fraction类中,我们可以通过再次将两个分数放在一起,然后比较分子来实现____eq____方法(参见清单7)。
It is important to note that there are other relational operators that can be overridden.
需要注意的是,还有其他关系操作符可以被覆盖。
For example, the le method provides the less than or equal functionality.
例如,__le__方法提供了小于或等于的功能。
Listing 7
清单 7
def __eq__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum == secondnum
The complete Fraction class, up to this point, is shown in ActiveCode 2.
到目前为止,完整的分数类显示在ActiveCode 2中。
We leave the remaining arithmetic and relational methods as exercises.
我们将剩下的算术和关系方法作为练习。
ActiveCode 2
def gcd(m,n):
while m%n != 0:
oldm = m
oldn = n
m = oldn
n = oldm%oldn
return n
class Fraction:
def __init__(self,top,bottom):
self.num = top
self.den = bottom
def __str__(self):
return str(self.num)+"/"+str(self.den)
def show(self):
print(self.num,"/",self.den)
def __add__(self,otherfraction):
newnum = self.num*otherfraction.den + \
self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum,newden)
return Fraction(newnum//common,newden//common)
def __eq__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum == secondnum
x = Fraction(1,2)
y = Fraction(2,3)
print(x+y)
print(x == y)
#输出结果如下
7/6
False
Self Check
To make sure you understand how operators are implemented in Python classes, and how to properly write methods, write some methods to implement *, /, and - .
为了确保你理解在Python类中操作符是如何实现的,以及如何正确地编写方法,编写一些实现*、/和-的方法。
Also implement comparison operators > and <
还实现了比较操作符>和<
Our final section will introduce another important aspect of object-oriented programming.
我们的最后一节将介绍面向对象编程的另一个重要方面。
Inheritance is the ability for one class to be related to another class in much the same way that people can be related to one another.
继承是一个类与另一个类关联的能力,就像人们可以彼此关联一样。
Children inherit characteristics from their parents.
孩子从父母那里继承特征。
Similarly, Python child classes can inherit characteristic data and behavior from a parent class.
类似地,Python的子类可以从父类继承特征数据和行为。
These classes are often referred to as subclasses and superclasses.
这些类通常被称为子类和超类。
Figure 8 shows the built-in Python collections and their relationships to one another.
图8显示了内置的Python集合及其彼此之间的关系。
We call a relationship structure such as this an inheritance hierarchy.
我们称这样的关系结构为继承层次结构。
For example, the list is a child of the sequential collection.
例如,列表是顺序集合的子集合。
In this case, we call the list the child and the sequence the parent (or subclass list and superclass sequence).
在本例中,我们将列表称为子类,将序列称为父类(或子类列表和超类序列)。
This is often referred to as an IS-A Relationship (the list IS-A sequential collection).
这通常被称为is - a关系(列表是-一个顺序集合)。
This implies that lists inherit important characteristics from sequences, namely the ordering of the underlying data and operations such as concatenation, repetition, and indexing.
这意味着列表从序列继承了重要的特征,即底层数据的顺序和操作,如连接、重复和索引。
Figure 8: An Inheritance Hierarchy for Python Collections
图8:Python集合的继承层次结构
Lists, tuples, and strings are all types of sequential collections.
列表、元组和字符串都是顺序集合的类型。
They all inherit common data organization and operations.
它们都继承通用的数据组织和操作。
However, each of them is distinct based on whether the data is homogeneous and whether the collection is immutable.
然而,根据数据是否同构以及集合是否不可变,它们都是不同的。
The children all gain from their parents but distinguish themselves by adding additional characteristics.
孩子们都从父母那里获得了好处,但又通过添加额外的特征来区分自己。
By organizing classes in this hierarchical fashion, object-oriented programming languages allow previously written code to be extended to meet the needs of a new situation.
通过以这种分层的方式组织类,面向对象的编程语言允许对以前编写的代码进行扩展,以满足新情况的需要。
In addition, by organizing data in this hierarchical manner, we can better understand the relationships that exist.
此外,通过以这种分层的方式组织数据,我们可以更好地理解存在的关系。
We can be more efficient in building our abstract representations.
我们可以更有效地构建我们的抽象表示。
To explore this idea further, we will construct a simulation, an application to simulate digital circuits.
为了进一步探讨这一想法,我们将构建一个模拟数字电路的应用程序。
The basic building block for this simulation will be the logic gate.
这个模拟的基本构建块是逻辑门。
These electronic switches represent boolean algebra relationships between their input and their output.
这些电子开关代表了输入和输出之间的布尔代数关系。
In general, gates have a single output line.
一般来说,栅极只有一条输出线。
The value of the output is dependent on the values given on the input lines.
输出的值取决于输入行给出的值。
AND gates have two input lines, each of which can be either 0 or 1 (representing False or True, repectively).
AND门有两个输入行,每个输入行可以是0或1(分别表示False或True)。
If both of the input lines have the value 1, the resulting output is 1.
如果两行输入的值都是1,那么结果输出是1。
However, if either or both of the input lines is 0, the result is 0.
但是,如果其中一行或两行输入都是0,则结果是0。
OR gates also have two input lines and produce a 1 if one or both of the input values is a 1.
OR门也有两个输入行,如果其中一个或两个输入值都是1,则生成1。
In the case where both input lines are 0, the result is 0.
如果两个输入行都为0,则结果为0。
NOT gates differ from the other two gates in that they only have a single input line.
NOT门与其他两个门的区别在于它们只有一个输入行。
The output value is simply the opposite of the input value.
输出值与输入值相反。
If 0 appears on the input, 1 is produced on the output.
如果输入为0,输出为1。
Similarly, 1 produces 0.
同理,1等于0。
Figure 9 shows how each of these gates is typically represented.
图9显示了这些门通常是如何表示的。
Each gate also has a truth table of values showing the input-to-output mapping that is performed by the gate.
每个门也有一个值的真值表,显示由门执行的输入-输出映射。
By combining these gates in various patterns and then applying a set of input values, we can build circuits that have logical functions.
通过以不同的模式组合这些门,然后应用一组输入值,我们可以建立具有逻辑功能的电路。
Figure 10 shows a circuit consisting of two AND gates, one OR gate, and a single NOT gate.
图10所示的电路由两个与门、一个或门和一个非门组成。
The output lines from the two AND gates feed directly into the OR gate, and the resulting output from the OR gate is given to the NOT gate.
双与门的输出线直接输入OR门,OR门的输出结果给NOT门。
If we apply a set of input values to the four input lines (two for each AND gate), the values are processed and a result appears at the output of the NOT gate.
如果我们将一组输入值应用到四个输入行(每个AND门两个),则会处理这些值,并在NOT门的输出处显示结果。
Figure 10 also shows an example with values.
图10还显示了一个带有值的示例。
Figure 10: Circuit
图10:电路
In order to implement a circuit, we will first build a representation for logic gates.
为了实现一个电路,我们将首先建立一个逻辑门的表示。
Logic gates are easily organized into a class inheritance hierarchy as shown in Figure 11.
逻辑门很容易组织到一个类继承层次结构中,如图11所示。
At the top of the hierarchy, the LogicGate class represents the most general characteristics of logic gates: namely, a label for the gate and an output line.
在层次结构的顶部,LogicGate类表示逻辑门的最一般特征:即门的标签和输出行。
The next level of subclasses breaks the logic gates into two families, those that have one input line and those that have two.
下一层的子类将逻辑门分为两类,一类有一个输入行,另一类有两个输入行。
Below that, the specific logic functions of each appear.
在它下面,出现了每一个特定的逻辑函数。
Figure 11: An Inheritance Hierarchy for Logic Gates
图11:逻辑门的继承层次结构
We can now start to implement the classes by starting with the most general, LogicGate.
现在,我们可以从最通用的LogicGate开始实现这些类。
As noted earlier, each gate has a label for identification and a single output line.
如前所述,每个门都有一个标识标签和一个输出行。
In addition, we need methods to allow a user of a gate to ask the gate for its label.
此外,我们还需要一些方法来允许gate的用户向gate请求其标签。
The other behavior that every logic gate needs is the ability to know its output value.
每个逻辑门需要的另一个行为是知道其输出值的能力。
This will require that the gate perform the appropriate logic based on the current input.
这就要求栅极根据输入电流执行适当的逻辑。
In order to produce output, the gate needs to know specifically what that logic is.
为了产生输出,门需要知道具体的逻辑是什么。
This means calling a method to perform the logic computation.
这意味着调用一个方法来执行逻辑计算。
The complete class is shown in Listing 8.
完整的类如清单8所示。
Listing 8
清单8
class LogicGate:
def __init__(self,n):
self.label = n
self.output = None
def getLabel(self):
return self.label
def getOutput(self):
self.output = self.performGateLogic()
return self.output
At this point, we will not implement the performGateLogic function.
此时,我们将不实现performGateLogic函数。
The reason for this is that we do not know how each gate will perform its own logic operation.
这样做的原因是我们不知道每个门将如何执行自己的逻辑操作。
Those details will be included by each individual gate that is added to the hierarchy.
这些细节将被添加到层次结构中的每个门所包含。
This is a very powerful idea in object-oriented programming.
这在面向对象编程中是一个非常强大的思想。
We are writing a method that will use code that does not exist yet.
我们正在编写一个方法,它将使用还不存在的代码。
The parameter self is a reference to the actual gate object invoking the method.
参数self是对调用该方法的实际gate对象的引用。
Any new logic gate that gets added to the hierarchy will simply need to implement the performGateLogic function and it will be used at the appropriate time.
添加到层次结构中的任何新的逻辑门都只需要实现performGateLogic函数,并在适当的时候使用它。
Once done, the gate can provide its output value.
一旦完成,门可以提供其输出值。
This ability to extend a hierarchy that currently exists and provide the specific functions that the hierarchy needs to use the new class is extremely important for reusing existing code.
这种扩展当前存在的层次结构并提供层次结构使用新类所需的特定函数的能力对于重用现有代码非常重要。
We categorized the logic gates based on the number of input lines.
我们根据输入行数对逻辑门进行了分类。
The AND gate has two input lines.
AND门有两条输入线。
The OR gate also has two input lines.
OR门也有两条输入线。
NOT gates have one input line.
NOT门只有一个输入行。
The BinaryGate class will be a subclass of LogicGate and will add two input lines.
BinaryGate类将是LogicGate的子类,并将添加两个输入行。
The UnaryGate class will also subclass LogicGate but will have only a single input line.
UnaryGate类也将继承LogicGate,但将只有一个输入行。
In computer circuit design, these lines are sometimes called “pins” so we will use that terminology in our implementation.
在计算机电路设计中,这些线有时被称为“引脚”,所以我们将在我们的实现中使用这个术语。
Listing 9
清单9
class BinaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pinA = None
self.pinB = None
def getPinA(self):
return int(input("Enter Pin A input for gate "+ self.getLabel()+"-->"))
def getPinB(self):
return int(input("Enter Pin B input for gate "+ self.getLabel()+"-->"))
Listing 10
清单10
class UnaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pin = None
def getPin(self):
return int(input("Enter Pin input for gate "+ self.getLabel()+"-->"))
Listing 9 and Listing 10 implement these two classes.
清单9和清单10实现了这两个类。
The constructors in both of these classes start with an explicit call to the constructor of the parent class using the parent’s init method.
这两个类的构造函数都以使用父类的__init__方法显式调用父类的构造函数开始。
When creating an instance of the BinaryGate class, we first want to initialize any data items that are inherited from LogicGate.
在创建BinaryGate类的实例时,我们首先希望初始化从LogicGate继承的任何数据项。
In this case, that means the label for the gate.
在本例中,这意味着gate的标签。
The constructor then goes on to add the two input lines (pinA and pinB).
构造函数接着添加两个输入行(pinA和pinB)。
This is a very common pattern that you should always use when building class hierarchies.
这是构建类层次结构时应该始终使用的一种非常常见的模式。
Child class constructors need to call parent class constructors and then move on to their own distinguishing data.
子类构造函数需要调用父类构造函数,然后转移到它们自己的区别数据。
Python also has a function called super which can be used in place of explicitly naming the parent class.
Python还有一个名为super的函数,可以用来代替显式命名父类。
This is a more general mechanism, and is widely used, especially when a class has more than one parent.
这是一种更通用的机制,并且被广泛使用,特别是当一个类有多个父类时。
But, this is not something we are going to discuss in this introduction.
但是,这不是我们要在介绍中讨论的东西。
For example in our example above LogicGate.
例如,在我们上面的例子LogicGate中。
init(self,n) could be replaced with super(UnaryGate,self).init(n).
_ init _(self,n)可以被super(UnaryGate,self).init(n)替换。
The only behavior that the BinaryGate class adds is the ability to get the values from the two input lines.
BinaryGate类添加的惟一行为是能够从两个输入行获取值。
Since these values come from some external place, we will simply ask the user via an input statement to provide them.
由于这些值来自外部位置,我们将通过输入语句请求用户提供它们。
The same implementation occurs for the UnaryGate class except that there is only one input line.
UnaryGate类也有相同的实现,只是只有一个输入行。
Now that we have a general class for gates depending on the number of input lines, we can build specific gates that have unique behavior.
现在,根据输入行数,我们有了一个用于门的通用类,我们可以构建具有独特行为的特定门。
For example, the AndGate class will be a subclass of BinaryGate since AND gates have two input lines.
例如,AndGate类将是BinaryGate的子类,因为AND门有两个输入行。
As before, the first line of the constructor calls upon the parent class constructor (BinaryGate), which in turn calls its parent class constructor (LogicGate).
与前面一样,构造函数的第一行调用父类构造函数(BinaryGate),该构造函数又调用它的父类构造函数(LogicGate)。
Note that the AndGate class does not provide any new data since it inherits two input lines, one output line, and a label.
请注意,AndGate类不提供任何新数据,因为它继承了两个输入行、一个输出行和一个标签。
Listing 11
清单11
class AndGate(BinaryGate):
def __init__(self,n):
super(AndGate,self).__init__(n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
The only thing AndGate needs to add is the specific behavior that performs the boolean operation that was described earlier.
AndGate唯一需要添加的是执行前面描述的布尔操作的特定行为。
This is the place where we can provide the performGateLogic method.
在这里我们可以提供performGateLogic方法。
For an AND gate, this method first must get the two input values and then only return 1 if both input values are 1.
对于AND门,该方法首先必须获得两个输入值,然后只有当两个输入值都为1时才返回1。
The complete class is shown in Listing 11.
完整的类如清单11所示。
We can show the AndGate class in action by creating an instance and asking it to compute its output.
我们可以通过创建一个实例并要求它计算输出来显示AndGate类的运行情况。
The following session shows an AndGate object, g1, that has an internal label “G1”.
接下来的会话显示了一个AndGate对象g1,它有一个内部标签“g1”。
When we invoke the getOutput method, the object must first call its performGateLogic method which in turn queries the two input lines.
当我们调用getOutput方法时,对象必须首先调用它的performGateLogic方法,该方法反过来查询两个输入行。
Once the values are provided, the correct output is shown.
一旦提供了这些值,就会显示正确的输出。
>>> g1 = AndGate("G1")
>>> g1.getOutput()
Enter Pin A input for gate G1-->1
Enter Pin B input for gate G1-->0
0
The same development can be done for OR gates and NOT gates.
同样的开发也适用于OR门和NOT门。
The OrGate class will also be a subclass of BinaryGate and the NotGate class will extend the UnaryGate class.
OrGate类也将是BinaryGate的子类,而NotGate类将扩展UnaryGate类。
Both of these classes will need to provide their own performGateLogic functions, as this is their specific behavior.
这两个类都需要提供它们自己的performGateLogic函数,因为这是它们的特定行为。
We can use a single gate by first constructing an instance of one of the gate classes and then asking the gate for its output (which will in turn need inputs to be provided).
我们可以使用单个门,首先构造一个门类的实例,然后请求门输出(这将反过来需要提供输入)。
For example:
例如:
>>> g2 = OrGate("G2")
>>> g2.getOutput()
Enter Pin A input for gate G2-->1
Enter Pin B input for gate G2-->1
1
>>> g2.getOutput()
Enter Pin A input for gate G2-->0
Enter Pin B input for gate G2-->0
0
>>> g3 = NotGate("G3")
>>> g3.getOutput()
Enter Pin input for gate G3-->0
1
Now that we have the basic gates working, we can turn our attention to building circuits.
现在我们已经有了基本的门,我们可以把注意力转向构建电路。
In order to create a circuit, we need to connect gates together, the output of one flowing into the input of another.
为了创造一个电路,我们需要将门连接在一起,一个门的输出流入另一个门的输入。
To do this, we will implement a new class called Connector.
为此,我们将实现一个名为Connector的新类。
The Connector class will not reside in the gate hierarchy.
连接器类不会驻留在gate层次结构中。
It will, however, use the gate hierarchy in that each connector will have two gates, one on either end (see Figure 12).
然而,它将使用门层次结构,因为每个连接器将有两个门,在两端各一个(参见图12)。
This relationship is very important in object-oriented programming.
这种关系在面向对象编程中非常重要。
It is called the HAS-A Relationship.
它被称为HAS-A关系。
Recall earlier that we used the phrase “IS-A Relationship” to say that a child class is related to a parent class, for example UnaryGate IS-A LogicGate.
回想一下前面我们使用短语“IS - A - Relationship”来表示子类与父类相关,例如UnaryGate IS- a LogicGate。
Figure 12: A Connector Connects the Output of One Gate to the Input of Another
图12:连接器将一个门的输出连接到另一个门的输入
Now, with the Connector class, we say that a Connector HAS-A LogicGate meaning that connectors will have instances of the LogicGate class within them but are not part of the hierarchy.
现在,对于Connector类,我们说连接器具有- a LogicGate,这意味着连接器将在其内部具有LogicGate类的实例,但不是层次结构的一部分。
When designing classes, it is very important to distinguish between those that have the IS-A relationship (which requires inheritance) and those that have HAS-A relationships (with no inheritance).
在设计类时,区分具有IS-A 关系(这需要继承)和具有HAS-A关系(没有继承)的类是非常重要的。
Listing 12 shows the Connector class.
清单12显示了连接器类。
The two gate instances within each connector object will be referred to as the fromgate and the togate, recognizing that data values will “flow” from the output of one gate into an input line of the next.
每个连接器对象中的两个gate实例将被称为fromgate和togate,认识到数据值将从一个gate的输出“流动”到下一个gate的输入行。
The call to setNextPin is very important for making connections (see Listing 13).
对setNextPin的调用对于创建连接非常重要(请参见清单13)。
We need to add this method to our gate classes so that each togate can choose the proper input line for the connection.
我们需要将这个方法添加到gate类中,以便每个togate可以为连接选择适当的输入行。
Listing 12
清单12
class Connector:
def __init__(self, fgate, tgate):
self.fromgate = fgate
self.togate = tgate
tgate.setNextPin(self)
def getFrom(self):
return self.fromgate
def getTo(self):
return self.togate
In the BinaryGate class, for gates with two possible input lines, the connector must be connected to only one line.
在BinaryGate类中,对于有两个可能的输入线的门,连接器必须只连接到一条线。
If both of them are available, we will choose pinA by default.
如果两者都可用,我们将默认选择pinA。
If pinA is already connected, then we will choose pinB.
如果pinA已经连接,那么我们将选择pinB。
It is not possible to connect to a gate with no available input lines.
没有可用输入线路的情况下,是不可能连接到门的。
Listing 13
清单13
def setNextPin(self,source):
if self.pinA == None:
self.pinA = source
else:
if self.pinB == None:
self.pinB = source
else:
raise RuntimeError("Error: NO EMPTY PINS")
Now it is possible to get input from two places: externally, as before, and from the output of a gate that is connected to that input line.
现在可以从两个地方获得输入:外部,像以前一样,和从连接到输入线的门的输出。
This requires a change to the getPinA and getPinB methods (see Listing 14).
这需要对getPinA和getPinB方法进行更改(参见清单14)。
If the input line is not connected to anything (None), then ask the user externally as before.
如果输入线没有连接到任何东西(None),那么像之前一样向外部用户询问。
However, if there is a connection, the connection is accessed and fromgate’s output value is retrieved.
但是,如果存在连接,则访问连接并检索fromgate的输出值。
This in turn causes that gate to process its logic.
这反过来导致门处理它的逻辑。
This continues until all input is available and the final output value becomes the required input for the gate in question.
这一过程一直持续到所有输入都可用,并且最终的输出值成为相关门所需的输入。
In a sense, the circuit works backwards to find the input necessary to finally produce output.
从某种意义上说,电路反向工作以找到最终产生输出所需的输入。
Listing 14
清单14
def getPinA(self):
if self.pinA == None:
return input("Enter Pin A input for gate " + self.getLabel()+"-->")
else:
return self.pinA.getFrom().getOutput()
The following fragment constructs the circuit shown earlier in the section:
下面的片段构造了本节前面所示的电路:
>>> g1 = AndGate("G1")
>>> g2 = AndGate("G2")
>>> g3 = OrGate("G3")
>>> g4 = NotGate("G4")
>>> c1 = Connector(g1,g3)
>>> c2 = Connector(g2,g3)
>>> c3 = Connector(g3,g4)
The outputs from the two AND gates (g1 and g2) are connected to the OR gate (g3) and that output is connected to the NOT gate (g4).
两个AND门(g1和g2)的输出接到OR门(g3), OR门的输出接到NOT门(g4)。
The output from the NOT gate is the output of the entire circuit.
NOT门的输出是整个电路的输出。
For example:
例如:
>>> g4.getOutput()
Pin A input for gate G1-->0
Pin B input for gate G1-->1
Pin A input for gate G2-->1
Pin B input for gate G2-->1
0
Try it yourself using ActiveCode 4.
您可以使用ActiveCode 4自己尝试一下。
AcitiveCode 4
class LogicGate:
def __init__(self,n):
self.name = n
self.output = None
def getLabel(self):
return self.name
def getOutput(self):
self.output = self.performGateLogic()
return self.output
class BinaryGate(LogicGate):
def __init__(self,n):
super(BinaryGate, self).__init__(n)
self.pinA = None
self.pinB = None
def getPinA(self):
if self.pinA == None:
return int(input("Enter Pin A input for gate "+self.getLabel()+"-->"))
else:
return self.pinA.getFrom().getOutput()
def getPinB(self):
if self.pinB == None:
return int(input("Enter Pin B input for gate "+self.getLabel()+"-->"))
else:
return self.pinB.getFrom().getOutput()
def setNextPin(self,source):
if self.pinA == None:
self.pinA = source
else:
if self.pinB == None:
self.pinB = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class AndGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
class OrGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a ==1 or b==1:
return 1
else:
return 0
class UnaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pin = None
def getPin(self):
if self.pin == None:
return int(input("Enter Pin input for gate "+self.getLabel()+"-->"))
else:
return self.pin.getFrom().getOutput()
def setNextPin(self,source):
if self.pin == None:
self.pin = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class NotGate(UnaryGate):
def __init__(self,n):
UnaryGate.__init__(self,n)
def performGateLogic(self):
if self.getPin():
return 0
else:
return 1
class Connector:
def __init__(self, fgate, tgate):
self.fromgate = fgate
self.togate = tgate
tgate.setNextPin(self)
def getFrom(self):
return self.fromgate
def getTo(self):
return self.togate
def main():
g1 = AndGate("G1")
g2 = AndGate("G2")
g3 = OrGate("G3")
g4 = NotGate("G4")
c1 = Connector(g1,g3)
c2 = Connector(g2,g3)
c3 = Connector(g3,g4)
print(g4.getOutput())
main()
Self Check
自我检测
Create a two new gate classes, one called NorGate the other called NandGate.
创建两个新的gate类,一个称为NorGate,另一个称为NandGate。
NandGates work like AndGates that have a Not attached to the output.
nandgate的工作方式与andgate类似,与输出没有关联。
NorGates work lake OrGates that have a Not attached to the output.
NorGates工作的湖门有一个不附在输出。
Create a series of gates that prove the following equality NOT (( A and B) or (C and D)) is that same as NOT( A and B ) and NOT (C and D).
创建一系列门,证明下列等式NOT((a和B)或(C和D))与NOT(a和B)和NOT(C和D)相同。
Make sure to use some of your new gates in the simulation.
确保在模拟中使用一些新的门。
Computer science is the study of problem solving.
计算机科学是研究解决问题的科学。
Computer science uses abstraction as a tool for representing both processes and data.
计算机科学使用抽象作为一种工具来表示过程和数据。
Abstract data types allow programmers to manage the complexity of a problem domain by hiding the details of the data.
抽象数据类型允许程序员通过隐藏数据的细节来管理问题领域的复杂性。
Python is a powerful, yet easy-to-use, object-oriented language.
Python是一种功能强大、易用、面向对象的语言。
Lists, tuples, and strings are built in Python sequential collections.
列表、元组和字符串都是用Python顺序集合构建的
Dictionaries and sets are nonsequential collections of data.
字典和集合是数据的非顺序集合。
Classes allow programmers to implement abstract data types.
类允许程序员实现抽象数据类型。
Programmers can override standard methods as well as create new methods.
程序员可以重写标准方法,也可以创建新的方法。
Classes can be organized into hierarchies.
类可以组织成层次结构。
A class constructor should always invoke the constructor of its parent before continuing on with its own data and behavior.
类的构造函数在继续处理自己的数据和行为之前,应该总是调用父类的构造函数。
abstract data type抽象数据类型
abstraction抽象
algorithm算法
class类
computable可计算的
data abstraction数据抽象
data structure数据结构
data type数据类型
deep equality深相等
dictionary字典
encapsulation封装
exception异常
format operator格式化操作
formatted strings格式化字符串
HAS-A relationship HAS-A关系
implementation-independent独立性实现
information hiding信息隐藏
inheritance继承
inheritance hierarchy继承体系
interface界面
IS-A relationship IS-A关系
list列表
list comprehension列表推导
method方法
mutability易变性
object对象
procedural abstraction过程抽象
programming编程
prompt提示符
self自己
shallow equality浅相等
simulation模拟
string字符串
subclass子类
superclass父类
truth table真值表