cs61a
welcome
python ok -q python-basics -u --local没有ucb账号本地运行ok的方法
有用的命令行参数
- -i :直接进入交互模式,可以方便地查看各变量
- python -m doctest:不单独编写测试文件的情况下进行测试(Doctest 模块通过扫描你代码的文档字符串(docstrings),查找看起来像 Python 交互式会话的代码块,并执行这些代码块以验证它们的输出是否与文档中预期的输出相匹配。)还可以添加-v详细看到输出信息
- e.g
def add(a, b): |
Functions
- 任何编程语言都具有的三种强大的机制:

- 中缀表示法(infix notation),运算符(例如 +、-、* 或 /)出现在操作数之间
- 调用表达式(operator:运算符,operand:操作数)
- 导入库函数: import operator导入常见的中缀运算符
- 名称与环境(name and environment):
- 我们使用名称来引用对象
name="tom" - 名称可以通过等号和import来与值绑定
- 解释器必须要维护内存来保存名称的引用,这就叫环境(environment)
- 我们使用名称来引用对象
- 求解嵌套表达式

- 求值本质上是递归的,我们规定,数字的值是其对应的数值,名称的值是环境中其对应的所关联的对象
- 非纯函数print
- 纯函数:调用一个有输入的函数,然后仅仅返回一些值
- 非纯函数:除了返回值外,调用一个非纯函数还会产生其他改变解释器和计算机的状态的副作用(side effect)
- e.g: print:它实际上会返回一个none值,输出控制台相当于是副作用
- 环境
- 求解表达式的环境由 帧 序列组成,它们可以被描述为一些盒子。每个帧都包含了一些 绑定,它们将名称与对应的值相关联。全局 帧(global frame)只有一个。赋值和导入语句会将条目添加到当前环境的第一帧。
- 函数名称重复两次,一次在环境帧中,另一次是作为函数定义的一部分。函数定义中出现的名称叫做 内在名称(intrinsic name),帧中的名称叫做 绑定名称(bound name)。两者之间有一个区别:不同的名称可能指的是同一个函数,但该函数本身只有一个内在名称。求值使用的是绑定名称
- 对函数形式参数的描述被称为函数的签名。
- 调用用户定义的函数
- 这会产生局部帧(local frame):该局部中的实参会绑定到形参上
- 名称求解(Name Evaluation):在环境中寻找该名称,最早找到的含有该名称的帧,其里边绑定的值就是这个名称的计算结果。
- 抽象函数:
- 域(domain):可以接收的参数集合
- 范围(range):可返回的值的集合
- 意图(intent):输入输出之间的关系,并且涉及任何副作用
hw01
注意:若add是一个函数
- f=add,这f是一个函数
- f=add(a,b),f是运算结果
lab01
python异或:^
阶乘:math.factorial()
交互式python环境中:任意定义完成一个函数之后要隔一个空行才算结束,并且调用函数会直接打印返回值
control
- 设计原则(函数就是抽象):每个函数只执行一个任务、不要重复自己(don’t repeat yourself)、定义通用的函数(pow和平方函数)
- 文档字符串(docstring):以函数名的字符串为参数调用help就会看到(q以退出该状态)
def pressure(v, t, n): |
- Python 代码是一系列语句。简单语句是不以冒号结尾的单行,而由其他语句(简单语句和复合语句)组成被称为复合语句。复合语句通常跨越多行,以单行头部(header)开始,并以冒号结尾,其中冒号标识语句的类型。头部和缩进的句体(suite)一起称为子句。
- 赋值语句:在更新左侧的绑定之前求出所有 = 右侧的内容
- 测试
assert fib(8) == 13, '第八个斐波那契数应该是 13'- 通常是在一个_test.py文件中执行的
#非命令行方法的测试 |
globals()函数,返回全局变量字典
Higher OrderFunctions && Environment
- 作通用方法的函数
def improve(update, close, guess=1): |
- 嵌套定义函数
#为了解决improve函数签名不一致的问题 |

- 继承环境(Extended Environments):一个环境可以由任意长的帧链构成,并且总是以全局帧结束。在举 sqrt 这个例子之前,环境最多只包含两种帧:局部帧和全局帧。通过使用嵌套的 def 语句来调用在其他函数中定义的函数,我们可以创建更长的(帧)链。
- 局部定义的函数通常被称为闭包(closures)
牛顿法
#沿着切线近似到0 |
柯里化(Currying)
通过函数链把多参数转化为单参数

