Chapter 1 - Building Abstractions with Functions (Sec1.1-1.4)
0 写在前面
其实CS61A的学习,在大一上学期就开始了,寒假时写完了第二个project,之后搁置,其实期间学的也比较断断续续,笔记也不太完整,今天下学期的期末考试都结束了,这门课程半途而废不大好,所以打算结合reading和之前比较零散的笔记再做一点复习,之后把这门课学完。
希望自己能做下去,我想这也许是对我自己的一种肯定,某种意义上吧。
课程的教材写的很好,所以文章不少内容是复制的教材的原文,但也经过了个人的整理,不至于像教材那样那么长
1 Getting Started
The high productivity of computer science is only possible because the discipline is built upon an elegant and powerful set of fundamental ideas. All computing begins with representing information, specifying logic to process it, and designing abstractions that manage the complexity of that logic.
Mastering these fundamentals will require us to understand precisely how computers interpret computer programs and carry out computational processes.
1.1 Programming in Python
In order to define computational processes, we need a programming language; preferably one that many humans and a great variety of computers can all understand. In this text, we will work primarily with the Python language.
1.2 Errors
Learning to interpret errors and diagnose the cause of unexpected errors is called debugging. Some guiding principles of debugging are:
-
Test incrementally: Every well-written program is composed of small, modular components that can be tested individually. Try out everything you write as soon as possible to identify problems early and gain confidence in your components.
-
Isolate errors: An error in the output of a statement can typically be attributed to a particular modular component. When trying to diagnose a problem, trace the error to the smallest fragment of code you can before trying to correct it.
-
Check your assumptions: Interpreters do carry out your instructions to the letter — no more and no less. Know your assumptions, then focus your debugging effort on verifying that your assumptions actually hold.
-
Consult others: You are not alone!
2 Elements of Programming
A programming language is more than just a means for instructing a computer to perform tasks. The language also serves as a framework within which we organize our ideas about computational processes.
Every powerful language has three such mechanisms:
- primitive expressions and statements, which represent the simplest building blocks that the language provides,
- means of combination, by which compound elements are built from simpler ones, and
- means of abstraction, by which compound elements can be named and manipulated as units.
In programming, we deal with two kinds of elements: functions and data. Informally, data is stuff that we want to manipulate, and functions describe the rules for manipulating the data.
2.1 Expressions
One kind of primitive expression is a number.
1 | 42 |
Expressions representing numbers may be combined with mathematical operators to form a compound expression, which the interpreter will evaluate:
1 | -1 - -1 |
2.2 Call Expressions
A call expression, which is the most important kind of compound expression, applies a function to some arguments.
1 | max(7.5, 9.5) |
This call expression has subexpressions: the operator is an expression that precedes parentheses, which enclose a comma-delimited(逗号分隔的) list of operand expressions.
The operator specifies a function. When this call expression is evaluated, we say that the function max is called with arguments 7.5 and 9.5, and returns a value of 9.5.
2.3 Importing Library Functions
1 | from math import sqrt |
2.4 Names and the Environment
A critical aspect of a programming language is the means it provides for using names to refer to computational objects. If a value has been given a name, we say that the name binds to the value.
1 | ''' |
The possibility of binding names to values and later retrieving those values by name means that the interpreter must maintain some sort of memory that keeps track of the names, values, and bindings. This memory is called an environment.
Names can also be bound to functions.
In Python, names are often called variable names or variables because they can be bound to different values in the course of executing a program. When a name is bound to a new value through assignment, it is no longer bound to any previous value. One can even bind built-in names to new values.(比如max赋值为6)
1 | max(3,4) |
When executing an assignment statement, Python evaluates the expression to the right of = before changing the binding to the name on the left.
1 | x=6 |
With multiple assignment, all expressions to the right of = are evaluated before any names to the left are bound to those values.
1 | x=6 |
2.5 Evaluating Nested Expressions
To evaluate a call expression, Python will do the following:
- Evaluate the operator and operand subexpressions, then
- Apply the function that is the value of the operator subexpression to the arguments that are the values of the operand subexpressions.
反复执行第一步,最后我们只会碰到最原始的表达式,例如一个数字或者name,我们这样处理:
- A numeral evaluates to the number it names (we actually mean that the Python interpreter evaluates a numeral to a number.)
- A name evaluates to the value associated with that name in the current environment.

In general, statements are not evaluated but executed; they do not produce a value but instead make some change.
2.6 The Non-Pure Print Function
1 | 5 |
The value that print returns is always None, a special Python value that represents nothing.
In the case of print, the function itself is printing output as a side effect of being called.
A function that doesn’t explicitly return a value will return None.
3 Defining New Functions
We have identified in Python some of the elements that must appear in any powerful programming language:
- Numbers and arithmetic operations are primitive built-in data values and functions.
- Nested function application provides a means of combining operations.
- Binding names to values provides a limited means of abstraction.
Function is a much more powerful abstraction technique by which a name can be bound to compound operation, which can then be referred to as a unit.
1 | def twice(x): |
We can also use twice as a building block in defining other functions.
1 | def sum_square(x, y): |
User-defined functions are used in exactly the same way as built-in functions.
Both def statements and assignment statements bind names to values, and any existing bindings are lost.
1 | def g(): |
3.1 Environments
An environment in which an expression is evaluated consists of a sequence of frames, depicted as boxes. Each frame contains bindings, each of which associates a name with its corresponding value. There is a single global frame. Assignment and import statements add entries to the first frame of the current environment. So far, our environment consists only of the global frame.
The environment diagram shows the bindings of the current environment, along with the values to which names are bound.
Online Python Tutor, 非常好的可视化environment diagram
用这个代码试一下
1 | from operator import add |
The name of a function is repeated twice, once in the frame and again as part of the function itself. The name appearing in the function is called the intrinsic name. The name in a frame is a bound name.
The name bound to a function in a frame is the one used during evaluation. The intrinsic name of a function does not play a role in evaluation.
例如,上例中的a函数在定义时,a函数被绑定到名字a,同时这个函数的intrinsic name就是a,之后a函数被绑定到名字b
3.2 Calling User-Defined Functions
To evaluate a call expression whose operator names a user-defined function, the Python interpreter follows a computational process.
Applying a user-defined function introduces a second local frame, which is only accessible to that function. To apply a user-defined function to some arguments:
- Bind the arguments to the names of the function’s formal parameters in a new local frame.
- Execute the body of the function in the environment that starts with this frame.
Name Evaluation. A name evaluates to the value bound to that name in the earliest(注意这里最早找到的意思是从本地逐渐往上找(即往parent找)第一个找到的) frame of the current environment in which that name is found.
试试这个,需要高阶函数的知识,可以阅读后面的内容后再回来看
1 | def make_adder(n): |
注意到adder的frame,f2的parent是f1,所以在找n时,先在f2里找,找不到,去f1找,得到n=1
而在evaluate make_adder(1)时,就创建了一个local frame f1,这个frame里formal parameter n bound to the value 1
3.3 Example
1 | from operator import add, mul |
All three local frames contain a binding for the name x, but that name is bound to different values in different frames. Local frames keep these names separate.
3.4 Local Names
We say that the scope of a local name is limited to the body of the user-defined function that defines it. When a name is no longer accessible, it is out of scope.
3.5 Choosing Names
Lowercase, with words separated by underscores
3.6 Functions as Abstractions
这里对abstraction的理解已经初现端倪
We can write sum_squares without concerning ourselves with how to square a number. The details of how the square is computed can be suppressed, to be considered at a later time.
square is not a particular function body, but rather an abstraction of a function, a so-called functional abstraction. At this level of abstraction, any function that computes the square is equally good.
这个程序和3.4节的程序完全等价
1 | from operator import add, mul |
3.7 Operators
对于带有数学操作符的表达式,我们只需把它们看作是call expression,再利用call expression的evaluation procedure即可
例如(2+3)*(4+5)就等同于mul(add(2, 3), add(4, 5))
注意/结果是浮点数,//的结果是向下取整,例如-5 // 4 = -2
4 Designing Functions
Fundamentally, the qualities of good functions all reinforce the idea that functions are abstractions.
- Each function should have exactly one job. That job should be identifiable with a short name and characterizable in a single line of text. Functions that perform multiple jobs in sequence should be divided into multiple functions.
- Don’t repeat yourself is a central tenet of software engineering. The so-called DRY principle states that multiple fragments of code should not describe redundant logic. Instead, that logic should be implemented once, given a name, and applied multiple times. If you find yourself copying and pasting a block of code, you have probably found an opportunity for functional abstraction.
- Functions should be defined generally. Squaring is not in the Python Library precisely because it is a special case of the pow function, which raises numbers to arbitrary powers.
4.1 Documentation
A function definition will often include documentation describing the function, called a docstring, which must be indented(缩进) along with the function body.
1 | def kinetic_energy(m, v): |
可以在可交互解释器页面输入help(kinetic_energy)查看文档
docstring guidelines
4.2 Default Argument Values
In Python, we can provide default values for the arguments of a function. When calling that function, arguments with default values are optional. If they are not provided, then the default value is bound to the formal parameter name instead.
1 | def pressure(v, t, n=6.022e23): |


