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

(1)接受任意数量的位置参数,可以使用一个*参数

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

(2)接受任意数量的关键字参数,使用一个以**开头的参数

import html

def make_element(name, value, **attrs):
    keyvals = [' %s="%s"' % item for item in attrs.items()]
    attr_str = ''.join(keyvals)
    element = '<{name}{attrs}>{value}</{name}>'.format(
                name=name,
                attrs=attr_str,
                value=html.escape(value))
    return element

# Example
# Creates '<item size="large" quantity="6">Albatross</item>'
make_element('item', 'Albatross', size='large', quantity=6) #size='large', quantity=6 类似于key=value的字典形式即为关键字参数

注意:

一个*参数出现在函数定义中最后一个位置参数后面,在*参数后面仍然可以定义其他参数。而 **参数只能出现在最后一个参数。

2.只接受关键字参数的函数

解决方法:将强制关键字参数放到某个*参数或者单个*后面

def recv(maxsize, *, block):
    'Receives a message'
    pass

recv(1024, True) # 类型错误
recv(1024, block=True) # Ok



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

3.给函数参数增加元信息

函数参数注解

def add(x:int, y:int) -> int:

    return x + y

注:python解释器不会对这些注解添加任何的语义,不会被类型检查

4.返回多个值的函数

为了能返回多个值,函数直接return一个元组

>>> def myfun():
... return 1, 2, 3
...
>>> a, b, c = myfun()
>>> a
1
>>> b
2
>>> c
3

注意:使用的是逗号来生成一个元组,而不是用括号 二者功能一致,均可为元组 如下:

>>> a = (1, 2) # With parentheses
>>> a
(1, 2)
>>> b = 1, 2 # Without parentheses
>>> b
(1, 2)
>>>

5.定义有默认参数的函数

定义一个有可选参数的函数是非常简单的,直接在函数定义中给参数指定一个默认值,并放到参数列表最后就行了。

def spam(a, b=42):
    print(a, b)

spam(1) # Ok. a=1, b=42
spam(1, 2) # Ok. a=1, b=2

如果默认参数是一个可修改的容器比如一个列表、集合或者字典,可以使用None作为默认值

# Using a list as a default value
def spam(a, b=None):
    if b is None:
        b = []
    ...

仅仅测试下某个默认参数是不是有传递进来

_no_value = object()

def spam(a, b=_no_value):
    if b is _no_value:
        print('No b value supplied')
    ...


>>> spam(1, 2) # b = 2
#有没有默认参数还是有区别的
>>> spam(1)
No b value supplied
>>> spam(1, None) # b = None
>>>

6.定义匿名或内联函数

当一些函数很简单,仅仅只是计算一个表达式的值的时候,就可以使用lambda表达式来代替

#求和

>>> add = lambda x, y: x + y
>>> add(2,3)
5
>>> add('hello', 'world')
'helloworld'
>>>


#排序
>>> names = ['David Beazley', 'Brian Jones',
...         'Raymond Hettinger', 'Ned Batchelder']
>>> sorted(names, key=lambda name: name.split()[-1].lower()) #按照空格分割 空格后单词作为排序依据
['Ned Batchelder', 'David Beazley', 'Raymond Hettinger', 'Brian Jones']
>>>

注意:

尽管lambda表达式允许定义简单函数,但是它的使用是有限制的。只能指定单个表达式,它的值就是最后的返回值。也就是说不能包含多个语句、条件表达式、迭代以及异常处理等等。

7.匿名函数捕获变量值

>>> x = 10
>>> a = lambda y: x + y
>>> x = 20
>>> b = lambda y: x + y
>>>

>>> a(10)
30
>>> b(10)
30
>>>

为什么不是20和30 ?

lambda表达式中的x是一个自由变量, 在运行时绑定值,而不是定义时就绑定 在调用这个lambda表达式的时候,x的值是执行时的值 所以 x值都为最新赋值20

想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可

>>> x = 10
>>> a = lambda y, x=x: x + y
>>> x = 20
>>> b = lambda y, x=x: x + y
>>> a(10)
20
>>> b(10)
30
>>>

注意,在循环迭代过程中对于lambda表达式的使用:

>>> funcs = [lambda x: x+n for n in range(5)]
>>> for f in funcs:
... print(f(0))
...
4
4
4
4
4
>>>


>>> funcs = [lambda x, n=n: x+n for n in range(5)]
>>> for f in funcs:
... print(f(0))
...
0
1
2
3
4
>>>

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

partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数

def spam(a, b, c, d):
    print(a, b, c, d)


>>> from functools import partial
>>> s1 = partial(spam, 1) # 默认 a = 1 
>>> s1(2, 3, 4)
1 2 3 4
>>> s1(4, 5, 6)
1 4 5 6
>>> s2 = partial(spam, d=42) # d = 42
>>> s2(1, 2, 3)
1 2 3 42
>>> s2(4, 5, 5)
4 5 5 42
>>> s3 = partial(spam, 1, 2, d=42) # 默认前两个a = 1, b = 2, 以及d = 42
>>> s3(3)
1 2 3 42
>>> s3(4)
1 2 4 42
>>> s3(5)
1 2 5 42
>>>

9.将单方法的类转换为函数

有一个除 __init__() 方法外只定义了一个方法的类。为了简化代码,将它转换成一个函数

大多数情况下,可以使用闭包来将单个方法的类转换成函数

from urllib.request import urlopen

class UrlTemplate:
    def __init__(self, template):
        self.template = template

    def open(self, **kwargs):
        return urlopen(self.template.format_map(kwargs))


#利用闭包形式 转单一方法类为函数

def urltemplate(template):
    def opener(**kwargs):
        return urlopen(template.format_map(kwargs))
    return opener

定义UrlTemplate类的唯一目的就是先在某个地方存储模板值template,以便将来可以在open()方法中使用

而一个闭包就是一个函数, 只不过在函数内部带上了一个额外的变量环境。闭包关键特点就是它会记住自己被定义时的环境。

10.访问闭包中定义的变量

扩展函数中的某个闭包,允许它能访问和修改函数的内部变量

通常来讲,闭包的内部变量对于外界来讲是完全隐藏的。 但是,你可以通过编写访问函数并将其作为函数属性绑定到闭包上来实现这个目的

def sample():
    n = 0
    # Closure function
    def func():
        print('n=', n)

    # Accessor methods for n
    def get_n():
        return n

    def set_n(value):
        nonlocal n
        n = value

    # Attach as function attributes
    func.get_n = get_n
    func.set_n = set_n
    return func

>>> f = sample()
>>> f()
n= 0
>>> f.set_n(20)
>>> f()
n= 20
>>> f.get_n()
20
>>>

上述操作类似于模拟一个类访问private私有变量

闭包的方案运行起来要快与类操作,大部分原因是因为对实例变量的简化访问, 闭包更快是因为不会涉及到额外的self变量。