目录
- 简介
- 内置函数
- 自定义函数参数的默认值
- 关键字参数
- 特殊参数
- 参数解包
- Lambda
- 函数标注
简介
函数是结构化编程的基础,也是代码复用的基石。Python中通过def来自定义函数。本文将会深入探索Python中函数的秘密。
内置函数
除了用户的自定义函数之外,Python内置了一些非常有用的函数:
内置函数 | ||||
---|---|---|---|---|
abs() |
delattr() |
hash() |
memoryview() |
set() |
all() |
dict() |
help() |
min() |
setattr() |
any() |
dir() |
hex() |
next() |
slice() |
ascii() |
divmod() |
id(ad8) |
object() |
sorted() |
bin() |
enumerate() |
input() |
oct() |
staticmethod() |
bool() |
eval() |
int() |
open() |
str() |
breakpoint() |
exec() |
isinstance() |
ord() |
sum() |
bytearray() |
filter() |
issubclass() |
pow() |
super() |
bytes() |
float() |
iter() |
print() |
tuple() |
callable() |
format() |
len() |
property() |
type() |
chr() |
frozenset() |
list() |
range() |
vars() |
classmethod() |
getattr() |
locals() |
repr() |
zip() |
compile() |
globals() |
map() |
reversed() |
__import__() |
complex() |
hasattr() |
max() |
round() |
自定义函数
Python中使用def来定义函数,并使用return来返回特定的值。
看一个简单的函数的例子:
def my_function(x, y, z):if z > 1:return z * (x + y)else:return z / (x + y)
把我们之前讲的斐波拉赫数列的例子重新用函数来定义,可以这样写:
def fib(n):a, b = 0, 1while a < n:print(a, end=\' \')a, b = b, a+bprint()# 调用函数fib(1000)
函数的内容需要使用空格或者tab来进行缩进。
参数的默认值
在Python中,我们可以给参数设置默认值,这样如果在函数调用的过程中没有传递参数的时候,就会使用默认值作为参数。
在我们之前定义的函数my_function中,我们可以给z设置一个默认值:
def my_function(x, y, z=10):if z > 1:return z * (x + y)else:return z / (x + y)
这样我们在调用my_function可以只用传递两个参数,最后的z可以使用默认的参数值。
注意,默认值只会执行一次,如果你传入的参数是可变对象(列表,字典和类实例)的话,我们需要注意这个问题:
def f(a, L=[]):L.append(a)return Lprint(f(1))print(f(2))print(f(3))# 输出[1][1, 2][1, 2, 3]
如果不想在后面的调用中共享默认值,那么可以把默认值的赋值放到函数体内部:
def f(a, L=None):if L is None:L = []L.append(a)return L
关键字参数
我们可以使用key=value的方式对函数进行调用。
还是前面的函数:
def my_function(x, y, z=10):if z > 1:return z * (x + y)else:return z / (x + y)
我们可以这样调用:
my_function(1,y=3,z=5)my_function(1,y=3)
但是不能这样用:
my_function(y=3,1)
关键字的参数必须要放在非关键词参数的后面。也不能对参数进行多次赋值:
>>> def function(a):... pass...>>> function(0, a=0)Traceback (most recent call last):File \"<stdin>\", line 1, in <module>TypeError: function() got multiple values for keyword argument \'a\'
通过上面的讨论我们可以看出,Python函数中的参数有两种,一种是带默认值的参数,一种是不带默认值的参数。
注意,不带默认值的参数一定要在带默认值的参数之前 。
看一个错误的例子:
In [69]: def fa(a=100,b,c=200):...: passFile \"<ipython-input-69-d5678b64f352>\", line 1def fa(a=100,b,c=200):^SyntaxError: non-default argument follows default argument
而向函数传递参数也有两种方式,一种是不带关键字的传递,一种是带关键字的传递。
注意,非关键词参数的传递一定要在关键词参数传递之前。
举个错误的例子:
In [70]: def fa(a,b=100,c=200):...: pass...:In [71]: fa(a=100,30)File \"<ipython-input-71-5a229b8e420e>\", line 1fa(a=100,30)^SyntaxError: positional argument follows keyword argument
那么问题来了,如果有多个关键词参数和多个非关键词参数,有没有简便的方法来定义这样的函数呢?
有的,那就是
*arguments
和
**keywords
*arguments
用来接收所有多余的非关键词参数。而
**keywords
用来接收所有额外的关键词参数。
注意,
*arguments
一定要出现在
**keywords
的前面。
举个例子:
def cheeseshop(kind, *arguments, **keywords):print(\"-- Do you have any\", kind, \"?\")print(\"-- I\'m sorry, we\'re all out of\", kind)for arg in arguments:print(arg)print(\"-\" * 40)for kw in keywords:print(kw, \":\", keywords[kw])
我们可以这样调用:
cheeseshop(\"Limburger\", \"It\'s very runny, sir.\",\"It\'s really very, VERY runny, sir.\",shopkeeper=\"Michael Palin\",client=\"John Cleese\",sketch=\"Cheese Shop Sketch\")
将会得到下面的结果:
-- Do you have any Limburger ?-- I\'m sorry, we\'re all out of LimburgerIt\'s very runny, sir.It\'s really very, VERY runny, sir.----------------------------------------shopkeeper : Michael Palinclient : John Cleesesketch : Cheese Shop Sketch
特殊参数
函数可以按位置传参,可以按照关键词传参,也可以混合传参。
在某些情况下,我们可能需要限制传参的类型,比如只接收按位置传递,只接收按关键词传递,或者只接受混合传递。
看下特殊参数的定义:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):----------- ---------- ----------| | || 按位置或者关键词 || - 只允许按关键词传递-- 只允许按位置传递
注意,参数之间是以 / 和 * 来进行区分的。
我们举个例子:
>>> def standard_arg(arg):... print(arg)...>>> def pos_only_arg(arg, /):... print(arg)...>>> def kwd15b0_only_arg(*, arg):... print(arg)...>>> def combined_example(pos_only, /, standard, *, kwd_only):... print(pos_only, standard, kwd_only)
上面定义了4种传参方式的函数。
第一个函数就是标准形式,可以按位置传递,也可以按关键词传递。
第二个函数只允许按照位置传递。
第三个函数只允许按照关键词来传递。
第四个函数是混合模式。
参数解包
有时候我们需要将列表或者字典的值转换为函数的参数。那么就需要用到参数解包的功能。
*
操作符 可以用来解包列表和元组。
>>> list(range(3, 6)) # normal call with separate arguments[3, 4, 5]>>> args = [3, 6]>>> list(range(*args)) # call with arguments unpacked from a list[3, 4, 5]
**
操作符 可以用来解包字典。
>>> def parrot(voltage, state=\'a stiff\', action=\'voom\'):... print(\"-- This parrot wouldn\'t\", action, end=\' \')... print(\"if you put\", voltage, \"volts through it.\", end=\' \')... print(\"E\'s\", state, \"!\")...>>> d = {\"voltage\": \"four million\", \"state\": \"bleedin\' demised\", \"action\": \"VOOM\"}>>> parrot(**d)
Lambda
熟悉java的朋友可能知道,在JDK8中,Java引入了Lambda表达式。同样的Python中也有Lambda。
你可以将Lambda看做是匿名函数。可以在任何需要函数的地方使用Lambda表达式。
看一个Lambda的例子:
>>> def make_incrementor(n):... return lambda x: x + n...>>> f = make_incrementor(42)>>> f(0)42>>> f(1)43
还可以将lambda的返回值作为参数:
>>> pairs = [(1, \'one\'), (2, \'two\'), (3, \'three\'), (4, \'four\')]>>> pairs.sort(key=lambda pair: pair[1])>>> pairs[(4, \'four\'), (1, \'one\'), (3, \'three\'), (2, \'two\')]
函数标注
之前我们讨论的是简单的自定义函数形式,我们并不知道函数的参数类型和返回值类型,其实函数可以写得更加详细一些,这就要用到函数标注了。
所谓函数标注就是用户自定义函数中的类型的可选元数据信息。
函数标注是以字典的形式存放在
__annotations__
属性中的。我们在参数的名称后面加上冒号,后面跟一个表达式,那么这个表达式会被求值为标注的值。对于返回值来说,返回值标注的定义是加上一个组合符号
->
,后面跟一个表达式,该标注位于形参列表和表示 def 语句结束的冒号之间。
举个例子:
>>> def f(ham: str, eggs: str = \'eggs\') -> str:... print(\"Annotations:\", f.__annotations__)... print(\"Arguments:\", ham, eggs)... return ham + \' and \' + eggs...>>> f(\'spam\')Annotations: {\'ham\': <class \'str\'>, \'return\': <class \'str\'>, \'eggs\': <class \'str\'>}Arguments: spam eggs\'spam and eggs\'
其实使用函数标注写出来的程序更加清晰,可读性更高。
本文已收录于 http://www.flydean.com/05-python-function/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!