week 1 notes
<Control>-P
(previous) <Control>-N
(next)<Control>-D
exits a session
# import lib from specific url
from urllib.request import urlopen
编程语言通常会提供三种方式来生成复杂的指令:
表达式包括两种:
call exressions 是复合表达式的一种,是 $f(x)$ 的一般形式,指计算(mapping)的过程。其特点:
name 是一种用于关联 computational objects 的方式。name 可以通过与该对象绑定(binding)的方式来与其关联,从而达到通过 name 来操作该对象的目的。name 的绑定方式有 3 种:
由于这种绑定关系的的存在,计算机需要分配一部分空间存储这些 name, object 以及关系。被分配的空间被称为环境(environment)。下面是 python 的示例:
#binding example
#import name with bound function
>>>from math import sqrt
>>>sqrt(256)
#assign name to a value
>>> radius = 10
#assgin name to an function
>>> max
#<built-in function max>
>>>f = max
#define a function
def square(x):
return x*x
>>> area, circumference = pi * radius * radius, 2 * pi * radius
>>> area
314.1592653589793
>>> radius = 11
>>> area
314.1592653589793
#another assignment needed for updating the value of area
>>> area = pi * radius * radius
380.132711084365
x
,y
先被取值,再进行了重新绑定:
>>> x, y = 3, 4.5
>>> y, x = x, y
>>> x
4.5
>>> y
3
基础的解释顺序为:
特点是:要计算主表达式,就得先计算出其子表达式的值。这种评估的过程被称为递归(recursive)
如果 operand 是子表达式(sub-expression),那么对该表达式按基础顺序继续执行,直到得到值(value)。例子如下:
该结构被称为表达式树(expression tree)
注意 CS 中树的构建是从上往下的,也就意味着求出根表达式需要求出其节点的值。而相比一半的算数式子,表达式的结构非常有利于解释执行(解释)的顺序,尤其是在多层嵌套的前提下。
# the operation does not return any values, but changes value of x
x = 3
纯函数(pure function)指有输入(argument)并会产生输出 (the result) 的函数,比如:
>>> abs(-2)
2
非纯函数(Non-pure functions)指应用时带有“副作用(side effects)” 的函数(通常指改变解释器状态)。打印是一种常见的副作用:
>>> print(1, 2, 3)
1 2 3
实际上,print()
函数的打印值并不是其返回值。其返回值是 NoneType
类型的,值为 None
的变量:
>>> print(print(1), print(2))
1
2
None None
自定义函数是抽象过程中更加高级的做法。这种做法允许我们将 name 绑定到指定的复合指令集合上。比如这段程序:
>>> def square(x):
return mul(x, x)
实际上使用了 square
来抽象了让 x
自己乘以自己的过程。此处的 x
被称为 formal parameter,x
为自乘的数据提供了 name。
def <name>(<formal parameters>):
return <return expression>
environments 通过一系列的 frame 来衡量表达式。以 frame 为单位,每个 frame 内部都会存储对应的 name,以及该 name 绑定的 value.
在 python 中,默认存在一个全局(golbal)frame。赋值 / 外部导入的 name 都会存储在这个 frame 中:
function 的 binding (无论是导入的,还是自定义的),也存储在这里:
我们将存储在 frame 中的 name 称为 bound name,而出现在函数内部的,同样的 name 称为 intrinsic name。每个函数可以同时存在多个 bound name,但 intrinsic name 有且只有一个。
考虑以下函数:
from operator import add, mul
def square(x):
return mul(x, x)
def sum_squares(x, y):
return add(square(x), square(y))
result = sum_squares(5, 12)
sum_squares
. 此时 sum_squares
与自定函数绑定,其关系存储在 global frame 中。sum_square()
,也就是 result = sum_squares(5, 12)
这一步时,此时:sum_suqare()
专属的 local framesum_suqare()
内部 operand 的绑定关系。此处是 square()
。需要注意的是这里的 operator add()
是 built-in(外部导入的),因此其绑定存储在 global frame 中,而不是 sun_square()
的 local frame 中square()
,因此针对该函数的绑定,需要再创建一个 Local frame 进行存储x
,即便 name 相同,根据其所在的 local frame, 其绑定的 value 也不同。
函数的实现与其 formal parameter 无关。比如下面的两个函数实际上是同一个:
>>> def square(x):
return mul(x, x)
>>> def square(y):
return mul(y, y)
也就是说,formal parameter 的 name 只会在函数体内部生效。这样的设计的好处:
我们将 local name 生效的区域(函数体)成为它的 scope。如果某个 name 无法被访问,那么我们就称其 out of scope。
print()
),或是得到的结果(比如 max
)
从之前的例子可以看到,我们在不考虑 square()
如何实现的情况下使用 sum_squares()
函数。我们将 square()
这种处于另一个函数内部的函数实现称之为函数抽象(functional abstraction)。从这点上来看,下面两个函数是没有区别的:
# these two functions are indistinguishable becuase of their return value
>>> def square(x):
return mul(x, x)
>>> def square(x):
return mul(x, x-1) + x
函数抽象需要考虑三个核心的属性:
/ /
是 floor division, /
是一般除法,分别对应 floordiv()
和 truediv()
函数设计应该遵循一个 idea:
functions are abstractions.
具体的来说:
square()
函数,我们应该实现的是 pow()
函数。因为指数运算包含了平方运算。python 中通常包括了函数的描述,这类 documentation 被称为 docstring:
>>> def pressure(v, t, n):
"""Compute the pressure in pascals of an ideal gas.
Applies the ideal gas law: http://en.wikipedia.org/wiki/Ideal_gas_law
v -- volume of gas, in cubic meters
t -- absolute temperature in degrees kelvin
n -- particles of gas
"""
k = 1.38e-23 # Boltzmann's constant
return n * k * t / v
还有一类以 #
起头的信息被称为 Comments。
Docstring Conventions
statement 与 expression 在本质上有不同:
statement 意味着应用更改:比如赋值,返回等等。exrepssion 在被评估的时候也可以被视作 statement,但其生成的结果会丢失。比如:
>>> def square(x):
mul(x, x) # Watch out! This call doesn't return a value
如果希望应用修改(返回返回值),则需要使用 return
statement:
>>> def square(x):
return mul(x, x)
结构如下:
<header>:
<statement>
<statement>
...
<separating header>:
<statement>
<statement>
...
...
根据上述定义,def
属于 compound statement。
上述的结构可以被视作 sequence,也就是该结构总可以分为两个部分:
这是一种递归的结构。
用户自定义的函数是在对应的 local frame 中运行的。local frame 在该函数被调用时创建;函数体中的 return
statement 会起到重定向的作用。函数什么时候结束取决于
return
statement 什么时候被执行赋值语句可以处于函数内部。任何函数内部的赋值,其绑定信息都存储于local frame,对外部的 name 不造成任何影响。
if <expression>:
<suite>
elif <expression>:
<suite>
else:
<suite>
while <expression>:
<suite>
python 中可以使用 assert
statement 做验证。如果 assert
返回的对象为 True
,那么什么也不会发生。否则,错误会被抛出:
>>> def fib_test():
assert fib(2) == 1, 'The 2nd Fibonacci number should be 1'
assert fib(3) == 1, 'The 3rd Fibonacci number should be 1'
assert fib(50) == 7778742049, 'Error at the 50th Fibonacci number'
docstring 中可以包含 test case,用于测试:
>>> def sum_naturals(n):
"""Return the sum of the first n natural numbers.
>>> sum_naturals(10)
55
>>> sum_naturals(100)
5050
"""
total, k = 0, 1
while k <= n:
total, k = total + k, k + 1
return total
>>> from doctest import testmod
>>> testmod()
TestResults(failed=0, attempted=2)