Python基础—初识函数(二)

1、给函数参数增加元信息

写好一个函数,然后想为这个函数的参数增加一些额外的信息,这样的话其他使用者就能清楚的知道这个函数应该怎么使用。

使用函数参数注解是一个很好的办法,它能提示程序员应该怎样正确使用这个函数。 例如,下面有一个被注解了的函数:

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

python解释器不会对这些注解添加任何的语义。它们不会被类型检查,运行时跟没有加注解之前的效果也没有任何差距。 然而,对于那些阅读源码的人来讲就很有帮助啦。第三方

工具和框架可能会对这些注解添加语义。同时它们也会出现在文档中。

help(add)

Help on function add in module __main__:

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

尽管可以使用任意类型的对象给函数添加注解(例如数字,字符串,对象实例等等),不过通常来讲使用类或者字符串会比较好点。

函数注解只存储在函数的 __annotations__ 属性中。例如:

add.__annotations__
{'x': int, 'y': int, 'return': int}

注解的使用方法可能有很多种,但是它们的主要用途还是文档。 因为python并没有类型声明,通常来讲仅仅通过阅读源码很难知道应该传递什么样的参数给这个函数。 这时候

使用注解就能给程序员更多的提示,从而可以正确的使用函数。

2、有默认参数的函数

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

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

# 使用一个列表作为默认值
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)   >> No b value supplied
spam(1,2)   >> a=1,b=2
spam(1,None)   >> a=1, b=None   # 可以发现到传递一个None值和不传值两种情况是有差别的

注意:默认参数的值仅仅在函数创建的时候赋值一次。

x = 20
def spam(a,b=x):
    print(a,b)

spam(1)  >> 1,20

x = 30
spam(1)   >> 1,20

当我们改变x的值的时候对默认参数值并没有影响,这是因为在函数定义的时候就已经确定了它的默认值了。

其次,默认参数的值应该是不可变的对象,比如None、True、False、数字或字符串。 特别的,千万不要像下面这样写代码:

def spam(a,[]):
    pass

如果这么做了,当默认值在其他地方被修改后将会遇到各种麻烦。这些修改会影响到下次调用这个函数时的默认值。比如:

def spam(a,b=[])
    print(b)
    return b

x = spam(1)

>> []

x.append(10)

x = spam(1)

>>[10]

这种结果不是我们想要的,为了避免这种结果的发生,尽量将默认值设为None,然后在函数里检查它,前面的例子就是这么做的,在测试None值时,is操作符很重要,也是这种操作的关键点,我们时常会写成下面的形式:

def spam(a, b=None):
    if not b:
        b=[]

这么写的问题在于尽管None值确实是被当成False, 但是还有其他的对象(比如长度为0的字符串、列表、元组、字典等)都会被当做False。 因此,上面的代码会误将一些其他输入也当成是没有输入。比如:

spam(1) # OK
x = []
spam(1, x) # 和上面一样,参数可以随意改变
spam(1, 0)   # 数字,不可变类型
spam(1, '')   # 字符串,不可变类型

3、匿名函数捕获变量值

先看下面代码效果:

x = 10
a = lambda y : x+y

x = 20

b = lambda y : x+y

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

lambda表达式中的x是一个自由变量, 在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。 因此,在调用这个lambda表达式的时候,x的值是执行时的值。例如:

x = 10
a(10)   >> 20

x = 20

b(10)  >> 30

如果想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可,就像下面这样:

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表达式列表,并期望函数能在定义时就记住每次的迭代值。

# 错误写法

a = [lambda x:x+n for n in range(4)]

for f in a:
    f(0)
    print(f(0))

上面的写法输出全是4,跟刚开始的例子一样,函数运行时,n的值是迭代的最后一个值。

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

a = [lambda x,n=n:x+n for n in range(4)]

for f in a:
    f(0)
    print(f(0))

----------------

猜你喜欢

转载自www.cnblogs.com/huiyichanmian/p/12103187.html