“专业人士笔记”系列目录:
创帆云:Python成为专业人士笔记--强烈建议收藏!每日持续更新!说明
Python中的函数提供有组织的、可重用的和模块化的代码来执行一组特定的操作。函数简化了编码过程,防止了冗余逻辑,并使代码更容易理解。本文描述Python中函数的声明和使用。Python有很多内置函数,比如print()、input()、len()。除了内置函数外,您还可以创建自己的函数来执行更具体的任务,这些函数称为用户定义函数。
定义和调用函数
使用def语句是python中定义函数的最常见方式。这个语句是一个具有以下语法的所谓的单复合语句:
def function_name(parameters):
statement(s)
函数名被称为函数的标识符。由于函数定义是一个可执行语句,它的执行将函数名绑定到函数对象,稍后可以使用标识符调用该函数对象。
parameters是一个可选的标识符列表,在调用函数时绑定到作为参数提供的值。一个函数可以有任意数量的参数,这些参数由逗号分隔。
statement(s) – 是一段代码(也称为函数体),是每次执行函数时需要执行的非空语句。这意味着函数主体不能为空,就像任何缩进块的代码一样。
下面是一个简单函数定义的例子,其目的是在每次调用Hello时打印Hello :
def greet():
print("Hello")
#现在调用函数:
greet()
#输出: Hello
这是函数定义的另一个例子,它接受一个参数,并在每次调用函数时传入参数的值:
def greet_two(greeting):
print(greeting)
#调用函数:
greet_two("Howdy")
#输出:
Howdy
你也可以给函数的参数一个默认值:
def greet_two(greeting="Howdy"):
print(greeting)
现在可以调用该函数而不给出值 :
greet_two()
#输出: Howdy
你会注意到,与许多其他语言不同,您不需要显式地声明函数的返回类型。Python函数可以通过return关键字返回任何类型的值,一个函数可以返回任意数量的不同类型
def many_types(x):
if x < 0:
return '我是',"Hello!"
else:
return 0,'这是错误的'
print(many_types(1))
print(many_types(-1))
print(many_types(-1)[0]) #由于是返回多个值,将以元组的形式返回,通过元组索引的方式找到具体一个值
#输出:
(0, '这是错误的')
('我是', 'Hello!')
我是
没有返回语句的函数在执行结束时总是返回None :
def do_nothing():
pass
print(do_nothing())
# #输出: None
如前所述,函数定义必须有一个函数体,即一个非空的语句序列。因此,pass语句被用作函数体,当它被执行时是一个空操作,什么也没有发生。当语法上需要一个语句,但不需要执行任何代码时,它作为占位符非常有用。
带任意数量参数的函数
按位置调用参数 :
定义一个能够接受任意数量参数的函数可以通过在其中一个参数前面加上*来完成 :
def func(*args):
# args将是一个包含传入的所有值的元组
for i in args:
print(i)
func(1,2,3,4,5,6,7) #参数可以是不同的数据类型
#输出:
1
2
3
4
5
6
7
不能为args提供默认值,例如func(*args=[1,2,3])将引发语法错误(甚至无法通过编译)。
按参数名调用参数:
通过在定义中定义一个参数并在其前面加上两个*,可以获得任意数量的带有名称的参数
def func(**kwargs):
# kwargs将是一个字典,其中包含作为键的名称和值
for name, value in kwargs.items():
print(name, value)
#值传入调用
func(value1=1, value2=2, value3=3)
#输出:
value1 1
value2 2
value3 3
#字典变量传入调用
my_dict = {'foo': 1, 'bar': 2}
func(**my_dict)
#输出:
foo 1
bar 2
你不能在没有名称的情况下提供参数,例如func(1、2、3)将引发一个类型错误
kwargs是一个普通的本地python字典。例如,args[‘value1’]将为参数value1提供值。请确保预先检查是否有这样的参数或将引发一个KeyError
注意:
你可以将这些参数与其他可选参数和必需参数混合使用,但是定义中的顺序很重要
1、位置关键字参数排在第一位 (必填参数)
2、然后是任意数量* arg参数。 (可选的)
3、接下来是关键字参数。( 必填参数 )
4、 最后是任意数量参数名调用**kwargs。(可选)
# 如下顺序 |-positional-|-optional-|---keyword-only--|-optional-|
def func(arg1, arg2=10 , *args, kwarg1, kwarg2=2, **kwargs):
pass
当然,对于嵌套函数的参数调用方式,也是一致的,如下代码:
#f1子函数
def f1(**kwargs):
print(len(kwargs))
#主函数
def fn(**kwargs):
print(kwargs)
f1(**kwargs)
#函数调用
fn(a=1, b=2)
#输出:
{'a': 1, 'b': 2}
2
Lambda 匿名函数
lambda关键字创建一个包含单个表达式的内联函数。该表达式的值是函数在调用时返回的值。
考虑如下函数:
def greeting():
return "Hello"
print(greeting())
#输出:Hello
这可以写成一个lambda函数,如下所示 :
greet_me = lambda: "Hello"
这将创建一个名为greet_me的内联函数,该函数返回Hello。注意,当使用lambda创建函数时,不需要编写return; “:”之后的值将自动返回
一旦将此lambda赋值给一个变量,它就可以像普通函数一样使用 :
print(greet_me())
当然,lambda也可以传入参数:
#定义函数并赋值给变量,函数功能是进行大写转换
strip_and_upper_case = lambda s: s.strip().upper()
#调用该函数
print(strip_and_upper_case(" Hello "))
#输出:
HELLO
它们也可以采用任意数量的参数(*args)/关键字参数(**kwargs),就像普通函数一样:
greeting = lambda x, *args, **kwargs: print(x, args, kwargs)
print(greeting('hello', 'world', world='world'))
#输出:
hello ('world',) {'world': 'world'}
lambdas通常用于短函数,便于在调用它们的地方快速定义函数(通常涉及sort、filter和map等)
例如,下面这行代码对字符串列表进行排序,忽略它们的大小写,忽略开头和结尾的空格 :
sorted( [" foo ", " bAR", "BaZ "], key=lambda s: s.strip().upper())
# 值:
# [' bAR', 'BaZ ', ' foo ']
如果只去除空格,按原始大小写排序:
sorted( [" foo ", " bAR", "BaZ "], key=lambda s: s.strip())
#值:['BaZ ', ' bAR', ' foo ']
涉及map的例子:
sorted( map( lambda s: s.strip().upper(), [" foo ", " bAR", "BaZ "]))
#值:['BAR', 'BAZ', 'FOO']
sorted( map( lambda s: s.strip(), [" foo ", " bAR", "BaZ "]))
#值:['BaZ', 'bAR', 'foo']
数值列表的例子:
my_list = [3, -4, -2, 5, 1, 7]
sorted( my_list, key=lambda x: abs(x))
#值:
#[1, -2, 3, -4, 5, 7]
list( filter( lambda x: x>0, my_list))
#值:
#[3, 5, 1, 7]
list( map( lambda x: abs(x), my_list))
#值
#[3, 4, 2, 5, 1, 7]
可以从lambda函数内部调用其他函数
def foo(msg):
print(msg)
greet = lambda x = "hello world": foo(x)
greet()
#输出:
hello world
这很有用,因为lambda可能只包含一个表达式,通过使用这个辅助函数,可以运行多个语句
用可选参数定义函数
可选参数可以通过为参数名分配(使用“=”)一个默认值来定义
def make(action='nothing'):
return action
调用这个函数有三种不同的方法 :
make("fun")
#值: fun
make(action="sleep")
#值: sleep
#参数是可选的,因此如果参数没有传入,函数将使用默认值
make()
#值: nothing
可变参数的函数
当使用带有可变数据类型的参数(如list列表)作为默认值时会出现一个问题,即这个参数的默认值可能会随着函数调用而改变值,这可能会导致意外的行为
解释:
出现这个问题是因为函数的默认参数只在定义函数时初始化一次,而不是(像许多其他语言一样)在调用函数时初始化。默认值存储在函数对象的__defaults__成员变量中
def f(a, b=42, c=[]):
pass
print(f.__defaults__)
#输出: (42, [])
对于不可变类型(请参阅参数传递和可变性),这不是问题,因为没有办法使变量发生变化; 因此,可以保证后续操作具有相同的默认值。但是,对于可变类型,通过调用其各种成员函数,原始值可能发生变化。因此,对函数的连续调用不能保证具有初始默认值。
def append(elem, to=[]):
to.append(elem)
# 函数的调用会使默认变量“to”发生了变化
return to
print(append(1))
# 输出: [1]
print(append(2))
# 将其追加到了内部存储的列表
# 输出: [1, 2]
print(append(3, []))
# 使用新创建的列表(第2个参数)可以得到预期的结果
# 输出: [3]
# 再次不带参数地调用它将再次追加到内部存储的列表中
print(append(4))
# 输出: [1, 2, 4]
解决方案 :
如果你需要确保参数的默认值不可变,那么解决方案就是始终使用不可变类型作为默认参数。
当需要一个可变类型作为默认值时,一个常见的习惯做法是使用None(不可变的)作为默认参数,然后将实际的默认值赋给参数变量:
def append(elem, to=None):
if to is None:
to = []
to.append(elem)
return to
参数的传递和可变性
首先,一些术语 :
实际参数: 传递给函数的变量
形式参数: 函数中使用的变量
在Python中,参数是通过赋值传递的(与其他语言不一样,在其他语言中,参数可以是通过值/引用/指针传递)
1、改变形式参数会同时改变实际参数(如果参数的类型是可变的)
def foo(x):
x[0] = 9 # 这会使列表值发生变化
print(x)
y = [4, 5, 6]
#用y作为参数调用foo
foo(y)
#输出:[9, 5, 6]
print(y)
#输出:[9, 5, 6]
# 我们看到,y值作为参数传递入函数后,其自身的值也发生了变化
2、重新分配形式参数不会改变实际参数
def foo(x):
x=[3,4,5] #这里 ,将x重新赋值了
x[2]=8 #继续对参数x操作并不会影响y的值
print(x)
y = [4, 5, 6]
#用y作为参数调用foo
foo(y)
#输出:[3, 4, 8]
print(y)
#输出:[4, 5, 6]
# 我们看到,y值并未被曩
注意: 在Python中,我们不给变量赋值,而是把变量绑定到对象上
不可变的类型: 整数、字符串、元组等等。所有的操作都是复制
可变的类型: 列表、字典、集合等等,可能会更改原始值
函数返回值
函数可以返回一个可以直接使用的值 :
def give_me_five():
return 5
print(give_me_five())
#输出: 5
#或者保存该值供以后使用
num = give_me_five()
print(num)
# 打印保存的返回值
#输出: 5
#或对任何操作使用该值:
print(give_me_five() + 10)
#输出: 15
如果在函数中遇到return,则函数将立即退出,并且不进行后续操作 :
def give_me_another_five():
return 5
print('这行提示不会打印')
print(give_me_another_five())
#输出: 5
函数还可以同时返回多个值(以元组的形式) :
def give_me_two_fives():
return 5, 5
#返回一个元组:(5,5)
没有返回语句的函数隐式返回None。类似地,带有return语句的函数,如果其后面没有跟返回值或变量,则也返回None。
函数闭包
Python中的闭包是通过函数调用创建的。这里,对makeInc的调用创建了在函数inc中引用的x的绑定。对makeInc的每次调用都会创建该函数的一个新实例,但每个实例都有一个不同的链接绑定到x
def makeInc(x):
def inc(y):
return y + x
return inc
incOne = makeInc(1) #这里进行了x变量的绑定
incFive = makeInc(5) #这里进行了x变量的绑定
print(incOne(5)) # 这里对y变量进行了绑定,输出 6
print(incFive(5)) # 同上,输出 10
注意,在常规闭包中,封闭函数完全继承其封闭环境中的所有变量,但在此构造中,封闭函数只能对继承的变量进行读访问,但不能对它们进行赋值 :
def makeInc(x):
def inc(y):
#这里对X进行赋值了,是不允许的
x += y
return x
return inc
incOne = makeInc(1)
incOne(5)
# 报错:UnboundLocalError: local variable 'x' referenced before assignment
Python 3提供了 Nonlocal 关键字,用于嵌套函数中对子函数对父函数变量的赋值:
def makeInc(x):
def inc(y):
nonlocal x
#现在允许为x赋值了
x += y
return x
return inc
incOne = makeInc(1)
incOne(5) # #值 6
强制使用命名参数
在python3中,可以在函数签名中加入一个星号,以确保剩下的参数只能通过关键字参数传递
def f(a, b, *, c):
pass
print(f(1, 2, 3))
#这个会报错
print(f(1, 2, c=3))
#正常运行,返回None
嵌套函数
python中的函数是一类对象,它们可以在任何范围内定义
def fibonacci(n):
#子函数
def step(a,b):
return b, a+b
a, b = 0, 1
for i in range(n):
a, b = step(a, b)
return a
函数的闭包范围可以像传递任何其他类型的对象一样传递:
def make_adder(n):
def adder(x):
return n + x
return adder
add5 = make_adder(5) #add5代表对其子函数adder的引用
add6 = make_adder(6)
add5(10) #adder的调用并传递参数
#返回: 15
add6(10)
#返回: 16
递归限制
递归调用的深度是有限制的,当达到限制时,会引发一个RuntimeError异常
def cursing(depth):
try:
cursing(depth + 1) #每次递归调用+1
except RuntimeError as RE:
print('我递归了 {} 次!'.format(depth))
cursing(0)
# 输出: 我递归了 998 次!
可以使用sys.setrecursionlimit(limit)更改递归深度限制,并通过sys.getrecursionlimit()检查这个限制
我们把上面的代码再改一下:
import sys
def cursing(depth):
try:
cursing(depth + 1) # actually, re-cursing
except RuntimeError as RE:
print('我递归了 {} 次!'.format(depth))
sys.setrecursionlimit(20000)
cursing(0)
#我递归了 19998 次!
From Python 3.5, the exception is a RecursionError, which is derived from RuntimeError.
从Python 3.5开始,出现异常后会抛RecursionError ,它是从RuntimeError派生出来的。
递归Lambda函数
创建递归lambda函数的一种方法是将函数赋值给一个变量,然后在函数本身中引用该变量。一个常见的例子是对一个数的阶乘进行递归计算——如下面的代码所示 :
lambda_factorial = lambda i:1 if i==0 else i*lambda_factorial(i-1)
print(lambda_factorial(4))
# 这是典型的递归,每次输入的参数减1后相乘;
#当i为0时,直接返回1,结束递归,所以最终输出结果是:
# 4 * 3 * 2 * 1 = 12 * 2 = 24
递归函数
递归函数是在其定义中调用自身的函数。例如,数学函数factorial(n)的业务公式为factorial(n) = n*(n-1)*(n-2)*…*3*2*1,其递归代码可编程为 :
def factorial(n):
# n必须为整数
if n == 0:
return 1
else:
return n*factorial(n-1)
#输出:
factorial(0)
# 1
factorial(1)
# 1
factorial(2)
# 2
factorial(3)
# 6
像预期的那样;注意,这个函数是递归的,因为第二个返回factorial(n-1),其中函数在其定义中调用自身
当然,此阶乘函数还可以简化:
factorial = lambda n: 1 if n == 0 else n*factorial(n-1)
此函数的输出与上面相同
好了,今天的分享就到这里,禁止转载,违者必究