def curried_pow(x): |
lambda函数
lambda x : f(g(x)) |
装饰器(decorator)
def trace(fn): |
hw02
#递归调用要有累积+柯里化 |
hog
# *args的用法 |
lab2

def cake(): |
functional abstraction
error and traceback
three types:语法错误(syntax error)、运行时错误(runtime error)、逻辑行为错误
none是无类型的:none-none也是错的
recurssion function
- 将递归调用看作一种函数抽象这一思想,就是所谓“递归的信仰之跃(recursive leap of faith)”。我们根据函数自身来定义一个函数,但在验证函数的正确性时,我们只需相信在更简单的情况下,函数同样能正确工作。
互递归
- 两个函数互相调用
def is_even(n): |
树递归
e.g:分割树(n被最大为m的整数分割的分法总数)
def count_partitions(n, m): |
disc3
def swipe(n): |
数据抽象
- 原始数据类型:int、float、complex
- 有一些可以求解为原始数据类型的表达式,被称为字面量(literals)、有用于操作原始类型值的内置函数和操作符.
- 将“数据表示”与“数据处理”的程序隔离的通用技术是一种强大的设计方法,称为数据抽象。
- e.g:有理数(内置的float有很大的精度问题,所以可用有理数来精确表示)

- 抽象屏障:当程序中有一部分本可以使用更高级别函数但却使用了低级函数时,就会违反抽象屏障,这会增大维护的难度
hw03
#分割+特殊情况 |
序列
- 序列(sequence)是一组有顺序的值的集合,是计算机科学中的一个强大且基本的抽象概念
- 列表间的相加和相乘(就是元素附上去)
- 序列解包(Sequence unpacking):
for x,y in pairs - 列表推导式(List Comprehensions)
[<map expression> for <name> in <sequence expression> if <filter expression>] - 聚合(Aggregation):sum、min、max等都算聚合函数
高阶函数
def apply_to_all(map_fn, s): |
序列抽象
- 成员资格(Membership)in\not in
- 切片(Slicing)
- 字符串中的成员资格是查找子字符串
lab3
- 列表乘2:变长为2倍
- range生成的是可迭代对象:只是不是数组,甚至可以直接用下标取值
- 判断整数:%1==0
disc4
def max_product(s): |
cats
- 对函数的递归调用:每一帧传进去的参数不一样!!!
- 负无穷
float('-inf')
#文本处理 |
可变数据
- 基本数据类型是不可变的,复杂对象是可变的
- 区分:身份验证和相等验证(前者是验证是否同一个对象)
- 元组是不可变对象
- 字典:可用dict()方法加列表构成,其key不能是可变数据,一个key对应一个val
- get,返回key对应val或者默认值
- 也有字典推导式
- 局部状态:声明变量非局部
nonlocal balance- 只有函数调用才会返回新的帧,否则就是在这里面自己变
#如果不使用nonlocal声明变量会导致python默认它是局部变量(有赋值加减的操作) |
- 使用函数来实现链表:需要一个调度函数,其参数首先是一个期望的指令,代表期望这个函数做什么;然后是该方法的需要用到的参数。
- 也可以使用一个调度字典(包含各种各样的操作情况和非局部变量(还可以避免使用nonlocal))
约束传递
- 计算机程序传统上是被定义为单向的计算(数学上无法利用一个方程进行双向计算)
- 将程序表示为约束(声明式编程)
connector ['set_val'](source, value) """表示 source 在请求连接器将当前值设为 value""" |
lab4
- in对字典只检测键,不检测值
检测值: x in dic.values()
迭代键值对for key,val in dic.items()
def buy(fruits_to_buy, prices, total_amount): |
disc5
any:内置函数,判断可迭代对象里是否至少有一个为True
注意区分:一个元素的列表和单个元素的处理方法也不一样
#nonlocal只对嵌套函数有效 |
隐式序列
- 惰性计算(Lazy computation):直到程序需要它的值才去计算
迭代器
- 两个组件:检查下一个元素是否存在、到达序列末尾发出信号的机制
- 对任何容器都可以调用iter函数获取迭代器,并对iter使用next来获取下一个元素,若没有下一个元素,python处理方法是抛出异常
try: |
- 同一对象可以有多个互不影响的迭代器,但是直接赋值迭代器是会相互影响的
- 在迭代器上调用iter会返回其本身
- 可迭代对象:序列、容器、迭代器本身(如果对象具有返回迭代器的 iter 方法(method),则表示对象是可迭代的。)要在 for 循环中使用迭代器,迭代器还必须具有 iter 方法
- 内置迭代器:map\filter\zip\reversed. map应用之后,其对应操作会推迟到对返回的对象调用next的时候,对迭代器使用list函数后会一次性next完。
生成器
- 生成器是由一种特殊类型的函数 生成器函数 返回的迭代器。 生成器函数与常规函数不同之处在于,它们在其主体内不包含 return 语句,而是使用 yield 语句来返回一系列元素
- 生成器不使用对象的属性来跟踪它们在序列中的进度。 相反,它们控制生成器函数的执行,在每次调用生成器的 next 方法时执行,直到下一个 yield 语句被执行为止
# 可迭代接口 |
python 流
- 流(Streams)提供了另一种隐式表示连续数据的方法。 Stream 是一个惰性计算的链表(linked-list)。
class Stream: |
hw04

