zoukankan      html  css  js  c++  java
  • 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))

    4、函数返回多个值

    希望构造一个返回多个值得函数,函数直接return一个元祖就行。

    def func():
        return 1,2,3

    a,b,c = func()

    a >> 1
    b >> 2
    c >> 3

    尽管func()看上去返回了多个值,实际上是先创建了一个元祖然后返回的,这个语法看上去比较奇怪,实际上我们使用的是用逗号生成的一个元祖,而不是用括号。比如下面:

    a = (1,2)
    a >> (1,2)
    
    a = 1,2
    a >> (1,2)

    当我们调用返回一个元祖的函数时候,通常我们会把结果赋值给多个变量,其实这就是元祖解包,返回结果也可以赋值给单个变量,这时候这个变量就是函数返回的那个元祖本身了。

  • 相关阅读:
    2018前端越来越流行的的技术
    程序员怎么写出一份漂亮的简历
    程序员怎么写出一份漂亮的简历
    程序员如何利用空闲时间挣零花钱
    扫雷(mine)
    一道数论好题(math)
    消失的数字(number)
    1380 没有上司的舞会 40分
    2596 售货员的难题 水深搜
    JavaEE Tutorials (17)
  • 原文地址:https://www.cnblogs.com/huiyichanmian/p/12103187.html
Copyright © 2011-2022 走看看