函数的递归调用
一:引入
函数的递归调用:就是在调用一个函数的过程中又直接或间接地调用自己
示例1:直接调用自己
def foo():
print('hello')
foo()
foo()
示例2:间接调用自己
def bar():
print('from bar')
foo()
def foo():
print('hello')
bar()
foo()
为何"死递归"会抛出异常???
因为无限的递归会导致内存溢出,所以python设定了最大的递归层数,这个最大递归层数可以自己设置,但是一般情况下python自身设定的层就已经足够使用了。
import sys
print(sys.getrecursionlimit())
print(sys.setrecursionlimit(2000))
所以:不应该无限递归调用下去,应该在满足某种条件下结束递归调用,然后返回
二:递归调用应该分为两个阶段
1、回溯(挖井) :一层一层地递归调用下去
2、递推(从井里往外跳):在满足某一条件的情况下结束回溯,然后开始向上一层层返回
例1:
salary(5) = salary(4) + 10
salary(4) = salary(3) + 10
salary(3) = salary(2) + 10
salary(2) = salary(1) + 10
salary(1) = 18
n=1 salary(n) = 18
n!=1 salary(n) = salary(n-1) + 10
def salary(n):
if n == 1:
return 18
return salary(n-1) + 10
res=salary(5)
print(res)
例2:将列表中的数字打印出来
nums=[111,[222,[333,[444,[5555,[6666,[777,[888,[9999]]]]]]]]]
def func(l):
for x in l:
if type(x) is list:
# 把自身的代码重新再调用一次
func(x)
else:
print(x)
func(nums)
例3:从小到大排列的一个数字列表,查找某个数是否在里面
nums = [11, 13, 32, 47, 53, 73, 84, 91,101,111,222,333,444,5555]
def binary_search(l,find_num):
print(l)
if len(l) == 0:
print('find_num not exists')
return
mid_index = len(l) // 2
if find_num > l[mid_index]:
right_l=l[mid_index+1:]
binary_search(right_l,find_num)
elif find_num < l[mid_index]:
left_l=l[:mid_index]
binary_search(left_l,find_num)
else:
print('find it')
binary_search(nums,85)
三元表达式
如下为判断两个数大小的一段:
def max2(x,y):
if x > y:
return x
else:
return y
可以使用三元表达式简化:
x=111
y=222
res=x if x > y else y
print(res)
三元表达式: 表达式1 if 条件 else 表达式2
列表生成式
1、示例
egg_list=[]
for i in range(10):
egg_list.append('鸡蛋%s' %i)
egg_list=['鸡蛋%s' %i for i in range(10)]
2、语法
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
类似于
res=[]
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2
...
for itemN in iterableN:
if conditionN:
res.append(expression)
3、优点:方便,改变了编程习惯,可称之为声明式编程
匿名函数
一、什么是匿名函数
匿名就是没有名字
def func(x,y,z=1):
return x+y+z
匿名
lambda x,y,z=1:x+y+z #与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,除非让其有名字
func=lambda x,y,z=1:x+y+z
func(1,2,3)
#让其有名字就没有意义
二、有名函数和匿名函数对比
有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
匿名函数:一次性使用,随时随时定义
匿名函数的应用:max,min,sorted,map,reduce,filter
模块
1、什么是模块
模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能。
模块可以分为四个通用类别:
-- 1 使用python编写的.py文件
-- 2 已被编译为共享库或DLL的C或C++扩展
-- 3 把一系列模块组织到一起的文件夹(注:文件夹下有一个init.py文件,该文件夹称之为包)
-- 4 使用C编写并链接到python解释器的内置模块
2、为什么要使用模块
1、从文件级别组织程序,更方便管理
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用
2、拿来主义,提升开发效率
同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率
ps:
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
3、如何使用模块(以spam为例)
(1)import
import spam
首次导入模块发生的事情
1、触发被导入的模块的运行,产生一个模块的名称空间,把模块中的名字都丢进去。
2、会在当前执行文件中得到一个名字spam,该名字是指向被导入模块的名称空间的。
3、之后的导入,名字spam直接引用首次导入产生的名称空间,不会再执行模块的内的代码了
可以在一行导入多个模块
import spam,m1,m2,m3 # 不推荐
如果导入的模块名过长可以修改它的名称
import spamasdfasfsadfadfasfd as sm
sm.xxx
(2)from...import...as...
-
1、和import唯一的区别就是:使用from...import...则是将spam中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:spam.
-
2、from...import...的方式有好处也有坏处
-- 好处:使用起来方便了
-- 坏处:容易与当前执行文件中的名字冲突 -
3、from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置
ps:大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题
- 4、可以使用_all_来控制*(用来发布新版本),在spam.py中新增一行
_all_=['money','read1'] # 这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
网友评论