美文网首页python3 运维开发
Python3 函数的更高级的话题(一)

Python3 函数的更高级的话题(一)

作者: 运维开发_西瓜甜 | 来源:发表于2018-11-24 20:54 被阅读28次

    本次我要给大家分享一些更加高级和不常见的函数定义与使用模式。

    涉及到的内容包括默认参数、任意数量参数、强制关键字 参数、注解和闭包等。

    * 表达式

    我们知道在定义函数的时候,用 * 表达式来收集所有未指明的位置参数。

    def avg(first, *rest):
        """求平均值"""
        return (first + sum(rest)) / (1 + len(rest))
        
    # 简单使用
    avg(1, 2)  # 1.5 
    avg(1, 2, 3, 4)  # 2.5
    

    当然在调用函数的时候,也可以使用 * 表达式来把一个序列类型数据中的元素一一解开。

    def show_args(*args):
        print(args)
    
    show_args(['a','b'])  # (['a', 'b'],)
    show_args(*['a','b'])  # ('a', 'b')
    

    print() 函数的另外一种用法

    print(['a','b'], sep='\n') 
    # ['a', 'b']
    
    print(*['a','b'], sep='\n')
    # a
    # b
    
    print(*'ab', sep='\n')
    # a
    # b
    

    其实 print() 函数的原型是这样定义的

    def print(self, *args, sep=' ', end='\n', file=None):
        pass
    

    print() 函数中的 sep 关键字参数定义的是当打印多个参数时,它们中间的分隔符是什么

    ** 表达式

    def foo(**kwargs):
        print(kwargs)  # 是一个字典
    

    一个 * 参数只能出现在函数定义中最后一个位置参数的后面,而 ** 参数只能出现在最后一个参数都位置。
    有一点要注意的是,在 * 参数后面其实还可以定义其他参数。下面就会用到。

    在函数中实现强制关键字参数

    有的时候你希望在调用函数的时候,必须用关键字参数,因为这样更易懂。
    可以将要限定的强制关键字参数放到某个 *参数 或者单个 * 后面就能达到这种效果。
    像下面这样。

    def query_keyword(max_file, *, servers):
        pass
    
    query_keyword(65535, servers=False)  # ok
    query_keyword(65535)  # TypeError
    

    使用函数注解

    函数注解是为了让看这个函数源码的人更能清楚参数的类型和用法

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

    python 解释器不会对这些注解添加任何的语义。它们不会被类型检查,运行时跟 没有加注解之前的效果也没有任何差距。

    这些注解存储在函数的 __annotations__ 属性中。
    print(add.__annotations__)

    感受 return 的强大

    def foo():
        return '千锋', 8, 1000000
    
    name, *nums = foo()
    
    print(name)  # 千锋
    print(nums)  # [8, 1000]
    

    其实在函数返回之前,先创建了一个元组,之后的赋值就是我们之前讲的元组解包

    在定义函数的默认参数时,不要用可变类型的数据

    假如你的确需要一个默认参数是一个可边类型的数据(比如列表)
    可以把默认参数的值先定义为 None

    def foo(a, b=None): 
        if b is None:
            b = []
    

    验证同一性

    假如你想在一个函数中判断使用者有没有给一个参数传参,你可能想到这样:

    def spam(a, b=None)
        if not b:  # 判断是否是 False
            print("用户没有传递变量")
    

    这样显然会有问题的,因为对于 python 来说, 长度为 0 的字符串 ''、列表 []、元组 ()、字典 {} 都会认为是 False, 并且数字 0 和 布尔值的 False 都认为是假的。
    也就是说用户传入这些参数是属于合法的参数。

    解决办法:

    _no_value = object()
    def spam(a, b=_no_value): 
        if b is _no_value:
            print("用户没有传递变量")
    

    _no_valueobject() 的是个实例, 这样可以判断变量 b 的值和 _no_value 的值是否是同一个对象来判断用户是否传入了值。

    匿名函数

    在匿名函数中使用了一个变量的值,这会是很有意思的一件事。

    li = [lambda n=30: n for n in range(10)]
    

    现在你回答下面几个问题

    1. li[0] 是什么类型的对象?

    li 是一个列表,其中的元素都是匿名函数

    1. li[0]()li[1]() 分别都是什么值?

    其实 li[0]()li[1]() 的值都是 9

    lambda 表达式中的 n 是一个自由变量,是在运行时给其绑定值,而不是在定义时就给其绑定值,这跟函数的默认值参数定义是不同的。
    因此,在调用这个 lambda 表达式的时候,n 的值是执行时的值。
    还有要考虑到, 在 Python 中只有在函数中定义的变量才是局部变量,其他都是全局的变量。 for 循环到最后 n 的值被绑定为 9, 所以 li 所用函数中的 n 的值在运行时都是 9

    现在对 lambda 函数稍作修改就会有不一样的效果

    funcs = [lambda n=n: n for n in range(10)]
    funcs[0]()  # 0
    funcs[1]()  # 1
    

    等号左边的 n 是函数的形参,右边是函数的实参,实参也就是迭代的变量的值。
    这里利用默认参数,就可以在定义函数时,把值绑定给变量。

    未完, 待续...

    相关文章

      网友评论

        本文标题:Python3 函数的更高级的话题(一)

        本文链接:https://www.haomeiwen.com/subject/oooyqqtx.html