3.26
上节课回顾
-
叠加多个装饰器的分析
@deco1 ===>wrapper1 @deco2 ===>wrapper2 @deco3 ===>wrapper3 def index(): pass
调用顺序:先运行wrapper1,wrapper1中调用wrapper2,wrapper2再调用wrapper3,wrapper3调用index,最后一层层结束
-
yield
x = yield 返回值 ... next(g) g.send('1') # send给yield传值 # yield的运行顺序
-
三元表达式
res = 条件成立返回值 if 条件 else 条件不成立返回值
-
生成式
lis = [有返回值的表达式 for x in 可迭代对象 if 条件]
用于快速生成列表
映射成一个新的值:
l = [1,2,3,4,5] new_l = [x**2 for x in l]
列表里的值非常多的时候可以用生成器生成式,把中括号换成小括号。
- list(),sum() 一类要传入可迭代对象的方法,往里面填生成器表达式可以不用小括号
字典生成式
dic = {键:值 for k in 可迭代对象 in 条件}
-
函数递归调用:我 调 我 自 己
回溯:一层一层的调用
递推: 满足某种条件下, 结束递归调用, 一层一层返回例子:汉诺塔,求文件夹下文件的大小
不应该无限地递归下去,应该在某种条件下return
今日内容
- 二分法:算法
- 面向过程的编程思想
- 函数式
- lambda
- map
- reduce
- filter
- sorted
- 模块 import
- 内置函数
正课
算法之 二分法
算法:高效解决问题的方法
现有需求:有一个按照从小到大顺序排列的数字列表,需要从该数字列表中找到我们想要的那一个数字,如何更加高效?
Num = [-3,4,10,15,33,99,101]
一般的思想:从头开始一个个取数,与要找的数字比较,找到就退出:整体遍历,效率低
使用二分法
要降低运行的次数,提高效率,使用算法
- 找到列表中间的值
- 判断要找的值比中间的值大 / 小
- 如果要找的值比中间值大,则要找的数应该再右半部分:当前列表切片,留下右半边列表成为新列表,再查找
- 如果要找的值比中间值小,则要找的数在列表的左半部分:当前列表切片,留下左半边列表成为新列表,再查找
- 如果不大不小,则表示找到了
def binary(find_num,lis):
print(lis)
mid_index = len(lis)//2
mid_val = lis[mid_index]
if find_num > mid_val:
lis = lis[mid_index+1:]
binary(find_num,lis)
elif find_num < mid_val:
lis = lis[:mid_index]
binary(find_num,lis)
else:
print('find',mid_index)
binary(-10,[-10,0,8,11,15,33,66])
改进:若要找的数字不存在,则报错:索引超出范围,对此进行判断
if len(lis) == 0:
print('not exist')
面向过程的编程思想
编程思想:编程范式:是一种写程序的套路,我们现在写程序用到的都是面向过程的编程思想
面向过程的编程思想:
- 核心是“过程”,即流程,指的是做事的步骤,先干什么再干什么
- 基于该思想编程写程序就好比设计一条流水线
优点:复杂的问题被分步解决,进而简单化,是计算机干活的步骤
缺点:上一个步骤与下一个步骤紧紧相连,牵一发动全身,扩展性差
面向过程的应用场景解析
- 不是所有的软件都需要频繁更迭版本:比如编写脚本
- 即便是一个软件需要频繁更迭,也不代表这个软件所有组成部分都需要一起更迭:软件的框架
函数式编程
并不是指用函数来编程,而是讲计算机的运算视为数学上的运算。python不是函数式编程语言,但是有一些内置的函数时编程方法,如lambda,map等
匿名函数
有名函数:def func(),让func与函数内存地址绑定
匿名函数定义:lambda
lambda 参数1,参数2: 函数体代码
# 接收参数,运行函数体代码,直接将结果返回,无需指定return
# 函数体代码更多地用简单的表达式,如x+y
匿名函数调用:(内存地址 lambda...)(实参1,实参2)
方式1:返回值赋值,函数体赋值
res = (lambda x,y: x+y)(1,2)
print(res)
func = lambda x,y: x+y
res = func(1,2)
# 把匿名函数变成有名了,这样很蠢
print((lambda x:x**2)(10))
# 返回100交给print,一次性使用
匿名函数在定义出来那一刻,立马被调用一次,引用计数为0,就被回收了,所以匿名函数用于临时调用一次的场景,更多地是将匿名函数与其他函数配合使用的场景
匿名函数的应用
max(iterable,key)
需求:在一个字典中比较value,取出value最大的key
salaries={
'deimos':222,
'aaa':111,
'zjy':500
}
可以使用内置的max() 函数,在可迭代对象中返回最大值
max(可迭代对象,key=)
# 内置函数,返回最大值,迭代的是什么就会返回什么。如果不指定key,会直接比较迭代器中取出的值,指定key则以key为依据进行比较
res = max([3,22,11,0,68])
# 得到68
对字典使用max,不指定key的话默认比较的是字典的key,而我们需要比的是字典的值,因此写一个函数把字典的值传给key进行比较
def func(k):
return salaries[k]
res = max(salaries,key=func)
# 迭代一次,从迭代对象中取出一个值传给key,以key作为比较依据进行比较,最终返回的是一开始传进key的值
print(res)
func在这个功能以外没有别的使用场景,所以是临时用一次的函数,应该被定义为匿名函数
res = max(salaries,key = lambda k:salaries[k])
一样的,有 min(iterable,key=)
返回的是最小值
res = min(salaries,key=lambda k:salaries[k])
# min(迭代对象,key = 可调用对象)
# 调用对象可以是有名函数,无名函数
print(res)
sorted(iterable,key)
用法与max() 一样,返回一个新的排序过的迭代器对象
仅了解的知识点
map(func,iteraale)
迭代这个可迭代对象,取出一个值,执行func方法,再迭代,再执行func...,最后得到一个迭代器对象
需求:为列表中每个字符串元素加上后缀
# 可以用列表生成式
l = ['aaa','bbb','ccc']
new_l = [l]
filter(func,iterable)
迭代这个迭代对象,只有执行结果为真的对象会被返回
l = ['aaa','bba','cca','dda','eee']
res = (name for name in l if name.endswith('a'))
print(res)
# filter
filter(lambda name:name.endswith('a'),l)
reduce(func,iterable,default)
在python3中使用reduce,要引用functools
from functools import reduce
res = reduce(lambda x,y:x+y,[1,2,3],10)
print(res) # 16
模块
介绍
什么是
模块就是一系列功能的集合体,通过模块.名字 的方式可以调用出一堆功能
模块分为三大类:
- 内置模块:python解释器提供的
- 第三方模块:别人写好的
- 自定义模块:可以是python写的,也可以是别的语言写的:一个python文件本身就是一个模块,文件名m.py,模块名叫m,没有py结尾
模块又分为四种形式:
- 使用python编写的.py文件
- 已被编译为共享库或DLL的c或c++扩展
- 把一系列模块组织到一起的文件夹,文件夹辖有一个
__init__.py
文件,该文件夹称之为包
我们课上研究的主题:1,3
为何要用模块
- 内置与第三方的模块拿来就用,无需定义,提高自己的开发效率
- 自定义的模块,可以将在多个文件中要使用相同的功能提取出来放到一个模块中为大家共享使用,好处是减少了代码冗余,程序组织结构更清晰
如何用
自定义模块
创建一个文件,文件中写任何代码:命名变量,定义函数,定义类等等。。。
在别的地方可以用这个文件的文件名当作模块导入
python3中定义模块都推荐用小写字母
导入
import foo
import m
import time
# foo,m为自定义的模块
# 也可以在一行,用逗号分隔导入多个模块,但是不建议
import foo,m
导入模块的规范:先后顺序:
- python内置模块 > 第三方模块 > 自定义模块
首次导入模块发生的事情:
- 执行foo.py
- 产生foo.py的名称空间,将foo.py运行过程中产生的名字都丢到foo的名称空间中
- 在当前文件中产生又一个名字foo,该名字指向2中产生的名称空间
首次之后的导入都是直接引用首次导入产生的结果(foo.py 名称空间)不会重复执行
- 可以在全局导入模块,也可以在函数内导入模块:导入模块时创建的名称空间不一样,注意辨别
用 as 改名
import foo as f
# 将foo改名为f,在使用的时候改用f作为模块名
f.get()
引用
print(foo.x)
print(foo.get)
# 导入foo后,指定名字在名称空间内找变量名和方法
强调
模块名.名字
是指名道姓地向某一个模块要名字对应的值,只会在模块对应的名称空间内找,与本体中同样的名字不会冲突- 无论是查看还是修改操作的都是模块本身,与调用位置无关