函数


In [1]:
%matplotlib inline
# 多行结果输出支持
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

可接受任意数量参数的函数

  • 为了能让一个函数接受任意数量的位置参数,可以使用一个*参数
  • 为了接受任意数量的关键字参数,使用一个以 **开头的参数
  • 这个和Packing和unpacking的用法是相同的,关键字参数一般是可以表示成字典的unpacking的
  • *arg1, **arg2就可以表示所有的参数形式
  • 一个*参数只能出现在函数定义中最后一个位置参数后面,而 **参数只能出现在最后一个参数。 有一点要注意的是,在*参数后面仍然可以定义其他参数

In [2]:
# 可变参数 packing and unpacking
def avg(first, *rest):
    return (first + sum(rest)) / (1 + len(rest))

# Sample use
avg(1, 2) # 1.5
avg(1, 2, 3, 4) # 2.5


Out[2]:
1.5
Out[2]:
2.5

In [3]:
# 如果你还希望某个函数能同时接受任意数量的位置参数和关键字参数,可以同时使用*和**
# 使用这个函数时,所有位置参数会被放到args元组中,所有关键字参数会被放到字典kwargs中
def anyargs(*args, **kwargs):
    print(args) # A tuple
    print(kwargs) # A dict

In [4]:
arg1 = 1, 2, 4
arg2 = {'q': 2, 'a': 3}
anyargs(*arg1, **arg2)


(1, 2, 4)
{'q': 2, 'a': 3}
def a(x, *args, y):
    pass

def b(x, *args, y, **kwargs):
    pass

只接受关键字参数的函数

  • 将强制关键字参数放到某个*参数或者单个*后面就能达到这种效果
  • 使用强制关键字参数会比使用位置参数表意更加清晰,程序也更加具有可读性
  • 使用强制关键字参数也会比使用**kwargs参数更好,因为在使用函数help的时候输出也会更容易理解

In [5]:
def recv(maxsize, *, block):
    'Receives a message'
    print(maxsize)

recv(1024, True) # TypeError


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-1317f310dbec> in <module>()
      3     print(maxsize)
      4 
----> 5 recv(1024, True) # TypeError

TypeError: recv() takes 1 positional argument but 2 were given

In [6]:
recv(1024, block=True) # Ok


1024

In [7]:
# 参数的默认值是None的意思是可以没有
def minimum(*values, clip=None):
    m = min(values)
    if clip is not None:
        m = clip if clip > m else m
    return m

minimum(1, 5, 2, -5, 10) # Returns -5
minimum(1, 5, 2, -5, 10, clip=0) # Returns 0


Out[7]:
-5
Out[7]:
0

给函数参数增加元信息

  • 使用函数参数注解是一个很好的办法,它能提示程序员应该怎样正确使用这个函数
  • PEP 3107 -- Function Annotations | Python.org
  • Python 函数注释 - CSDN博客
  • python解释器不会对这些注解添加任何的语义。它们不会被类型检查,运行时跟没有加注解之前的效果也没有任何差距。 然而,对于那些阅读源码的人来讲就很有帮助啦。第三方工具和框架可能会对这些注解添加语义。同时它们也会出现在文档中
  • 函数注解只存储在函数的 __annotations__属性中 func.__annotations__
  • 尽管注解的使用方法可能有很多种,但是它们的主要用途还是文档。 因为python并没有类型声明,通常来讲仅仅通过阅读源码很难知道应该传递什么样的参数给这个函数。 这时候使用注解就能给程序员更多的提示,让他们可以正确的使用函数

In [8]:
def add(x:int, y:int) -> int:
    return x + y

默认参数

  • 定义一个有可选参数的函数是非常简单的,直接在函数定义中给参数指定一个默认值,并放到参数列表最后就行了
  • 如果默认参数是一个可修改的容器比如一个列表、集合或者字典,可以使用None作为默认值
  • 默认参数的值仅仅在函数定义的时候赋值一次
  • 默认参数的值应该是不可变的对象,比如None、True、False、数字或字符串

In [9]:
# Using a list as a default value
# 可变的默认参数使用 None 作为默认值
def spam(a, b=None):
    # 这里使用 is
    if b is None:
        b = []
    pass

定义匿名或内联函数

  • 当一些函数很简单,仅仅只是计算一个表达式的值的时候,就可以使用lambda表达式来代替了
  • lambda表达式典型的使用场景是排序或数据reduce等:

In [13]:
y = lambda x, y: x ** y

In [14]:
y(2, 3)


Out[14]:
8

In [15]:
names = ['David Beazley', 'Brian Jones',
         'Raymond Hettinger', 'Ned Batchelder']
sorted(names, key=lambda name: name.split()[-1].lower())


Out[15]:
['Ned Batchelder', 'David Beazley', 'Raymond Hettinger', 'Brian Jones']

In [20]:
'David Beazley'.split()[-1].lower()


Out[20]:
'beazley'

In [22]:
x = 10
a = lambda y: x + y
x = 20
b = lambda y: x + y

In [23]:
a(10)


Out[23]:
30

In [24]:
b(10)


Out[24]:
30
  • 这其中的奥妙在于lambda表达式中的x是一个自由变量, 在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的
  • lambda 表达式的参数是在运行时绑定的,而函数定义的参数是在定义的时候才绑定的,两者是存在这不同的
  • 如果你想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可

In [26]:
x = 10
a = lambda y, x=x: x + y
x = 20
b = lambda y, x=x: x + y

In [27]:
a(10)


Out[27]:
20

In [28]:
b(10)


Out[28]:
30

通过使用函数默认值参数形式,lambda函数在定义时就能绑定到值

减少可调用对象的参数个数

  • 如果需要减少某个函数的参数个数,你可以使用 functools.partial() 。 partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数
  • partial() 通常被用来微调其他库函数所使用的回调函数的参数

In [29]:
def spam(a, b, c, d):
    print(a, b, c, d)

In [30]:
from functools import partial
s2 = partial(spam, d=3)

In [31]:
s2(1, 2, 3)


1 2 3 3

In [ ]: