python 的可变参数及用法

论坛看到了一个关于可变参数的问题,记录一下我的回答, 以免将来遗忘。
请问在Python函数中的字符串会影响到函数形参吗 | Python | Python 技术论坛 (learnku.com)

首先, def foo(*args)def foo(**kwds) 这个是python的可变参数写法,args 表示传入的多个位置参数, kwds 表示传入的多个关键字参数,正常它是这么用的 :

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
logger.addHandler(handler)
def test_1(*args):
    for key in range(len(args)):
        logger.debug(test_1.__name__ + ': key is ' + str(key) + ", val is " + str(args[key]))

test_1(0, 1, 2, 3, 4)
#  test_1: key is 0, val is 0
#  test_1: key is 1, val is 1
#  test_1: key is 2, val is 2
#  test_1: key is 3, val is 3
#  test_1: key is 4, val is 4

def  test_1_1(arg_1, arg_2, arg_3):
    logger.debug(test_1_1.__name__ + ': arg_1 is ' + str(arg_1) + ' arg_2 is ' + str(arg_2) + ' arg_3 is ' + str(arg_3))

test_1_1(*(1, 2, 3))
# test_1_1: arg_1 is 1 arg_2 is 2 arg_3 is 3

def test_2(**kwds):
    for key in kwds:
        logger.debug(test_1.__name__ + ': key is ' + str(key) + ", val is " + str(kwds[key]))

test_2(a='a', b='b', c='c', d='d')
# test_2: key is a, val is a
# test_2: key is b, val is b
# test_2: key is c, val is c
# test_2: key is d, val is d

def  test_2_1(kwd_1, kwd_2, kwd_3):
    logger.debug(test_2_1.__name__ + ': kwd_1 is ' + str(kwd_1) + ' kwd_2 is ' + str(kwd_2) + ' kwd_3 is ' + str(kwd_3))

test_2_1(**{'kwd_1':1, 'kwd_2':2, 'kwd_3':3})
# test_2_1: kwd_1 is 1 kwd_2 is 2 kwd_3 is 3

然后 / 是什么意思呢? 通常, python 参数既可以按参数位置传, 也可以按参数名字传. 但是如果加了 / 那么 / 前面的参数就只能按位置传,比如下面这样:

def test_3(arg_1, /, arg_2, arg_3):
    logger.debug(test_3.__name__ + ': arg_1 is ' + str(arg_1) + ' arg_2 is ' + str(arg_2) + ' arg_3 is ' + str(arg_3))

test_3(1, 2, arg_3 = 3)
# test_3: arg_1 is 1 arg_2 is 2 arg_3 is 3

test_3(1, arg_2 = 2, arg_3 = 3)
# arg_1 is 1 arg_2 is 2 arg_3 is 3

test_3(1, arg_3 = 3, arg_2 = 2)
# arg_1 is 1 arg_2 is 2 arg_3 is 3

try:
    test_3(1, 2, arg_2=3)
except Exception as error:
    logger.debug(error)
# test_3() got multiple values for argument 'arg_2'

try:
    test_3(arg_1=1, arg_2=2, arg_3=3)
except Exception as error:
    logger.debug(error) 
# test_3() got some positional-only arguments passed as keyword arguments: 'arg_1'

然后楼主提的问题中, 如果第一个位置参数是 nane, 然后可变参数又传一个 name。 那么相当于调用的时候传了两个 name, 它就会冲突。 之所以这样,是因为这个 name 它既可以是位置参数, 也可以是关键字参数。 那么我们在后面加一个 / 说明 name 只能按位置传,它就不会起冲突了:

def test_4(name, **kwds):
    logger.debug(test_4.__name__ + ': name is ' + str(name))

try:
    test_4('a', name='b')
except Exception as error:
    logger.debug(error) 
# test_4() got multiple values for argument 'name'

def test_5(name, / ,**kwds):
    logger.debug(test_5.__name__ + ': name is ' + str(name))

test_5('a', name='b')
# test_5: name is a

然后正常来说, 为了代码的可读性, 写方法的时候是应该明确的定义方法的参数。 可变参数一般是用来写 decorator 的, 就是修改方法的方法, 比如装饰器。 如果不好理解的话可以看一下下面的例子。

def reverse_order(func):
    def wrapper(*args):
        return func(*args[::-1])
    return wrapper

@reverse_order
def test_6(*args):
    for key in range(len(args)):
        logger.debug(test_6.__name__ + ': key is ' + str(key) + ", val is " + str(args[key]))

test_6(0, 1, 2, 3, 4)
# wrapper: key is 0, val is 4
# wrapper: key is 1, val is 3
# wrapper: key is 2, val is 2
# wrapper: key is 3, val is 1
# wrapper: key is 4, val is 0

可以看到, 正常 test_6 是按照位置顺序打印它的所有参数的。 我们定义了一个装饰器修改了 test_6 的行为,使其按照和原来相反的顺序打印参数。

下面是示例的所有代码:

def test_1(*args):
    for key in range(len(args)):
        logger.debug(test_1.__name__ + ': key is ' + str(key) + ", val is " + str(args[key]))

test_1(0, 1, 2, 3, 4)

def test_1_1(arg_1, arg_2, arg_3):
    logger.debug(test_1_1.__name__ + ': arg_1 is ' + str(arg_1) + ' arg_2 is ' + str(arg_2) + ' arg_3 is ' + str(arg_3))

test_1_1(*(1, 2, 3))


def test_2(**kwds):
    for key in kwds:
        logger.debug(test_2.__name__ + ': key is ' + str(key) + ", val is " + str(kwds[key]))

test_2(a='a', b='b', c='c', d='d')

def test_2_1(kwd_1, kwd_2, kwd_3):
    logger.debug(test_2_1.__name__ + ': kwd_1 is ' + str(kwd_1) + ' kwd_2 is ' + str(kwd_2) + ' kwd_3 is ' + str(kwd_3))

test_2_1(**{'kwd_1':1, 'kwd_2':2, 'kwd_3':3})

def test_3(arg_1, /, arg_2, arg_3):
    logger.debug(test_3.__name__ + ': arg_1 is ' + str(arg_1) + ' arg_2 is ' + str(arg_2) + ' arg_3 is ' + str(arg_3))

test_3(1, 2, arg_3 = 3)
test_3(1, arg_2 = 2, arg_3 = 3)
test_3(1, arg_3 = 3, arg_2 = 2)

try:
    test_3(1, 2, arg_2=3)
except Exception as error:
    logger.debug(error)

try:
    test_3(arg_1=1, arg_2=2, arg_3=3)
except Exception as error:
    logger.debug(error) 


def test_4(name, **kwds):
    logger.debug(test_4.__name__ + ': name is ' + str(name))


try:
    test_4('a', name='b')
except Exception as error:
    logger.debug(error) 

def test_5(name, / ,**kwds):
    logger.debug(test_5.__name__ + ': name is ' + str(name))

test_5('a', name='b')

def reverse_order(func):
    def wrapper(*args):
        return func(*args[::-1])
    return wrapper

@reverse_order
def test_6(*args):
    for key in range(len(args)):
        logger.debug(test_6.__name__ + ': key is ' + str(key) + ", val is " + str(args[key]))

test_6(0, 1, 2, 3, 4)
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!