文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

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函数参数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