/:就算是整数也会得到浮点数
def deep_map(f, s): |
面向对象编程
- 当通过点表达式调用方法时,对象本身(在本例中绑定为 spock_account )扮演双重角色。首先,它确定名称 withdraw 的含义; withdraw 不是环境中的名称,而是 Account 类的本地名称。其次,当调用 withdraw 方法时,它绑定到第一个参数 self
- 点表达式
<expression>.<name>等价于getattr(expression,name)- hasattr()判断是否有
- 方法和函数
#参数为self,num |
- Python 的约定规定,如果属性名称以下划线开头,则只能在类本身的方法中访问它,而不是用户访问。
- 命名约定:类名通常使用 CapWords 约定(也称为 CamelCase,因为名称中间的大写字母看起来像驼峰)编写。方法名称遵循使用下划线分隔的小写单词命名函数的标准约定。
类属性(类变量,静态变量)

- 属性赋值
- 所有左侧包含点表达式的赋值语句都会影响该点表达式对象的属性。如果对象是实例,则赋值将设置实例属性。如果对象是类,则赋值将设置类属性。(对实例的属性赋值不会影响同类的其他实例)
继承
class CheckingAccount(Account): |
菱形继承问题:
lab05
a, b = s, s[:] |
局部赋值问题
- 变量 = 新对象 → 只是变量绑定变了(局部赋值,不会影响外部)
- 对象内部修改(如 list.append, list[:] = …, dict[…] = …)→ 会影响外部
- 避免局部赋值的方法:直接修改传入对象的内部结构,而不是给形参重新赋值
disc06
filter(function, iterable),过滤所有应用function的值为true的
def gen_fib(): |
hw05
- yield:产出一个值,暂停执行,等下一次 next() 继续。
yield from:把另一个可迭代对象的值逐个 yield 出来(简化写法)。
还能处理 生成器返回值 和 方法转发,功能更强。
def hailstone(n): |
实现类和对象
- 在本节中,我们将看到类和对象本身可以使用函数和字典来表示。通过以这种方式实现对象系统的目的是为了说明使用对象的隐喻并不需要特定的编程语言。即使在没有内置对象系统的编程语言中,程序也可以是面向对象的。
实例
#当当前实例没有name属性时在该类中寻找方法,并将其绑定到实例上 |
类
def make_class(attributes, base_class=None): |
使用自定义的对象
def make_account_class(): |
Account = make_account_class() |
#inheritance |
lab06
类内调用类内方法:加个self
邮件系统
class Email: |
对象抽象
- 对象抽象的一个核心概念就是泛型函数,这种函数能够接受多种不同类型的值。我们将思考三种不同的用于实现泛型函数的技术:共享接口,类型派发和类型强制转换。
字符串转换
- Python 规定所有的对象都应该生成两个不同的字符串表示:一种是人类可读的文本,另一种是 Python 可解释的表示式。字符串的构造函数,即 str,返回一个人类可读的字符串。如果可能,repr 函数返回一个 Python 可解释的表达式,该表达式的求值结果与原对象相同(eval(repr(object))==object)
- 如repr这样能作用于各种甚至自定义的数据类型,实现方法是在每个类里面有不同定义的共享属性名称(_str_())
专用方法
- 某些特殊名称会在特殊情况下自动调用
- eg.1 boolean
Account.__bool__ = lambda self: self.balance != 0- 可以使用bool(account_name)来调用该方法
- eg.other:
__len__ __getitem__
class Adder(object): |
多重表示
- 如复数有几乎相同的两种表示方式(直角坐标和极坐标),有时需要支持同一个对象的两种表示
class Number: |
泛型函数
- 类型派发:根据函数或方法的参数类型进行选择(isinstance,type_tag)
class Number: |
- 强制转换(自己写,如实数转换成虚部为零的复数)
class Number: |
disc 07
super().__init()
def draw(hand, positions): |
hw06
def store_digits(n): |
效率

