DataWhale-Python基础-10.函数与lambda表达式
1.函数
1.1函数的定义
-
函数以def关键词开头,后接函数名和圆括号()。
-
函数执行的代码以冒号起始,并且缩进。
-
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回None。
def functionname(parameters): "函数_文档字符串" function_suite return [expression]
1.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]
1.3函数文档
def Fisrtfunction(name):
"函数的定义过程中name是形参"
#因为name只是一个形式,表示占据一个参数的位置
print("传进来的{0}叫做实参,因为它才是具体的数值".format(name))
Fisrtfunction('Wine')
# 传进来的Wine叫做实参,因为它才是具体的数值
print(Fisrtfunction.__doc__)
# 函数的定义过程中name是形参
help(Fisrtfunction)
#Help on function Fisrtfunction in module __main__:
#Fisrtfunction(name)
# 函数的定义过程中name是形参
1.4函数参数
-
位置参数
-
默认参数
-
可变参数
-
关键字参数
-
命名关键字参数
-
参数组合
def functionname(arg1, arg2=v, *args, *, nkw, **kw): "函数_文档字符串" function_suite return [expression]
-
arg1 -位置参数,这些参数在调用时位置固定
-
arg2 = v -默认参数=默认值,调用时若没有传入默认参数,则默认参数取默认值
-
arg -可变参数,可以是0到任意个,自动组装成元组;加了的变量名会存放所有未命名的变量参数。
-
**kw -关键字参数,可以是0到任意个,自动组装成字典
-
*,nkw -命名关键字参数,用户想要输入的关键字参数,定义方式是nkw前面+一个分隔符,如果需要限制关键字参数的名字,就可以用命名关键字参数;在使用命名关键字参数时,要特别注意不能缺少参数名。
可变参数和关键字参数的异同: 1.可变参数允许传入0到多个参数,它们在函数调用时自动组装乘一个元组 2.关键字参数允许传入0到多个参数,它们在函数内部自动组装成一个字典
参数组合
在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:
- 位置参数、默认参数、可变参数和关键字参数。
- 位置参数、默认参数、命名关键字参数和关键字参数。
要注意定义可变参数和关键字参数的语法: - *arg是可变参数,args接受的是一个元组
- **kw是关键字参数,kw接收的是一个字典
命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名关键字参数不要忘了写分隔符 *,否则定义的是位置参数。
警告:虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数很难懂。
1.5函数的返回值
return
#例子
def add1(a,b):
return a + b
add1(1,2)
# 3
1.6变量作用域
-
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
-
定义在函数内部的变量拥有局部作用域,该变量称为局部变量。
-
定义在函数外部的变量拥有全局作用域,该变量称为全局变量。
-
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。
-
当内部作用域想要修改外部作用域的变量时,就要用到global和nonlocal关键字了。
#global全局关键字的应用
num = 1
def func1():
global num
print(num)
num = 123
print(num)
func1()
#1
#123
内嵌函数
def outer():
print('outer函数在这被调用')
def inner():
print('inner函数在这被调用')
inner() # 该函数只能在outer函数内部被调用
outer()
# outer函数在这被调用
# inner函数在这被调用
闭包
- 闭包是函数式编程的一个重要的语法结构,是一种特殊的内嵌函数
- 如果在一个内部函数里对外层非全局作用域的变量进行引用,那么内部函数就被认为是闭包
- 通过闭包可以访问外层非全局作用域的变量,这个作用域被称为闭包作用域。
#例子
def funX(x):
def funY(y):
return x * y
return funY
i = funX(8)
print(type(i))
print(i(5))
#闭包的返回值通常是函数
def make_counter(init):
counter = [init]
def inc(): counter[0] += 1
def dec(): counter[0] -= 1
def get(): return counter[0]
def reset(): counter[0] = init
return inc, dec, get, reset
inc, dec, get, reset = make_counter(0)
#这里的inc,dec,get,reset分别对应make_counter(0)函数对应返回的四个值
inc() #counter[0] += 1
inc() #counter[0] += 1
inc() #counter[0] += 1
print(get()) # 3
dec() #counter[0] -= 1
print(get()) # 2
reset() #counter[0] = init
print(get()) # 0
#如果要修改闭包作用域中的变量则需要 nonlocal 关键字
def outer():
num = 10
def inner():
nonlocal num #这里将num变为外部嵌套函数内的变量,即outer函数下的变量
num = 100
print(num)
inner()
print(num)
outer()
# 100
# 100
递归
- 如果一个函数在内部调用自身本身,这个函数就是递归函数。
# 例1:n! = 1*2*3...*n
# 循环方法
n = 5
for k in range(1,5):
n *= k
print(n)
# 120
# 递归法
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
factorial(5)
# 120
#例2:斐波那契数列
#循环法
i = 0
j = 1
lst = [i,j]
for k in range(2,10):
m = lst[k-1] + lst[k-2]
lst.append(m)
print(lst)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
#递归法
def fib(n):
if n <= 1:
return n
else:
return fib(n-1) + fib(n-2)
fib(9)
# 34
2.Lambda表达式
2.1匿名函数的定义
在Python中有两类函数:
-
用def关键字定义的正规函数
-
用lambda关键字定义的匿名函数
Python里用lambda来创建匿名函数,语法结构如下:lambda argument_list:expression
-
lambda - 定义匿名函数的关键字
-
argument - 函数参数,可以是位置参数,默认参数,关键字参数和正规函数里的参数类型一样。
-
: - 冒号,函数参数和表达式之间要加个冒号
-
expression - 一个表达式,输入函数的参数,输出一些值。
-
expression中没有return语句,因为lambda不需要它来返回,表达式本身结果就是返回值。
-
匿名函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
#例子
sqr = lambda x:x ** 2
y = [sqr(x) for x in range(10)]
y
#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
sum1 = lambda x1,x2: x1 + x2
sum1(1,2)
# 3
sum2 = lambda *arg:sum(arg)
sum2(1,2,3,4)
# 10
2.2匿名函数的应用
函数式编程是指代码中每一块都是不可变的,都由纯函数的形式组成。这里的纯函数,是指函数本身相互独立、互不影响,对于相同的输入,总会有相同的输出,没有任何副作用。
#非函数式编程
def f(x):
for i in range(0,len(x)):
x[i] += 10
return x
x = [1,2,3]
f(x)
#[11,12,13]
#函数式编程
#函数式编程
def f(x):
y = []
for item in x:
y.append(item + 10)
return y
x = [1,2,3]
f(x)
#[11,12,13]
匿名函数常常应用于函数式编程的高阶函数 (high-order function)中,主要有两种形式:
- 参数是函数 (filter, map)
- 返回值是函数 (closure)
如,在 filter和map函数中的应用:
- filter(function, iterable) 过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。
odd = lambda x: x % 2 == 1
templist = filter(odd,[1,2,3,4,5,6,7])
list(templist)
#[1,3,5,7]
- map(function, *iterables) 根据提供的函数对指定序列做映射。
m1 = map(lambda x:x**2,[1,2,3])
list(m1)
#[1,4,9]
除了Python这些内置的函数之外,我们还可以自己定义一些高阶的函数。
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
练习题
1.怎么给函数编写⽂档?
help()是Python中内置函数,通过__doc__方法、help()函数可以查询Python中函数的⽤法。
在定义函数时,可以在函数内部编写⽂档字符串,⽂档字符串就是对函数的说明
文档字符串(DocStrings):
用于解释文档程序,帮助你的程序文档更加简单易懂。
在函数体的第一行使用一对三个单引号 或者一对三个双引号来定义文档字符串。
文档字符串使用惯例:它的首行简述函数功能,第二行空行,第三行为函数的具体描述。
可以使用 doc(注意双下划线)\help()函数调用函数中的文档字符串属性。
2.怎么给函数参数和返回值注解?
这个也可以在上述的文档里进行注解
3.闭包中,怎么对数字、字符串、元组等不可变元素更新。
闭包中我们利用nonlocal关键字来对一些不可变元素进行更新
4.分别根据每一行的首元素和尾元素大小对二维列表 a = [[6, 5], [3, 7], [2, 8]] 排序。(利用lambda表达式)
a = sorted(a,key=lambda x:x[0],reverse=False)
a
# [[2, 8], [3, 7], [6, 5]]
a = sorted(a,key= lambda x:x[1],reverse = False)
a
# [[6, 5], [3, 7], [2, 8]]
5.利用python解决汉诺塔问题?
def hanoi(n,x,y,z):
if n == 1:
print(x,"--->",z)
else:
hanoi(n-1,x,z,y)
print(x,"--->",z)
hanoi(n-1,y,x,z)