关于Python函数参数的进阶用法

目录
  • 1、关键字参数和位置参数
    • (1)关键字参数
    • (2)位置参数
  • 2、接受任意数量的参数
    • (1)接受任意数量的位置参数
    • (2)接受任意数量的关键字参数
    • (3)同时接受任意数量的位置参数和关键字参数
  • 3、keyword-only参数
  • 3、可选参数(带默认值的参数)

1、关键字参数和位置参数

关键字参数(positional argument)和位置参数(keyword argument)

Python函数的参数根据函数 在调用时 (注意,不是函数定义时)传参的形式分为关键字参数和位置参数。

(1)关键字参数

关键字参数是指在函数调用传参时,由标识符(如name= )引导的参数,或者放在一个由**引导的字典里进行传递。如下所示:

complex(real=3, imag=5)
complex(**{'real': 3, 'imag': 5})

(2)位置参数

不是关键字参数的参数就是位置参数。它除了单独传递之外,还可以放在一个由*引导的可迭代序列(列表、元组等)里进行传递。如下所示:

complex(3, 5)
complex(*(3, 5))

位置参数总是放在函数的参数列表最前方,关键字参数必须要放在位置参数后面。它们之间的位置关系如下所示:

def func(arg1, arg2, kwarg1, kwarg2):
func(1, 2, kwarg1=3, kwarg2=4)

这里arg1arg2是位置参数,kwarg1kwarg2是关键字参数。 关键字参数的key(也就是这里的 'kwarg1=3'中的 'kwarg1''kwarg2=4'中的'kwarg2')要保证和形参名称一致 。

2、接受任意数量的参数

(1)接受任意数量的位置参数

"*"号表达式除了上一章我们讲的用于对任意长度可迭代对象进行拆分之外, 还能在 函数定义 中使用,用于定义一个可以接受任意数量位置参数的函数,如下所示:

def avg(first, *rest):
    print(rest)
    return (first + sum(rest)) / (1 + len(rest))
print(avg(1, 2, 3, 4, 5))
# (2, 3, 4, 5)
# 1. 5

"*"开头的参数必须做为最后一个位置参数使用,且"*"开头的参数传进来后是元组数据结构。

(2)接受任意数量的关键字参数

想接受任意数量的关键字参数,我们可以类似地使用"**"开头的参数。如下所示:

import html
def make_element(name, value, **attrs) -> str:
    key_values = [ ' %s="%s"' % item for item in attrs.items()]
    attr_str = ''.join(key_values)
    # Perform a string formatting operation.
    element = '<{name} {attrs}>{value}</{name}>'.format(name=name, attrs=attr_str, value=html.escape(value))
    return  element

res_1 = make_element('item', 'Albatross', size='large', quantity=6)
res_2 = make_element('p', '<spam>') # escape会把这里'<spam>'中的'<'和'>'替代成安全的序列< >
print(res_1) # <item  size="large" quantity="6">Albatross</item>
print(res_2) # <p ><spam></p>

"**"开头的参数必须做为最后一个关键字参数使用,且"**"开头的参数传进来后是字典数据结构。

(3)同时接受任意数量的位置参数和关键字参数

如果想要函数同时接受任意数量的位置参数和关键字参数,只要联合使用"*""**"即可。

def anyargs(*args:tuple, **kwargs:dict):
    print(args)
    print(kwargs)
anyargs(2, 3, 4, 5, time=1, data=2)
# (2, 3, 4, 5)
# {'time': 1, 'data': 2}

3、keyword-only参数

前面说过, "*"打头的参数只能做为最后一个位置参数, "**"打头的参数只能做为最后一个关键字参数(自然也是最后一个参数),而依此推断"*"打头的参数后的参数就必然是关键字参数了。

# 出现在*args之后的参数称为keyword-only参数
# 这两个例子中y都只能是关键字参数,在传参时要保证key和形参的一致性
def a(x, *args, y):
    print(y)
def b(x, *args, y, **kwargs):
    print(y)

a(4, 6, 7, 8, y=1)
b(4, 6, 7, 3, y=1, data=2, year=3)