- 这种树递归是非常冗余低效的
- 空间效率:要了解函数的空间需求,我们通常必须指定在计算的环境模型中如何使用、保留和回收内存。在计算表达式时,解释器会保存所有活动的环境,以及这些环境引用的所有值和帧。我们称一个环境是活动的,如果它为正在计算的某个表达式提供了评估上下文。每当为其创建第一个帧的函数调用最终返回时,环境将变为非活动状态。
记忆化(memoization)
#函数本身也是对象,可以挂载属性 |
- 记忆化自然的可以使用高阶函数的方式进行记录(使用字典作为缓存的数据结构要求被记忆化的函数的参数是不可变的。)
>>> def memo(f): |
增长类别

lab07
class Link: |
disc08
def display(s, k=10): |
函数式编程
表达式
- scheme 程序主要是由各种表达式构成的,这些表达式可以是函数调用或一些特殊的结构。
(quotient 10 2)调用函数
(+ (* 3 5) (- 10 6))前缀操作符

(define (<name> <formal parameters>) <body>)定义一个函数(过程)
(lambda (<formal-parameters>) <body>)
复合类型
- pair 是内置的数据结构。出于历史原因,pair 是通过内置函数 cons 创建的,而元素则可以通过 car 和 cdr 进行访问
- Scheme 语言中也内置了递归列表,它们使用 pair 来构建。特殊的值 nil 或 '() 表示空列表。递归列表的值是通过将其元素放在括号内,用空格分隔开来表示的
(define one-through-four (list 1 2 3 4))
(define (length items) |
- 为了操作符号,我们需要语言中的一个新元素:引用数据对象的能力。假设我们想构建列表 (a b)。我们不能通过 (list a b) 来实现这一目标,因为这个表达式构建的是 a 和 b 的值,而不是它们自身的符号。在 Scheme 中,我们通过在它们前面加上一个单引号来引用符号 a 和 b 而不是它们的值
lab08
#max+lambda函数 |
lab09
- scheme中的if:必须要真假都写上
;;cond的用法,本身就是一个表达式所以不需要再加括号 |
- A Scheme symbol is equivalent to a Python name. A symbol evaluates to the value bound to that symbol in the current environment. (They are called symbols rather than names because they include + and other arithmetic symbols.)
- Just like in Python, to evaluate a call expression:
- Evaluate the operator. It should evaluate to a procedure.
- Evaluate the operands, left to right.
- Apply the procedure to the evaluated operands.
(define <symbol> <expression>)分配值
(if <predicate> <if-true> <if-false>)
| 函数式编程思想 | 解释 |
|---|---|
| 一切皆函数 | 函数可以当数据用 |
| 无副作用 | 函数不修改外部状态 |
| 不可变性 | 数据一旦创建不可更改 |
| 递归代替循环 | 不依赖变量状态 |
| 函数组合 | 把小函数组合成复杂逻辑 |
hw07
(define (square n) (* n n)) |

disc 09

三种嵌套列表的方式
(define with-list |
异常
try: |
- 当抛出异常时,当前代码块中的后续语句将不会执行。除非异常被处理(如下所述),否则解释器将直接返回到交互式的读取 - 求值 - 打印(read-eval-print-loop)循环,或者在 Python 是通过文件参数启动时会完全终止。此外,解释器将打印一个堆栈回溯(stack backtrace),它是一个结构化的文本块,描述了在异常被抛出的执行分支中活动的嵌套函数调用集合。
异常对象
class IterImproveError(Exception): |
组合语言的解释器

hw08
(define (no-repeats s) |
lab10
#计算 |
抽象语言的解释器
- 计算器语言不支持任何方式的抽象。因此,它不是一种特别强大或通用的编程语言。现在,我们的任务是定义一种通用编程语言,通过将名称绑定到数值和定义新操作来支持抽象。
结构
- 解析器解析得到表达式,对表达式调用求值函数。
- 求值(Evaluation)。Scheme 一次计算一个表达式。scheme_read 返回的每个表达式都传递给 scheme_eval 函数,该函数计算当前环境 env 中的表达式 expr。
- 函数应用(procedure application)。两种参数
- primitiveprocedure:python实现,直接绑定
- lambdaprocedure:scheme实现,调用就是对body 部分求值,为了求值会在环境中新建一个帧
- 求值流程的本质就是求值函数和应用函数的互相递归
环境
- 就是各个frame(有个绑定以及parent,global没有prarent)
- 绑定不能直接访问,而是通过两种 Frame 方法:lookup 和 define。第一个方法:符号与当前帧的绑定相匹配。如果找到它,则返回它绑定到的值。如果没有找到,则继续在父帧中查找。另一方面,define 方法用来将符号绑定到当前帧中的值。
disc10
def print_evals(expr): |
scheme
- User-defined procedures open a new frame; builtins do not
- Builtins simply execute a predefined Python function; user-defined procedures must evaluate additional expressions in the body
- 注意当前帧找不到的到父帧去找
- scheme中的(+)结果为0
- para.append()返回值是none
#why:scheme中nil也是一个有效参数值 |
- 求值的关键pair(2,nil)<=>(2),解决办法就是map
#递归思路:两个就直接用,多个操作数先map全部求值再递归 |
- begin
#为什么这么写,在求值过后,还有pair就是直接quote的 |
Pair(3,nil)(3)
Pair(2,Pair(3,nil))(2,3)
Pair(2,3)(2.3);;不算列表
- lambda函数
child_frame=procedure.env.make_child_frame(procedure.formals,args) |
- 嵌套lambda函数示意
(define outer |
-
一般的function就可以看作其去掉函数名的lambda绑定在这上面
-
lexical scope(词法作用域):the parent of the new call frame is the environment in which the procedure was defined.(主流,决定于其定义的上下文)
-
dynamic scope(动态作用域):the parent of the new call frame is the environment in which the call expression was evaluated.
-
do_and_form
#attention :when judging by if statement,the first is already evaled |
- 判断是否多个语句:len(pair)==1
- scheme函数嵌套
(define (enumerate s) |
program as data、macro(宏)
- scheme expression is just the scheme list(这使得编写产生程序的程序尤其直接)
- quosiquotation:部分引用

- 宏实际上相当有用(新代码生成,控制抽象)
hw09
;(car formals)=>`a 则,(car formals)出来的是a,没有对其求值 |
SQL
- structured query language(SQL):
- declarative programming language(声明式)
- 仅仅只需要描述你期望的结果,解释器会自动翻译过来
- 区别于python 等命令式编程(imperative)(描述执行的过程)

- project (从一个表生成另一个表)

.mode column设置模式为列select * from ints查看整个列表(union生成的列表无法保证顺序,这取决于解释器的实现)
disc 11
;宏的一个特点:在调用宏的帧当中求值,可以直接交换x,y=y,x这种 |
Tables




hw10

- 降序:DESC记号
- sql 不允许嵌套创建table
- 最好建立表的时候对相同名的列进行重命名
CREATE TABLE by_parent_height AS |
aggregation(聚合)
- 通过group by 进行聚合


database
- DDL(数据库定义语言):create\drop
- drop table 是直接把表给删了(这和清空表不一样)
- DML(修改表格数据)
- insert

- udpate
- delete:delete没指定,就是全删,但是此时的列表还是存在的
- insert

designing functions

--remember to use and |





