Chapter 1 : The Way of the Program
What Is a Program?
A program is a sequence of instructions that specifies how to perform a computation.
input:
Get data from the keyboard, a file, the network, or some other device.
output:
Display data on the screen, save it in a file, send it over the network, etc.
math:
Perform basic mathematical operations like addition and multiplication.
conditional execution:
Check for certain conditions and run the appropriate code.
repetition:
Perform some action repeatedly, usually with some variation.
Chapter 2: Variables, Expressions and Statements
Order of Operations
Parentheses have the highest precedence and can be used to force an expression to evaluate in the order you want
括号(Parentheses)具有最高的优先级,并且可以强制表达式按你希望的顺序计算
Exponentiation has the next highest precedence,
指数运算(Exponentiation)具有次高的优先级,
Multiplication and Division have higher precedence than Addition and Subtraction
乘法(Multiplication)和除法(Division)有相同的优先级,比加法(Addition)和减法(Subtraction)高,加法和减法也具有相同的优先级。
Operators with the same precedence are evaluated from left to right (except exponentiation)
具有相同优先级的运算符按照从左到右的顺序进行计算
String Operations
The + operator performs string concatenation, which means it joins the strings by linking them end-to-end.
加号运算符 + 可用于 字符串拼接
|
|
The operator also works on strings; it performs repetition.
乘法运算符 也可应用于字符串;它执行重复运算
Chapter 3: Function
函数调用( function call)
|
|
函数定义( function definition)
指定了新函数的名称以及当函数被调用时执行的语句序列.
def 是一个关键字,表明这是一个函数定义, 函数的命名规则与变量名相同:字母、数字以及下划线是合法的,但是第一个字符不能是数字。
不能使用关键字作为函数名,并应该避免变量和函数同名
函数名后面的圆括号是空的,表明该函数不接受任何实参。
函数定义的第一行被称作函数头( header) ;其余部分被称作函数体( body) 。函数头必须以冒号结尾,而函数体必须缩进。按照惯例,缩进总是 4 个空格。函数体能包含任意条语句。
所有引号(单引号和双引号)必须是“直引号(straight quotes)”,它们通常位于键盘上 Enter 键的旁边。像这句话中使用的‘弯引号(curly quotes)’,在 Python 语言中则是不合法的。
函数名后面的圆括号是空的,表明该函数不接受任何实参
为了结束函数定义,你必须输入一个空行。
定义一个函数会创建一个 函数对象( function object) ,其类型是 function:
该程序包含两个函数定义: print_lyrics 和 repeat_lyrics。函数定义和其它语句一样,都会被执行,但是其作用是创建函数对象。函数内部的语句在函数被调用之前,是不会执行的,而且函数定义不会产生任何输出
函数定义不改变程序执行的流程,但是请记住,函数不被调用的话,函数内部的语句是不会执行的。
函数调用像是在执行流程上绕了一个弯路。执行流程没有进入下一条语句,而是跳入了函数体,开始执行那里的语句,然后再回到它离开的位置。
这听起来足够简单,至少在你想起一个函数可以调用另一个函数之前。当一个函数执行到中间的时候,程序可能必须执行另一个函数里的语句。然后在执行那个新函数的时候,程序可能又得执行另外一个函数!
形参( parameters
在函数内部,实参被赋给称作形参( parameters) 的变量。下面的代码定义了一个接受一个实参的函数:
这个函数将实参赋给名为 bruce 的形参。当函数被调用的时候,它会打印形参(无论它是什么)的值两次。
当你在函数里面创建变量时,这个变量是局部的( local) ,也就是说它只在函数内部存在。
形参也都是局部的。例如,在 print_twice 函数的外部并没有 bruce 这个变量.
堆栈图 stack diagram
可以帮助你跟踪哪个变量能在哪儿用。与状态图类似,堆栈图要说明每个变量的值,但是它们也要说明每个变量所属的函数。
每个函数用一个栈帧( frame) 表示。一个栈帧就是一个线框,函数名在旁边,形参以及函数内部的变量则在里面
这些线框排列成栈的形式,说明了哪个函数调用了哪个函数等信息。在此例中,print_twice 被 cat_twice 调用, cattwice 又被 main 调用,
main 是一个表示最上层栈帧的特殊名字。当你在所有函数之外创建一个变量时,它就属于 __main.
每个形参都指向其对应实参的值。因此, part1 和 line1 的值相同, part2 和 line2 的值相同, bruce 和 cat 的值相同。
如果函数调用时发生错误, Python 会打印出错函数的名字以及调用它的函数的名字,以及调用 后面这个函数的函数的名字,一直追溯到 main 为止
|
|
这个函数列表被称作回溯( traceback) 。它告诉你发生错误的是哪个程序文件,错误在哪一行,以及当时在执行哪个函数。它还会显示引起错误的那一行代码。
有返回值函数和无返回值函数 fruitful functions void functions
无返回值函数可能在屏幕上打印输出结果,或者产生其它的影响,但是它们并没有返回值。如果你试图将无返回值函数的结果赋给一个变量,你会得到一个被称作 None 的特殊值
|
|
为什么写函数
• 创建一个新的函数可以让你给一组语句命名,这可以让你的程序更容易阅读和调试。
• 通过消除重复的代码,函数精简了程序。以后,如果你要做个变动,你只需在一处修改即可。
• 将一个长程序分解为多个函数,可以让你一次调试一部分,然后再将它们组合为一个可行的整体。
• 设计良好的函数经常对多个程序都有帮助。一旦你写出并调试好一个函数,你就可以重复使用它。
chapter 4
方法(method):与对象相关联的函数,并使用点标记法(dot notation)调用。
循环(loop):程序中能够重复执行的那部分代码。
封装(encapsulation):将一个语句序列转换成函数定义的过程。
泛化(generalization):使用某种可以算是比较通用的东西(像变量和形参),替代某些没必要那么具体的东西(像一个数字)的过程。
关键字实参(keyword argument):包括了形参名称作为“关键字”的实参。
接口(interface):对如何使用一个函数的᧿述,包括函数名、参数说明和返回值。
重构(refactoring):修改一个正常运行的函数,改善函数接口及其他方面代码质量的过程。
开发计划(development plan):编写程序的一种过程。
文档字符串(docstring):出现在函数定义顶部的一个字符串,用于记录函数的接口。
先决条件(preconditions):在函数运行之前,调用者应该满足的要求。 ends.
后置条件(postconditions):函数终止之前应该满足的条件
chapter 5 条件和递归
地板除运算符 (floor division operator) // 先做除法,然后将结果保留到整数。
求余运算符 (modulus operator) % ,它会将两个数相除,返回余数布尔表达式( boolean expression) 的结果要么为真要么为假。下面的例子使用 == 运算符。它比较两个运算数,如果它们相等,则结果为 True ,否则结果为 False
True 和 False 是属于 bool 类型的特殊值;它们不是字符串
有三个逻辑运算符( logical operators) : and 、 or 和 not。not 运算符对一个布尔表达式取反
if 之后的布尔表达式被称作条件( condition) 。如果它为真,则缩进的语句会被执行。如果不是,则什么也不会发生。
if 语句和函数定义有相同的结构:一个语句头跟着一个缩进的语句体。类似的语句被称作复合语句( compound statements) 。if 语句的第二种形式是二选一执行( alternative execution) ,此时有两个可能的选择,由条件决定执行哪一个
|
|
- 多于两个的分支。表示像这样的计算的方法之一是链式条件( chained conditional)
|
|
|
|
程序将按顺序逐个检测条件,如果第一个为假,检测下一个,以此类推。如果它们中有一个为真,相应的分支被执行,并且语句结束。即便有不止一个条件为真,也只执行第一个为真的分支。
- 嵌套条件 1234567if x == y:print('x and y are equal')else:if x < y:print('x is less than y')else:print('x is greater than y')
|
|
- 递归123456def countdown(n):if n <= 0:print('Blastoff!')else:print(n)countdown(n-1)
如果 n 是 0 或负数,程序输出单词“Blastoff!”。否则,它输出 n 然后调用一个名为countdown 的函数—即它自己—传递 n-1 作为实参
每当一个函数被调用时, Python 生成一个新的栈帧,用于保存函数的局部变量和形参。
对于一个递归函数,在堆栈上可能同时有多个栈帧
通常,堆栈的顶部是 main 栈帧。因为我们在 main 中没有创建任何变量,也没有传递任何实参给它,所以它是空的。
对于形参 n,四个 countdown 栈帧有不同的值。 n=0 的栈底,被称作基础情形( basecase) 。它不再进行递归调用了,所以没有更多的栈帧了
- Python提供了一个内建函数 input ,可以暂停程序运行,并等待用户输入。当用户按下回车键 (Return or Enter),程序恢复执行, input 以字符串形式返回用户键入的内容。
|
|
Chapter 6 有返回值的函数
调用一个有返回值的函数会生成一个返回值,我们通常将其赋值给某个变量或是作为表达式的一部分。
|
|
另一方面,像 a 这样的 临时变量( temporary variables) 能使调试变得更简单。
增量式开发( incremental development ) 的方法。增量式开发的目标,是通过每次只增加和测试少量代码,来避免长时间的调试。
|
|
函数可以返回布尔值(booleans),通常对于隐藏函数内部的复杂测试代码非常方便
Chapter 7 迭代
此外,数学中,相等命题不是对的就是错的。如果 a = b,那么 a 则是永远与 b 相等。在Python 中,赋值语句可以使得两个变量相等,但是这两个变量不一定必须保持这个状态
- 更新变量
如果试图去更新一个不存在的变量,则会返回一个错误。这是因为 Python 是先求式子右边的值,然后再把所求的值赋给 x:
在更新变量之前,你得先 初始化( initialize) 它,通常是通过一个简单的赋值实现
|
|
- while 语句
|
|
Chapter 8 字符串
- 括号中的表达式被称作 索引 (index) 。索引指出在序列中你想要哪个字符
字符串是由字符组成的序列。你可以用括号运算符一次访问一个字符
|
|
索引值必须使用整数
len 是一个内建函数,其返回字符串中的字符数量
出现 IndexError 的原因,是在 ’banana’ 中没有索引为 6 的字母。由于我们从 0 开始计数,六个字母的编号是 0 到 5。
- 遍历 (traversal) 遍历的方法之一是使用 while 循环:12345index = 0while index < len(fruit):letter = fruit[index]print(letter)index = index + 1
- 12for letter in fruit:print(letter)
- 1234prefixes = 'JKLMNOPQ'suffix = 'ack'for letter in prefixes:print(letter + suffix)
- 12345'Monty Python's =0:5]s['Monty'6:12]s['Python'
- 1234'Hello world!'greeting ='J' + greeting[1:]new_greeting =new_greeting'Jello world!'
- 12345def find(word letter):index = 0while index < len(word):if word[index] == letter:return index
这是我们第一次在循环内部看见 return 语句。如果 word[index] == letter ,函数停止循环并马上返回。
如果字符没出现在字符串中,那么程序正常退出循环并返回 -1。
这种计算模式——遍历一个序列并在找到寻找的东西时返回——被称作 搜索 (search)
- 字符串方法
字符串ᨀ供了可执行多种有用操作的 方法 (method) 。方法和函数类似,接受实参并返回一个值,但是语法不同。
方法调用 (invocation) ;在此例中,我们可以说是在 word 上调用 upper
|
|
|
|
- in 运算符
单词 in 是一个布尔运算符,接受两个字符串。如果第一个作为子串出现在第二个中,则返回 True:
|
|
- 字符串比较
|
|
|
|
chapter 10 列表
- 列表是一个序列
与字符串类似, 列表是由多个值组成的序列。在字符串中,每个值都是字符;在列表中,值可以是任何数据类型。列表中的值称为 元素( element) ,有时也被称为 项( item)
创建新列表的方法有多种;最简单的方法是用方括号 ( [ 和 ] ) 将元素包括起来
一个列表在另一个列表中,称为 嵌套(nested)列表
一个不包含元素的列表被称为空列表;你可以用空的方括号 [] 创建一个空列表。
正如你想的那样,你可以将列表的值赋给变量
|
|
|
|
和字符串不同的是,列表是可变的。当括号运算符出现在赋值语句的左边时,它就指向了列表中将被赋值的元素
|
|
|
|
- 遍历列表12for i in range(len(numbers)):numbers[i] = numbers[i] * 2
- 列表操作
+运算符拼接多个列表, 运算符 * 以给定次数的重复一个列表.
- 1234567>>> t = ['a', 'b', 'c', 'd', 'e', 'f']1:3]t[['b', 'c']4]t[:['a', 'b', 'c', 'd']3:]t[['d', 'e', 'f']
如果你省略第一个索引,切片将从列表头开始。如果你省略第二个索引,切片将会到列表尾结束。所以如果你两者都省略,切片就是整个列表的一个拷贝。
列表方法
123456789101112131415'a', 'b', 'c']t = ['d')t.append(t['a', 'b', 'c', 'd']'a', 'b', 'c']t1 = ['d', 'e']t2 = [t1.extend(t2)t1['a', 'b', 'c', 'd', 'e']'d', 'c', 'e', 'b', 'a']t = [t.sort()t['a', 'b', 'c', 'd', 'e']
sort 将列表中的元素从小到大进行排序
- 映射、筛选和归并 12345def add_all(t):total = 0for x in t:total += xreturn total
增量赋值语句( augmented assignment statement)
total = total + x
当循环执行时, total 将累计元素的和;一个这样的变量有时被称为 累加器( accumulator) 。
把一个列表中的元素加起来是一个很常用的操作,所以 Python 将其设置为一个内建内置函数 sum
|
|
- 123456789'banana'a ='banana'b =is baTrue1, 2, 3]a = [1, 2, 3]b = [is baFalse
在这个例子中,我们称这两个列表是 相等( equivalent) 的,因为它们有相同的元素。
但它们并不 相同( identical) ,因为他们不是同一个对象。如果两个对象 相同,它们也是
相等的,但是如果它们是相等的,它们不一定是相同的
- pop 修改列表,并返回被移除的元素。如果你不ᨀ供下标,它将移除并返回最后一个元素。
如果你不需要被移除的元素,可以使用 del 运算符:
如果你知道要删除的值(但是不知道其下标),你可以使用 remove
|
|
- 列表和字符串
可以使用 list 将一个字符串转换为字符的列表
|
|
|
|
- 别名
|
|
变量和对象之间的关联称为 引用( reference)
如果一个对象有多于一个引用,那它也会有多个名称,我们称这个对象是 有别名的( aliased)
- 12def bad_delete_head(t):t = t[1:]
|
|
|
|
在 bad_delete_head 的开始处, t 和 t4 指向同一个列表。在结束时, t 指向一个新列表,但是 t4 仍然指向原来的、没有被改动的列表。
一个替代的写法是,写一个创建并返回一个新列表的函数。例如, tail 返回列表中除了第一个之外的所有元素:
通过创建拷贝来避免别名
chapter 11 字典
字典包含了一个索引的集合,被称为 键( keys) ,和一个值 (values) 的集合。一个键对应一个值。这种一一对应的关联被称为 键值对( key-value pair) ,有时也被称为 项( item)。
在数学语言中,字典表示的是从键到值的 映射,所以你也可以说每一个键“映射到”一个值。举个例子,我们接下来创建一个字典,将英语单词映射至西班牙语单词,因此键和值都是字符串。
dict 函数生成一个不含任何项的新字典。由于 dict 是内建函数名,你应该避免使用它来命名变量。花括号 {} 表示一个空字典。你可以使用方括号向字典中增加项
想要知道字典中是否存在某个值,你可以使用 values 方法,它返回值的集合,然后你可以使用 in 操作符来验证:
- 12345678def histogram(s):d = dict()for c in s:if c not in d:d[c] = 1else:d[c] += 1return d
实现是指执行某种计算的方法;有的实现更好。例如,使用字典的实现有一个优势,即我们不需要事先知道字符串中有几种字母,只要在出现新字母时分配空间就好了
字典中的键是无序的。如果要以确定的顺序遍历字典,你可以使用内建方法sorted:
|
|
- 逆向查找
|
|
- 倒转字典
|
|
哈希( hash) 函数接受一个值(任何类型)并返回一个整数。字典使用被称作哈希值的这些整数,来存储和查找键值对
known 是在函数的外部创建的,因此它属于被称作 main 的特殊帧。因为 main 中的变量可以被任何函数访问,它们也被称作 全局变量( global) 。与函数结束时就会消失的局部变量不同,不同函数调用时全局变量一直都存在
全局变量普遍用作 标记( flag) ;也就是说明(标记)一个条件是否为真的布尔变量
要在函数内对全局变量重新赋值,你必须在使用之前 声明 (declare) 该全局变量:
chapter 12 tuple 元组
- 元组是一组值的序列。其中的值可以是任意类型,使用整数索引,因此从这点上看,元组与列表非常相似。二者不同之处在于元组的不可变性
如果实参是一个序列(字符串、列表或者元组),结果将是一个包含序列内元素的元组
|
|
关系型运算符也适用于元组和其他序列; Python 会首先比较序列中的第一个元素,如果它们相等,就继续比较下一组元素,以此类推,直至比值不同。其后的元素(即便是差异很大)也不会再参与比较
|
|
- 元组赋值
等号左侧是变量组成的元组;右侧是表达式组成的元组。每个值都被赋给了对应的变量。变量被重新赋值前,将先对右侧的表达式进行求值。
左侧的变量数和右侧值的数目必须相同
- 元组作为返回值
一个函数只能返回一个值,但是如果这个返回值是元组,其效果等同于返回多个值。
|
|
- 可变长度参数元组
zip 对象是迭代器的一种,即任何能够按照某个序列迭代的对象。迭代器在某些方面与列表非常相似,但不同之处在于,你无法通过索引来选择迭代器中的某个元素
- 列表和元组
zip 是一个内建函数,可以接受将两个或多个序列组,并返回一个元组列表,其中每个元组包含了各个序列中相对位置的一个元素
|
|
如果将 zip 、 for 循环和元组赋值结合起来使用,你会得到一个可以同时遍历两个(甚至多个)序列的惯用法
|
|
enumerate 的返回结果是一个枚举对象(enumerate object),可迭代一个包含若干个对的序列;每个对包含了(从 0 开始计数)的索引和给定序列中的对应元素
如果你想使用一个序列作为字典的键,那么你必须使用元组或字符串这样的不可变类型。
如果你向函数传入一个序列作为参数,那么使用元组可以降低由于别名而产生的意外行为的可能性。
在一些情况下(例如 return 语句),从句式上生成一个元组比列表要简单
在很多情况下,不同类型的序列(字符串、列表、元组)可以互换使用。因此,我们该如何选用合适的序列呢?
首先,显而易见的是,字符串比其他序列的限制更多,因为它的所有元素都必须是字符,且字符串不可变。如果你希望能够改变字符串中的字符,使用列表嵌套字符或许更合适。列表比元组更常用,主要是因为它们是可变的。但是有些情况下,你可能更倾向于使用元组: