【python学习笔记】task06 函数与Lambda表达式
函数
1.函数的定义
基本格式
def functionname( parameters ):
"函数_文档字符串"
function_suite
return [expression]
(1)函数以def关键词开头,后接函数名和圆括号()。
(2)任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
(3)函数执行的代码以冒号起始,并且缩进。
(4)return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回None
2.调用函数
如需调用函数,使用函数名称后跟括号
【例子】定义加法函数,并调用
def add(a, b):
print(a + b)
add(1, 2) # 3
add([1, 2, 3], [4, 5, 6]) # [1, 2, 3, 4, 5, 6]
3.函数文档
即函数定义中"函数_文档字符串"这一部分
(1)作用:方便函数功能、参数以及返回值的含义能够被调用者知悉.,能够在调用时快捷查看函数相关信息
(2)与注释的区别:需要查看函数定义时才能看到
(3)查看方法:函数名.__doc__或者help(函数名)
【例子】定义并查看函数文档
def MyFirstFunction(name):
"函数定义过程中name是形参"
# 因为Ta只是一个形式,表示占据一个参数位置
print('传递进来的{0}叫做实参,因为Ta是具体的参数值!'.format(name))
MyFirstFunction('老马的程序人生')
# 传递进来的老马的程序人生叫做实参,因为Ta是具体的参数值!
print(MyFirstFunction.__doc__)
# 函数定义过程中name是形参
help(MyFirstFunction)
# Help on function MyFirstFunction in module __main__:
# MyFirstFunction(name)
# 函数定义过程中name是形参
4.函数参数
| 参数类型 | 格式 | 作用 |
|---|---|---|
| 位置参数arg1 | def functionname(arg1) | 在调用函数时位置固定的参数 |
| 默认参数arg2=v | def functionname(arg1, arg2=v) | 调用函数时,默认参数的值如果没有传入,则被认为是默认值,默认参数必须放在位置参数的后面 |
| 可变参数*args | def functionname(arg1, arg2=v, *args) | 传入的参数个数是可变的,可以是 0, 1, 2 到任意个,是不定长的参数 |
| 关键字参数**kw | def functionname(arg1, arg2=v, *args, **kw) | 可以是从零个到任意个,自动组装成字典,使用 key = value 语法发送参数 |
| 命名关键字参数*, nkw | def functionname(arg1, arg2=v, *args, *, nkw, **kw) | 如果要限制关键字参数的名字,就可以用「命名关键字参数」,使用命名关键字参数时,要特别注意不能缺少参数名 |
【例子】命名关键字参数的使用时候,如果没有写命名关键字参数,会误将该位置的参数当成「位置参数」,致使程序会报错
def printinfo(arg1, *, nkw, **kwargs):
print(arg1)
print(nkw)
print(kwargs)
printinfo(70, nkw=10, a=1, b=2)
# 70
# 10
# {'a': 1, 'b': 2}
printinfo(70, 10, a=1, b=2)
# TypeError: printinfo() takes 1 positional argument but 2 were give
注意:
(1)Python 允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
【例子】本例子中在调用函数时候参数的顺序和函数定义的不一致,但是Python能够识别
def printinfo(name, age):
print('Name:{0},Age:{1}'.format(name, age))
printinfo(age=8, name='小马') # Name:小马,Age:8
(2)可变参数与关键字参数的区别在于:
- 可变参数允许传入零个到任意个参数,它们在函数调用时自动组装为一个元组 (tuple)。
- 关键字参数允许传入零个到任意个参数,它们在函数内部自动组装为一个字典 (dict)。
【例子】比较可变参数与关键字参数
def printinfo(arg1,*args,**kwargs):
print(arg1)
print(args)
print(kwargs)
#只传入了位置参数和可变参数
printinfo(70,60,50)
# 70
# (60, 50)
# {}
#传入位置参数、河边参数和关键字参数
printinfo(70,60,50,a=1,b=2)
# 70
# (60, 50)
# {'a': 1, 'b': 2}
(3)在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:
- 位置参数、默认参数、可变参数和关键字参数。
- 位置参数、默认参数、命名关键字参数和关键字参数。
5.函数的返回值
通过return语句实现
【例子】对于无参数的函数实现返回值
def back():
return 1, '小马的程序人生', 3.14
print(back()) # (1, '小马的程序人生', 3.14)
6.变量作用域
(1)局部变量:定义在函数内部的变量拥有局部作用域,称为局部变量,局部变量只能在其被声明的函数内部访问,当内部作用域想修改外部作用域的变量时,需要用到global和nonlocal关键字了
num = 1
def fun1():
global num # 需要使用 global 关键字声明
print(num) # 1
num = 123
print(num) # 123
fun1()
print(num) # 123
(2)全局变量:定义在函数外部的变量拥有全局作用域,称为全局变量,全局变量可以在整个程序范围内访问
7.内嵌函数
在函数中又定义了函数,如下面的例子,调用上层函数时候,下层函数也被执行了
def outer():
print('outer函数在这被调用')
def inner():
print('inner函数在这被调用')
inner() # 该函数只能在outer函数内部被调用
outer()
# outer函数在这被调用
# inner函数在这被调用
8.闭包
(1)闭包是函数式编程的一个重要的语法结构,是一种特殊的内嵌函数。
(2)如果在一个内部函数里对外层非全局作用域的变量进行引用,那么内部函数就被认为是闭包。
(3)通过闭包可以访问外层非全局作用域的变量,这个作用域称为 闭包作用域。
【例1】闭包的类型及使用,可以看出i是函数类型
def funX(x):
def funY(y):
return x * y
return funY
i = funX(8)
print(type(i)) # <class 'function'>
print(i(5)) # 40
注意:
(1)闭包的返回值通常是函数
【例子】实现一个计时器,
函数的作用是做一个计数器,
- 用增加子函数 inc() 续一秒
- 用减少子函数 dec() 废一秒
- 用获取子函数 get() 看秒数
- 用重置子函数 reset() 回原点
def make_counter(init):
counter=[init]
def inc():counter[0]+=1
def dec():counter[0]-=1
def get():return counter[10]
def reset():counter[10]=init
return inc,dec,get,reset
#初始化
inc, dec, get, reset = make_counter(0)
#续了3秒
inc()
inc()
inc()
#输出此时的秒数
dec()
#减去一秒,查看此时的秒数
print(get())
reset()
#重置并查看此时的秒数
print(get())
结果为:
3
2
0
(2)如果要修改闭包作用域中的变量则需要 nonlocal 关键字
【例子】改变闭包作用域中的变量
def outer():
num = 10
def inner():
nonlocal num # nonlocal关键字声明
num = 100
print(num)
inner()
print(num)
outer()
# 100
# 100
9.递归
如果一个函数在内部调用自身本身,这个函数就是递归函数。
【例1】实现阶乘n!
def factorial(n):
if n == 1:
return 1
return n * fact(n - 1)
【例2】实现斐波那契数列 f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1
def recur_fibo(n):
if n <= 1:
return n
return recur_fibo(n - 1) + recur_fibo(n - 2)
求从0开始的11个斐波那契数列
lst=list()
for k in range(11):
lst.append(recur_fibo(k))
print(lst)
【例3】设置递归的层数,Python默认递归层数为 100
import sys
sys.setrecursionlimit(1000)
Lambda表达式
1.匿名函数的定义
在 Python 里有两类函数:
- 第一类:用 def 关键词定义的正规函数
- 第二类:用 lambda 关键词定义的匿名函数
匿名函数的基本格式为
lambda argument_list: expression
其中
expression中没有return语句,因为 lambda 不需要它来返回,表达式本身结果就是返回值- 匿名函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数
【例子】使用可变参数与匿名函数实现加法操作
func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5)) # 15
2.匿名函数的应用
函数式编程 是指代码中每一块都是不可变的,都由纯函数的形式组成。这里的纯函数,是指函数本身相互独立、互不影响,对于相同的输入,总会有相同的输出,没有任何副作用
【例1】函数式编程
def f(x):
y = []
for item in x:
y.append(item + 10)
return y
【例2】非函数式编程
def f(x):
for i in range(0, len(x)):
x[i] += 10
return x
匿名函数常常应用于函数式编程的高阶函数 中,主要有两种形式:
- 参数是函数 (filter, map)
- 返回值是函数 (closure)
(1)匿名函数在filter函数中的应用
filter(function, iterable) 过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换
【例1】将匿名函数与filter函数结合,实现奇数的过滤
odd=lambda x:x%2==1 #如果x是奇数
templist=filter(odd,[1,2,3,4,5,6,7,8,9])
print(list(templist))
# [1, 3, 5, 7, 9]
(2)匿名函数在map函数中的应用
map(function, *iterables) 根据提供的函数对指定序列做映射
【例2】
m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5])
print(list(m1))
# [1, 4, 9, 16, 25]
m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(m2))
# [3, 7, 11, 15, 19]
(3)匿名函数在自定义的高阶函数中的应用
除了 Python 这些内置函数,我们也可以自己定义高阶函数
【例3】
def apply_to_list(fun, some_list):
return fun(some_list)
lst = [1, 2, 3, 4, 5]
print(apply_to_list(sum, lst))
#15
print(apply_to_list(len, lst))
#5
print(apply_to_list(lambda x: sum(x) / len(x), lst))
#3.0
相关资料
- https://www.runoob.com/python3/python3-tutorial.html
- https://www.bilibili.com/video/av4050443
- https://mp.weixin.qq.com/s/gKhXS8JVU8dZBHJF7sIFsw
练习题
1.怎么给函数编写⽂档?
在 def 语句后⾯把注释⽂档放在引号(单引、双引、三引都可以)⾥⾯就⾏,这个⽂档可以通过
function.__doc__访问。
def square(x):
"""返回平⽅值"""
return x*x
square.__doc__ ## '返回平⽅值'
2.怎么给函数参数和返回值注解?
代码执⾏时,注解不会做任何处理,只是存储在函数的
__annotations__属性(⼀个字典)中。
def function(text: str, max_len: 'int > 0' = 80) -> str:
函数声明中的各个参数可以在:之后增加注解表达式。如果参数有默认值,注解放在参数名和=号之间。如果想
注解返回值,在)和函数声明末尾的:之间添加->和⼀个表达式。
Python对注解所做的唯⼀的事情是,把它们存储在函数的__annotations__属性⾥。 仅此⽽已,Python不做检
查、不做强制、不做验证,什么操作都不做。换句话说, 注解对Python解释器没有任何意义。注解只是元数
据,可以供IDE、框架和装饰器等⼯具使⽤。
3.闭包中,怎么对数字、字符串、元组等不可变元素更新。
使用
nonlocal关键字
4.分别根据每一行的首元素和尾元素大小对二维列表 a = [[6, 5], [3, 7], [2, 8]] 排序。(利用lambda表达式)
使用sorted()函数实现
sorted(iterable, cmp=None, key=None, reverse=False)
- iterable – 可迭代对象。
- cmp – 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
- key – 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
- reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
参考https://www.runoob.com/python/python-func-sorted.html
#按首元素排序
a = [[6, 5], [3, 7], [2, 8]]
sorted(a, key=lambda x:x[0])
#按照尾元素排序
a = [[6, 5], [3, 7], [2, 8]]
sorted(a, key=lambda x:x[1])
5.利用python解决汉诺塔问题?
有a、b、c三根柱子,在a柱子上从下往上按照大小顺序摞着64片圆盘,把圆盘从下面开始按大小顺序重新摆放在c柱子上,尝试用函数来模拟解决的过程。(提示:将问题简化为已经成功地将a柱上面的63个盘子移到了b柱)

有n个盘子的汉诺塔步骤可以归纳为以下3步
(1)将1~n-1号盘子从a移动到b
(2)将n号盘子从a移动到c
(3)将1~n-1号盘子从b移动到c
def move(n,a,b,c):
if n==0:
return
#如果n>1,a就要把n-1个盘子移动到b(可以经过c)
move(n-1,a,c,b)
#然后将剩下的n号盘子从a移动到c
print('将%d号盘子从%s移动到%s'%(n,a,c))
#最后将b上的n-1号盘子移动到c上
move(n-1,b,a,c)
move(63,'a','b','c')
本文深入探讨Python中的函数定义、调用、参数、返回值、作用域等核心概念,详解闭包、递归的使用技巧,同时介绍Lambda表达式在函数式编程中的应用。


230

被折叠的 条评论
为什么被折叠?



