一.函数递归
函数的递归调用:是函数嵌套调用的一种特殊形式
具体指的是在调用一个函数的过程中又直接或者间接地调用自己,称之为函数的递归调用
函数的递归调用其实就是用函数实现的循环# def f1():
# print('from f1')
# f1()
# f1()
或者
# def f1():
# print('f1')
# f2()
#
# def f2():
# print('f2')
# f1()
#
# f1()
一个递归的过程应该分为两个阶段:
1、回溯:向下一层一层调用
2、递推:向上一层一层返回
#1
# age(5) = age(4) + 10
# age(4) = age(3) + 10
# age(3) = age(2) + 10
# age(2) = age(1) + 10
#
# age(1) = 18
#
# n > 1 -> age(n) = age(n-1) + 10
# n = 1 -> age(1) = 18
# def age(n):
# if n == 1:
# return 18
# return age(n-1) + 10
#
# res = age(5)
# print(res)
#2
# nums = [1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]]
#
# def func(nums):
# for item in nums:
# if type(item) is list:
# func(item)
# else:
# print(item)
#
# func([1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]])
#3
nums = [-3,1,5,7,9,11,13,18,22,38,78,98]
# for num in nums:
# if num == find_num:
# print('find it')
# break
def search(find_num,nums):
print(nums)
if len(nums) == 0:
print('not exists')
return
mid_index = len(nums) // 2
if find_num > nums[mid_index]:
# in the right
new_nums = nums[mid_index+1:]
search(find_num,new_nums)
elif find_num < nums[mid_index]:
# in the left
new_nums = nums[:mid_index]
search(find_num,new_nums)
else:
print('find it')
# search(22,nums)
search(23,nums)
二.匿名函数
对比使用def关键字创建的是有名字的函数,使用lambda关键字创建则是没有名字的函数,即匿名函数,语法如下
# 1、定义
lambda x,y,z:x+y+z
#等同于
def func(x,y,z):
return x+y+z
# 2、调用
# 方式一:
res=(lambda x,y,z:x+y+z)(1,2,3)
# 方式二:
func=lambda x,y,z:x+y+z # “匿名”的本质就是要没有名字,所以此处为匿名函数指定名字是没有意义的
res=func(1,2,3)
匿名函数与有名函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,所以匿名函数用于临时使用一次的场景,匿名函数通常与其他函数配合使用
#取得薪水的最大值和最小值
salaries={
'siry':3000,
'tom':7000,
'lili':10000,
'jack':2000
}
# 函数max会迭代字典salaries,每取出一个“人名”就会当做参数传给指定的匿名函数,然后将匿名函数的返回值当做比较依据,最终返回薪资最高的那个人的名字
>>> max(salaries,key=lambda k:salaries[k])
'lili'
# 原理同上
>>> min(salaries,key=lambda k:salaries[k])
'jack'
三.map、reduce、filter
函数map、reduce、filter都支持迭代器协议,用来处理可迭代对象,我们以一个可迭代对象array为例来介绍它们三个的用法
array=[1,2,3,4,5]
1.map
要求一:对array的每个元素做平方处理,可以使用map函数
map函数可以接收两个参数,一个是函数,另外一个是可迭代对象,具体用法如下
>>> res=map(lambda x:x**2,array)
>>> res
<map object at 0x1033f45f8>
>>>
解析:map会依次迭代array,得到的值依次传给匿名函数(也可以是有名函数),而map函数得到的结果仍然是迭代器。
>>> list(res) #使用list可以依次迭代res,取得的值作为列表元素
[1, 4, 9, 16, 25]
2.reduce
要求二:对array进行合并操作,比如求和运算,这就用到了reduce函数
reduce函数可以接收三个参数,一个是函数,第二个是可迭代对象,第三个是初始值
# reduce在python2中是内置函数,在python3中则被集成到模块functools中,需要导入才能使用
>>> from functools import reduce
>>> res=reduce(lambda x,y:x+y,array)
>>> res
15
解析:1 没有初始值,reduce函数会先迭代一次array得到的值作为初始值,作为第一个值数传给x,然后继续迭代一次array得到的值作为第二个值传给y,运算的结果为3
2 将上一次reduce运算的结果作为第一个值传给x,然后迭代一次array得到的结果作为第二个值传给y,依次类推,知道迭代完array的所有元素,得到最终的结果15
也可以为reduce指定初始值
>>> res=reduce(lambda x,y:x+y,array,100)
>>> res
115
3.filter
要求三:对array进行过滤操作,这就用到了filter函数,比如过滤出大于3的元素
>>> res=filter(lambda x:x>3,array)
解析:filter函数会依次迭代array,得到的值依次传给匿名函数,如果匿名函数的返回值为真,则过滤出该元素,而filter函数得到的结果仍然是迭代器。
>>> list(res)
[4, 5]
提示:我们介绍map、filter、reduce只是为了带大家了解函数式编程的大致思想,在实际开发中,我们完全可以用列表生成式或者生成器表达式来实现三者的功能。
四.模块
1、什么是模块
模块就是一系列功能的集合体
模块分为四种类别:
1、一个py文件就可以是一个模块
2、包:就是一个存放有__init__.py文件的文件夹
3、使用C编写并链接到python解释器的内置模块
4、已被编译为共享库或DLL的C或C++扩展
模块有三种来源
1、python解释器自带的
内置的
标准库
import time # 内置库
print(time)
import os # 标准库
print(os)
2、第三方的库
3、自定义的库
2、为何要用模块
1、拿来主义,极大地提升开发效率
2、解决代码冗余问题
3、模块用法
import foo #导入模块foo
a=foo.x #引用模块foo中变量x的值赋值给当前名称空间中的名字a
foo.get() #调用模块foo的get函数
foo.change() #调用模块foo中的change函数
obj=foo.Foo() #使用模块foo的类Foo来实例化,进一步可以执行obj.func()
#加上foo.作为前缀就相当于指名道姓地说明要引用foo名称空间中的名字,所以肯定不会与当前执行文件所在名称空间中的名字相冲突,并且若当前执行文件的名称空间中存在x,执行foo.get()或foo.change()操作的都是源文件中的全局变量x。
#需要强调一点是,第一次导入模块已经将其加载到内存空间了,之后的重复导入会直接引用内存中已存在的模块,不会重复执行文件,通过import sys,打印sys.modules的值可以看到内存中已经加载的模块名。# 后续的导入直接引用首次导入的成果
#首次导入模块发生三件事
1、会触发spam.py运行,所以会产生一个模块的名称空间
2、运行spam.py的代码,将运行过程中产生的名字都丢到模块的名称空间中
3、在当前执行文件的名称空间中拿到一个名字spam,该名字就是指向模块的名称空间的
2.from-import 语句
from...import...与import语句基本一致,唯一不同的是:使用import foo导入模块后,引用模块中的名字都需要加上foo.作为前缀,而使用from foo import x,get,change,Foo则可以在当前执行文件中直接引用模块foo中的名字,如下
from foo import x,get,change #将模块foo中的x和get导入到当前名称空间
a=x #直接使用模块foo中的x赋值给a
get() #直接执行foo中的get函数
change() #即便是当前有重名的x,修改的仍然是源文件中的x
3.其它
(1) *
from foo import * #把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字
a=x
get()
change()
obj=Foo()
(2)as
#为导入的模块起别名
import foo as f #为导入的模块foo在当前位置起别名f,以后再使用时就用这个别名f
f.x
f.get()
#为导入的名字起别名
from foo import get as get_x
get_x()
4.循环导入问题
详见: https://zhuanlan.zhihu.com/p/109127048
5.搜索模块的路径与优先级
在导入一个模块时,如果该模块已加载到内存中,则直接引用,否则会优先查找内置模块,然后按照从左到右的顺序依次检索sys.path中定义的路径,直到找模块对应的文件为止,否则抛出异常。sys.path也被称为模块的搜索路径,它是一个列表类型
>>> sys.path
['',
'/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip',
'/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
...,
'/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages'
6. 区分py文件的两种用途
一个Python文件有两种用途:
(1)一种被当主程序/脚本执行
(2)另一种被当模块导入
#为了区别同一个文件的不同用途,每个py文件都内置了__name__变量,该变量在py文件被当做脚本执行时赋值为“__main__”,在py文件被当做模块导入时赋值为模块名