zoukankan      html  css  js  c++  java
  • Python基础-函数参数

    Python基础-函数参数

    写在前面

    如非特别说明,下文均基于Python3

    摘要
    本文详细介绍了函数的各种形参类型,包括位置参数,默认参数值,关键字参数,任意参数列表,强制关键字参数;也介绍了调用函数时传递实参的各种方式,包括位置实参,关键字实参以及使用*和**来解包序列和字典。

    1. 概述

    函数在一定程度上是为了重用而创建的。如果有一段非常优秀的代码段,实现了网络资源下载的功能,如果没有函数,将会在每次需要实现网络资源下载的地方复制该段代码。懒惰即美德,将这段代码抽象为函数,在需要使用的地方调用即可。
    函数的使用有以下好处:

    • 增加代码的可读性。如在需要下载网络资源的地方调用函数:download(),可以通过名字读懂程序的目的;
    • 增加代码可重用性。相比复制大段代码,调用函数的可操作性无疑更强;
    • 增加可维护性。如果需要更改下载网络资源的实现,没有使用函数的情况下,不得不在每个实用下载功能的地方修改,使用了函数,只需要修改函数即可;
    • 减少犯错误的可能性。在复制代码的过程中,无疑会因为各种原因出现一些差错,而函数不会。

    函数定义非常简单:

    def func([formal_parameter1, ... formal_parameter1]):
    	statement
    

    以上函数定义的作用是创建函数对象,并且在当前作用域创建名字func,指向函数对象,在可及该作用域范围内,可以使用名字func调用函数。定义函数时候参数列表中的名字是函数形参,调用函数用的参数是实参。

    Python函数的参数十分强大,但相应也为这种强大付出了相对复杂的代价。

    函数定义时,函数的形参可以有以下几种类型:

    • 位置参数 positional parameters,最常用的形参形式,位置比名字重要;
    • 默认参数值 default argument values,param_name = argu_value形式,为形参提供默认值,必须放置在位置参数之后;
    • 任意参数列表 arbitrary argument lists,*args形式,args以元组的形式接收未匹配的位置实参;
    • 关键字形参字典 keyword arguments, **kwargs形式,kwargs以字典的形式接收未匹配的关键字实参,关键字参数需在任意参数列表之后;
    • 强制关键字参数 keyword-only arguments,在任意参数列表之后(或者在单独的*之后),调用是只能使用关键字实参。

    函数调用时,实参可以由以下方式传递:

    • 位置实参按照位置从左到右匹配,位置比名字重要;
    • 关键字实参,通过明确形参的名字为其指定实参值。调用时关键字实参必须在位置实参之后,且形参列表中要有与之匹配的关键字形参;
    • 解包列表/字典,使用*(sequence)从序列中解包位置实参,使用**(dict)的方式从字典中解包关键字实参。

    当这些不同的形参组合在一起时,构成的函数参数列表将会相当复杂,始终牢记实参形参匹配是位置参数优先。而且,任意参数列表与关键字参数组合的形参列表,可以匹配任意方式的函数调用。

    2. 位置参数

    位置形参是最常见的形参类型,其中,位置比名字重要,因为在实参匹配是是按照位置来的:

    # positional argument, name is not important, but order matters
    def positional_argument(name, age):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('age->type:%s, value:%s' % (type(age), age))
    

    调用时,如果改变实参位置,意义完全不同:

    positional_argument('Richard', 20)
    positional_argument(20, 'Richard')
    

    位置形参和位置实参(统称位置参数)是最重要的参数类型,在参数匹配中它的优先级是最高的。

    3. 参数默认值

    有其他高级语言(如java)经验的人知道,有重载函数这一说法,两个函数的名字相同,其参数列表不同,功能不同。调用者通过指定不同的实参,调用不同形参的重载函数。

    但是在Python中没有重载函数的说法,因为默认参数值得存在,是的调用者在调用同一个函数的时候可以指定不同参数。虽然不支持重载,但是Python以默认参数值的方式实现了重载函数的功能。

    指定了默认参数值的形参不能位于位置参数之前,因为实参匹配是位置优先的,这时在前面的指定了默认值的参数会被位置实参覆盖,导致后面的位置形参无法匹配到实参值而调用失败:

    # default argument values, non-default argument cann't follow default argument
    def default_argument_value(name, age = 20, id = '0001'):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('age->type:%s, value:%s' % (type(age), age))
    	print('id->type:%s, value:%s' % (type(id), id))
    	
    # 调用时,可以有多种实参形式
    # 指定唯一的强制参数
    default_argument_value('Richard')
    # 指定其中一个默认参数
    default_argument_value('Richard', 22)
    # 指定全部参数
    default_argument_value('Richard', 22, '002')
    

    4. 任意参数列表

    Python的函数相较于其他高级语言强大的地方在于,可以收集多余的未匹配到形参的实参。使用如下格式的形参:*args,收集到尚未匹配到形参的实际参数。

    接收的额外位置实参以元组的形式存储,且任意参数列表需要在位置参数之后:

    # Arbitrary Argument Lists
    # It receives a tuple containing the positional arguments beyond the formal parameter list. (*name must occur before **name.) 
    # be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function
    
    def arbitrary_arguments_list(name, age, *args):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('age->type:%s, value:%s' % (type(age), age))
    	print('args->type:%s, value:%s' % (type(args), args))
    
    # 实参1, 2, 3没有位置形参匹配,被任意参数列表收集
    arbitrary_arguments_list('Richard', 20, 1, 2, 3)
    

    output:

    name->type:<class 'str'>, value:Richard
    age->type:<class 'int'>, value:20
    args->type:<class 'tuple'>, value:(1, 2, 3)
    

    5. 关键字参数

    在调用函数时,通过位置参数方式调用,每个参数到底匹配哪个形参是不容易发现的,之后查看函数定义才能知道。可以通过指定形参对应的实参值的方式调用,这样实参形参的匹配更加明了。

    还是以位置形参为例:

    # positional argument, name is not important, but order matters
    def positional_argument(name, age):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('age->type:%s, value:%s' % (type(age), age))
    

    在调用时可以通过关键字方式:

    # keyword arguments. 
    # In a function call, keyword arguments must follow positional arguments. 
    # All the keyword arguments passed must match one of the arguments accepted by the function, and their order is not important.
    positional_argument(age = 20, name = 'Richard')
    

    关键字实参必须在位置实参之后,并且可以在形参列表中匹配到形参名字,否则调用失败:

    # 形参中没有名为id的参数,所以调用失败
    positional_argument(age = 20, name = 'Richard', id = '003')
    

    收集多余关键字实参
    任意参数列表能够接收没有匹配到位置形参的实参,而关键字形参字典能够接受为匹配到关键字参数的实参。通过如**kwargs的方式,收集尚未匹配的关键字实参,关键字参数字典也要在位置参数之后:

    # keyword arguments dict **kwargs.
    # It receives a dictionary containing all keyword arguments except for those corresponding to a formal parameter.
    def keyword_argument_dict(name, age, **kwargs):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('age->type:%s, value:%s' % (type(age), age))
    	print('kwargs->type:%s, value:%s' % (type(kwargs), kwargs))
    keyword_argument_dict(name = 'Richard', age = 20, id = '0001', type = 'it')
    

    output:

    name->type:<class 'str'>, value:Richard
    age->type:<class 'int'>, value:20
    kwargs->type:<class 'dict'>, value:{'id': '0001', 'type': 'it'}
    

    另外,关键字形参字典需要在任意参数列表之后。

    6. 强制关键字参数

    任意出现在*arg或者*之后的形参都是命名关键字参数,意味着它们只能作为关键字实参匹配,而非位置实参。

    # Keyword only argument.
    # Any formal parameters which occur after the *args parameter are ‘keyword-only’ arguments,
    # meaning that they can only be used as keywords rather than positional arguments.
    def keyword_only_argument(name, *, age, id):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('age->type:%s, value:%s' % (type(age), age))
    	print('id->type:%s, value:%s' % (type(id), id))
    
    keyword_only_argument('Richard', age = 20, id = '001')
    

    output:

    name->type:<class 'str'>, value:Richard
    age->type:<class 'int'>, value:20
    id->type:<class 'str'>, value:001
    

    7. 序列和字典实参的解包

    在函数调用时,使用*sequence将序列解包为位置实参;
    使用**dict将字典解包为关键字实参。

    def mix_param(name, *args, **kwargs):
    	print('name->type:%s, value:%s' % (type(name), name))
    	print('args->type:%s, value:%s' % (type(args), args))
    	print('kwargs->type:%s, value:%s' % (type(kwargs), kwargs))
    
    mix_param('Richard', *(1, 2, 3), **{'age':20, 'id':'001'})
    

    output:

    name->type:<class 'str'>, value:Richard
    args->type:<class 'tuple'>, value:(1, 2, 3)
    kwargs->type:<class 'dict'>, value:{'age': 20, 'id': '001'}
    

    注意到*args解包为位置参数,而**kwargs解包为关键字参数,涵盖了Python中所有可能出现的实参类型。因此,可以使用这两个组合调用任意形参实行的函数:

    def foo(x, y, z, m = 0, n = 0):
    	print(x, y, z, m, n)
    
    def call_foo(*args, **kwargs):
    	print('Call foo!')
    	foo(*args, **kwargs)
    

    注意到call_foo函数中,args是一个元组,kwargs是一个字典,所以可以解包他们组合调用任意形参形式的函数。这一种方式在调用父类构造函数时非常有用!

  • 相关阅读:
    mojo 接口示例
    MojoliciousLite: 实时的web框架 概述
    接口返回json
    centos 6.7 perl 版本 This is perl 5, version 22 安装DBI DBD
    centos 6.7 perl 5.22 安装DBD 需要使用老的perl版本
    商业智能改变汽车行业
    商业智能改变汽车行业
    读MBA经历回顾(上)目的决定手段——北漂18年(48)
    perl 升级到5.20版本
    Group Commit of Binary Log
  • 原文地址:https://www.cnblogs.com/crazyrunning/p/7100359.html
Copyright © 2011-2022 走看看