# 1
# 1

这样的参数称为keyword-only参数,即出现在*args之后的参数只能做为关键字参数使用。

我们可以充分利用这一性质,将关键字参数放在以*打头的参数后,或者一个单独的*之后,强迫函数的调用者必须传关键字参数,比如下面这样:

def recv(max_size, *, block):
    'Receives a message'
    pass

recv(1024, True)  # recv2() takes 1 positional argument but 2 were given
# and missing 1 required keyword-only argument: 'block'
recv(1024, block=True) # OK

这项技术在实际项目中,可以用来为接受任意数量的位置参数的函数来指定关键字参数,比如下面这个带截断功能的求最小值函数。这里的clip参数被强迫为必须按照关键字参数传入,而且设定了一个默认值None, 使参数为可选的。如下所示:

def mininum(*values, clip=None):
    m = min(values)
    if clip is not None:
        m = clip if clip > m else m
    return m

res1 = mininum(1, 5, 2, -5, 10)
res2 = mininum(1, 5, 2, -4, 10, clip=0)
print(res1, res2) # -5, 0

除此之外,keyword-only参数可以提高代码可读性,像下面这种函数写法:

msg = recv(1024, False)

如果代码的阅读者不熟悉recv函数的工作方式,那么可能不太明白这里的False参数有什么作用,如果这个函数的调用可以写成下面这样的话,那就清晰多了(当然,需要这个函数的编写者最开始就强制函数的使用者这样写):

msg = recv(1024, block=False)

最后,如果 函数定义 的的时候强制使用了keyword-only参数,那么当用户请求帮助信息时,参数信息可以很自然地显现出来:

print(help(recv))
# Help on function recv in module __main__:

# recv(max_size, *_, block)
#     Receives a message

3、可选参数(带默认值的参数)

要想定义一个可选参数,需要在 函数定义 中为参数赋值,并保证默认参数出现在参数列表最后。像下面这样:

def spam(a, b=42):
    print(a, b)
spam(1) # 1, 42
spam(1, 2) # 1, 2

如果默认值是可变容器,比如说列表、集合、字典等,需要把None做为默认值:如下所示:

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

警示1:千万不能直接像下面这样写:

def spam(a, b=[]):

如果像上面那样写,那么就会发生一些你所不期望看到的现象:如果默认值在函数体之外被修改了,那么这种修改在之后的函数调用中仍然阴魂不散,如下面所示:

def spam(a, b=[]):
    print(b)
    return b
x = spam(1)
x.append('oh!')
x.append('no!')
print(x)
spam(1)
# []
# ['oh!', 'no!']
# ['oh!', 'no!']

警示2:在函数体中,我们常常需要判断参数是否为None,此处需要使用is运算符,千万不能直接像下面这样写:

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

这里的问题在于:尽管None会被判定为False,可还有其他许多对象(比如长度为0的字符串、列表、元组、字典等)也存在这样的行为。这样,有很多其他的特定输入也会被判定为False,然后本该是用户传进来的值直接被默认的[]覆盖掉了。如下所示:

def spam(a, b=None):
    if not b:
        b = []
spam(1) # OK
x = []
spam(1, x) # Oops! x will be overwritten by default []
spam(1, 0) # Oops! 0 will be overwritten by default []
spam(1, '') # Oops! '' will be overwritten by default []

最后,我们再来讨论一个非常棘手的问题。我们想要在函数中检测调用者是否对可选参数提供了某个特定值(可以是任意值,None也算)这样,我们自然就不能用None0False当做默认值然后再来做检测了,因为用户本身就可能拿它们当做参数。

要解决这个问题,可以像下面这样写:

_no_value = object()
def spam(a, b=_no_value):
    if b == _no_value:
        print("No b value supplied")
        return
    print(a, b)
spam(1) # No b value supplied
spam(1, 2) # 1 2
spam(1, None) # 1 None

