zoukankan      html  css  js  c++  java
  • Learning Python 010 函数 2

    Python 函数 2

    函数的参数

    位置参数(普通,正常的参数)

    随便编写一个求x^n的值的函数power(x, n)

    def power(x, n):
        s = 1
        while n > 0:
            n = n - 1
            s = s * x
        return s

    我们调用这个函数,需要传入两个参数,这个两个参数也叫做位置参数

    >>> power(5, 2)
    25

    如果我们给power()函数只传入1个参数,会这样:

    >>> power(5)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: power() missing 1 required positional argument: 'n'

    显然会报错,这个时候,提示的错误大概意思是说:你需要在传入一个n参数。

    如果我们就像给power(x, n)函数传入1个参数,要怎么做?这个时候,就是要是默认参数了。

    默认参数

    默认参数在其他高级语言里也用,C/C++也有,你可以一时想不起来,但是,只要上代码,你就知道什么是默认参数了:

    def power(x, n=2):
        s = 1
        while n > 0:
            n = n - 1
            s = s * x
        return s

    现在,这个形参n就是默认参数

    Q:使用默认参数有什么好处?
    A:最大的好处是能降低调用函数的难度。
    怎么就降低了函数的调用难度呢:一共两个方面:
    1. 参数的数量。对于power(x, n)函数,参数数量可以是1个,也可以是2个。
    2. 参数的顺序。如果有更多的参数,我们可以这样使用函数power(2, n=2)默认参数对输入顺序没有要求。


    对于默认参数一定要注意一点:必须使用不变对象,想:list这种可变的参数:不要使用。举例:

    def add_end(L=[]):
        L.append('END')
        return L

    这个函数,你不给它传入参数时,如果你多次调用,会得到下面的结果:

    >>> add_end()
    ['END']
    >>> add_end()
    ['END', 'END']
    >>> add_end()
    ['END', 'END', 'END']

    如果你给它参数参数,它却可以正常的使用,不会出现上面的错误结果:

    >>> add_end([1, 2, 3])
    [1, 2, 3, 'END']
    >>> add_end(['x', 'y', 'z'])
    ['x', 'y', 'z', 'END']

    Q:这是为什么?
    A:解释:每次调用该函数,如果改变了L的内容,则下次调用时,默认参数内容就变了,不再是函数定义时的[]了。
    所以,结论就是,add_end()函数你写的不对,正确的写法是这样的:

    def add_end(L=None):
        if L is None:
            L = []
        L.append('END')
        return L

    现在就对了,你可以试试,不管你调用几次,都没有问题。

    可变参数

    Q:什么是可变参数?
    A:就是指针!

    举个例子,看下面的程序:

    def calc(*numbers):
        sum = 0
        for n in numbers:
            sum = sum + n * n
        return sum

    这个calc(*numbers)函数,我可以随便个它传入参数:

    >>> calc(*[1, 2, 3])
    14
    >>> calc(*(1, 3, 5, 7))
    84
    >>> nums = [1, 2, 3]
    >>> calc(nums[0], nums[1], nums[2])
    14
    >>> calc(1, 2, 3)
    14
    >>> calc(1, 3, 5, 7)
    84
    >>> calc()
    0

    这个可变参数几乎无敌,它允许你传入0个或任意个参数。(解释:这些可变参数在函数调用时自动组装为一个tuple。)所以说,玩转指针,你就牛逼了。

    关键字参数

    Q:什么是关键字参数
    A:**para这样的参数就是所谓的关键字参数
    举例:

    def person(name, age, **kw):
        print('name:', name, 'age:', age, 'other:', kw)

    这个**kw参数可以不赋值,也可以随便赋值:

    >>> person('Michael', 30)
    name: Michael age: 30 other: {}
    >>> person('Bob', 35, city='Beijing')
    name: Bob age: 35 other: {'city': 'Beijing'}
    >>> person('Adam', 45, gender='M', job='Engineer')
    name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

    看到了吧,这就是关键字参数
    Q:那它究竟有什么作用?
    A:也是,降低了函数的调用难度,并且可以扩展函数的功能。比如,这个person()函数,我们只需要保证能够接受到nameage这个两个参数就可以,如果调用者愿意提供更多的参数,我们也能收到。使用起来相当的灵活。

    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, city=extra['city'], job=extra['job'])
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, **extra)
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

    注意:kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。

    命名关键字参数

    命名关键字参数就是:调用者依然可以不受限制的向person()函数传入形参,但是我们只接受指定的几个参数。

    def person(name, age, *, city, job):
        print(name, age, city, job)

    其中*后面的参数就命名关键字参数。(命名关键字参数需要一个特殊分隔符*)。

    第2种写法:

    def person(name, age, *args, city, job):
        print(name, age, args, city, job)

    如果函数定义中已经有了一个可变参数,那么后面跟的形参就都是命名关键字参数,不需要特意在指定特殊分隔符*

    严重注意:
    命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,即使参数的顺序正确,调用依然报错:

    >>> person('Jack', 24, 'Beijing', 'Engineer')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: person() takes 2 positional arguments but 4 were given
    >>> person('Jack', 24, city = 'Beijing', job = 'Engineer')
    Jack 24 Beijing Engineer

    解释:
    如果命名关键字参数没有写参数名,Python解释器把这4个参数均视为位置参数
    所以,既然命令关键字参数是可选输入的,所以命令关键字参数需要些默认值。程序还要改:

    def person(name, age, *, city='Beijing', job='Engineer'):
        print(name, age, city, job)

    这样,程序就完美了。


    总结:
    1. 必须使用*作为特殊分隔符,它是为Python解释器识别位置参数命名关键字参数用的。
    2. 如果有可变参数,就不需要特意在加*了,可变参数后面跟着的默认为命名关键字参数
    3. 命名关键字参数必须赋默认值。

    参数组合

    Python中,函数的形参是上面这么多的形式。如果组合使用,顺序必须注意:必选参数默认参数可变参数命名关键字参数关键字参数, 这样的顺序。

    def f1(a, b, c=0, *args, **kw):
        print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
    
    def f2(a, b, c=0, *, d, **kw):
        print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

    调用:

    >>> f1(1, 2)
    a = 1 b = 2 c = 0 args = () kw = {}
    >>> f1(1, 2, c=3)
    a = 1 b = 2 c = 3 args = () kw = {}
    >>> f1(1, 2, 3, 'a', 'b')
    a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
    >>> f1(1, 2, 3, 'a', 'b', x=99)
    a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
    >>> f2(1, 2, d=99, ext=None)
    a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

    更可气的是:通过一个tuple和dict,你也可以调用上述函数:

    >>> args = (1, 2, 3, 4)
    >>> kw = {'d': 99, 'x': '#'}
    >>> f1(*args, **kw)
    a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
    >>> args = (1, 2, 3)
    >>> kw = {'d': 88, 'x': '#'}
    >>> f2(*args, **kw)
    a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

    小技巧:
    对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。


    • 可见Python语言的灵活。如果使用C++取实现一个这种函数的功能,需要写多少重载方法啊!
    • Python非常适合实验室做程序。就是,突然有一个灵感涌现,我们快速的使用Python编写一段脚本程序,来测试一下这个灵感的可行性。

    递归函数

    这的重点是讲Python,而不是递归。要说起递归,那是有很多好讲的,有许多的技巧。递归的思想很简单,但是写起程序来很绕。
    随便写一个:

    def fact(n):
        if n==1:
            return 1
        return n * fact(n - 1)

    Python使用自己写的递归函数时,注意的事情就是:传入的值不要太大,容易产生栈溢出错误。

    >>> fact(1000)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in fact
      ...
      File "<stdin>", line 4, in fact
    RuntimeError: maximum recursion depth exceeded in comparison

    参考网站:
    http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014316784721058975e02b46cc45cb836bb0827607738d000

  • 相关阅读:
    操作系统概述总结
    string类的用法总结
    stack的简单用法总结
    递归用法总结
    C语言中常见的图形打印总结
    C++中list的用法总结
    STL中find和sort的用法总结
    unity Physics/Physics2DProjectSettings中LayerCollisionMatrix的存储方式
    UnityEvent<T> 系列化
    十字相乘法
  • 原文地址:https://www.cnblogs.com/aobosir/p/5928637.html
Copyright © 2011-2022 走看看