这里_no_value是基于object()类创建的一个独有对象,可以用这个来对用户提供的参数做检测,因为用户几乎不可能把_no_value对象做为参数输入(除非用户传入的是相同的对象,否则哪怕是object类型的另一个对象都和_no_value对象是不同的)。

这里简要说明一下object类,objectPython中几乎所有对象的基类,object对象没有任何数据(底层缺少__dict__字典,甚至没办法设置任何属性),它唯一的作用就是用来检测相等性。

到此这篇关于关于Python函数参数的进阶用法的文章就介绍到这了,更多相关Python函数参数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python函数参数和注解的使用

    四种参数 Python函数func定义如下: def func(first, *args, second="Hello World", **kwargs): print(first) print(args) print(second) print(kwargs) func("dongfanger", "san", py="good") 运行后会输出: dongfanger ('san',) Hello World {'py':

  • 详解Python 函数参数的拆解

    本文为阅读 <Python Tricks: The Book>一书的 3.5 Function Argument Unpacking 的笔记与扩充理解.函数参数拆解是定义可变参数(VarArgs) *args 和 **kwargs 的反向特性. *args 和 **kwars 是函数可定义一个形参来接收传入的不定数量的实参. 而这里的函数参数拆解是形参定义多个,在调用时只传入一个集合类型对象(带上 * 或 ** 前缀),如 list, tuple, dict, 甚至是 generator, 然

  • Python函数参数定义及传递方式解析

    python中函数定义参数有四种形式: def fun1(a,b,c): pass def fun2(a=1,b=2,c=3): pass def fun3(*args): pass def fun4(**kargs): pass 四种中最常见是前两种,基本上一般点的教程都会涉及,后两种一般很少单独出现,常用在混合模式中 第一种 fun1(a,b,c)是直接将实参赋予形参,根据位置做匹配,即严格要求实参的数量与行参的数量位置相等,比较一般,大多数语言常用这种方式. 第二种 fun2(a=1,b=

  • python教程对函数中的参数进行排序

    内建的 sorted() 函数可接受一个用来传递可调用对象( callable ) 的参数 key,而该可调用对象会返回待排序对象中的某些值,sorted则利用这些值来比较对象. 例如,如果应用中有一系列的 User 对象实例,而我们想通过 user_id 属性来对他们排序,则可以提供一个可调用对象将 User 实例作为输入然后返回 user_id. class User: def __init__(self, user_id): self.user_id = user_id def __rep

  • Python函数中的不定长参数相关知识总结

    一. 不定长位置参数 # 在定义函数参数时,可以在形参的前面加*,该形参将获取所有的位置实参 # 它会将所有的实参保存在一个元组中 def fn(*args): print("args=", args) print("args type:", type(args)) # 带*形参和其他参数配合使用 def fn1(a, b, *args): print(a) print(b) print(args) # 下面这两种写法可以,但是在传实参的时候要注意 def fn2(

  • Python 类,对象,数据分类,函数参数传递详解

    目录 1.基本概念 1.1 类与对象的关系 1.3 对象的创建与引用 2.数据的分类 2.1 不可变类型 2.2 可变类型 3.函数传递参数的方式 3.1 值传递 3.2 引用传递 总结 最近在基于python写的接口自动化脚本,从Excel表中读取所有数据,每一行数据保存为字典,再将很多行的字典数据保存到一个列表里,运行时发现,列表中的字典均相同,且一直是excel最后一行的数据,情况类比如下: dd = {"a":1,"b":10} i = 2 list1 =

  • 关于Python函数参数的进阶用法

    目录 1.关键字参数和位置参数 (1)关键字参数 (2)位置参数 2.接受任意数量的参数 (1)接受任意数量的位置参数 (2)接受任意数量的关键字参数 (3)同时接受任意数量的位置参数和关键字参数 3.keyword-only参数 3.可选参数(带默认值的参数) 1.关键字参数和位置参数 关键字参数(positional argument)和位置参数(keyword argument) Python函数的参数根据函数 在调用时 (注意,不是函数定义时)传参的形式分为关键字参数和位置参数. (1)

  • python函数参数*args**kwargs用法实例

    复制代码 代码如下: #coding=utf8__author__ = 'Administrator' # 当函数的参数不确定时,可以使用*args和**kwargs.*args没有key值,**kwargs有key值 def fun_var_args(farg, *args):    print 'args:', farg    for value in args:        print 'another arg:',value # *args可以当作可容纳多个变量组成的list或tupl

  • Python函数参数操作详解

    本文实例讲述了Python函数参数操作.分享给大家供大家参考,具体如下: 简述 在 Python 中,函数的定义非常简单,满足对应的语法格式要求即可.对于调用者来说,只需关注如何传递正确的参数,以及获取相应的返回值就足够了,无需了解函数的内部实现(除非想学习.跟踪源码). 话虽如此,但对于函数的定义来说,灵活性非常高.除了常规定义的必选参数以外,还支持默认参数.可变参数.以及关键字参数.这样以来,不但能处理复杂的参数,还可以简化调用者的代码. 形参和实参 不止 Python,几乎所有的编程语言都

  • 老生常谈python函数参数的区别(必看篇)

    在运用python的过程中,发现当函数参数为list的时候,在函数内部调用list.append()会改变形参,与C/C++的不太一样,查阅相关资料,在这里记录一下. python中id可以获取对象的内存地址 >>> num1 = 10 >>> num2 = num1 >>> num3 = 10 >>> id(num1) >>> id(num2) >>> id(num3) 可以看到num1.num2

  • PHP substr()函数参数解释及用法讲解

    substr(string,start,length) 参数: 1,string 即你要截取的字符串 2,start 即要截取的开始位置(0表示从从前往后数 第一个字符开始,负数表示从从后往前数) eg:start=1,表示从从前往后开始的第二个数开始截取,start=-1,表示从从后往前开始的第一(是第一不是第二哦)个数开始截取, 3,length 当为正数时,为需要截取的长度:当为负数时,即理解为去掉末尾的几个字符 eg:length=3,表示截取三个长度:length=-2,即为去掉末尾的

  • Python函数参数匹配模型通用规则keyword-only参数详解

    Python3对函数参数的排序规则更加通用化了,即Python3 keyword-only参数,该参数即为必须只按照关键字传递而不会有一个位置参数来填充的参数.该规则在处理人一多个参数是很有用的. keyword-only kword_only(1, 2, 3, c=4) print('-' * 20) kword_only(a=1, c=3) 示例结果: 1 (2, 3) 4 -------------------- 1 () 3 在 *args 之后的参数都需要在调用中使用关键字的方式传递,

  • python函数参数(必须参数、可变参数、关键字参数)

    #python中的函数定义,使用和传参 ###------------------- 必要参数 ------------------------------------- 必要参数,即函数调用时必须要传的参数, #下面进行举例说明 def helloWorld(a): print('输出:hello') if __name__ == '__main__': helloWorld("aaa") ## 必须要有参数 ##------------ 默认参数 -----------------

  • Python函数参数类型及排序原理总结

    这篇文章主要介绍了Python函数参数类型及排序原理总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Python中函数的参数问题有点复杂,主要是因为参数类型问题导致的情况比较多,下面来分析一下. 参数类型:缺省参数,关键字参数,不定长位置参数,不定长关键字参数. 其实总共可以分为 位置参数和关键字参数,因为位置参数被放在list里面,关键字参数放在dict里面,Python在解读的时候首先处理list,没有遇到关键字就append到list

  • Python函数参数中的*与**运算符

    问题描述 在阅读某些代码时,经常会看到函数定义/调用时的参数前带有 * 或者 ** 运算符,比较糊涂,今天来探究记录一番. 函数定义时的 * 和 ** 查阅相关资料得知,在参数前面加上* 号 ,意味着参数个数不止一个,而带一个星号(*)参数的函数传入的参数存储为一个元组(tuple),带两个(*)号则是表示字典(dict)! 我们定义3个函数来分别测试一下*和**的功能. 第一个函数func1参数列表中有两个参数,其中参数b前有* 第二个函数func2参数列表中有两个参数,其中参数b前有** 第

随机推荐