接受扩展参数和元组的函数

时间:2017-01-27 16:22:34

标签: python

是否有 Pythonic 方法来创建一个接受单独参数和元组的函数?即实现这样的目标:

def f(*args):
    """prints 2 values 
            f(1,2)
                1 2
            f( (1,2) )
                1 2"""
    if len(args) == 1:
        if len(args[0]) != 2:
            raise Exception("wrong number of arguments")
        else:
            print args[0][0],args[0][1]
    elif len(args) == 2:
        print args[0],args[1]
    else:
            raise Exception("wrong number of arguments")

3 个答案:

答案 0 :(得分:5)

首先,我不知道这样做是否明智。假设一个人调用的函数如下:

f(*((1,4),(2,5)))

如您所见,元组包含两个元素。但是现在由于某种原因,这个人只用一个元素来调用它(因为例如生成器没有生成两个元素):

f(*((1,4),))

然后用户可能希望你的函数报告这个,但现在它只是接受它(这可能导致复杂和意外的行为)。好的打印元素当然不会造成太大的伤害。但在一般情况下,后果可能会更严重。

然而,一个优雅的方法是制作一个简单的装饰器,它首先检查是否有一个元素被喂它检查是否有一个元组元素,如果是,则扩展它:

def one_tuple(f):
    def g(*args):
        if len(args) == 1 and isinstance(args[0],tuple):
            return f(*args[0])
        else:
            return f(*args)
    return g

并将其应用于f

@one_tuple
def f(*args):
    if len(args) == 2:
        print args[0],args[1]
    else:
        raise Exception("wrong number of arguments")

装饰器one_tuple因此检查是否有一个元组被送入,如果是,解包它,然后再将它传递给f函数。

因此 f不必考虑元组案例:它将始终被提供扩展参数并处理这些(当然也可以相反)

定义装饰器的优点是重用:您可以将此装饰器应用于所有类型的功能(从而更容易实现这些功能)。

答案 1 :(得分:3)

我自己并不同意这个想法(尽管我喜欢python不需要定义变量类型因此允许这样的事实)但是可能存在需要这样的事情的情况。所以你走了:

def my_f(a, *b):

    def whatever(my_tuple):
        # check tuple for conformity
        # do stuff with the tuple
        print(my_tuple)
        return

    if hasattr(a, '__iter__'):
        whatever(a)
    elif b:
        whatever((a,) + b)
    else:
        raise TypeError('malformed input')
    return

稍微重组了一下,但逻辑保持不变。如果“a”是可迭代的,则认为它是你的元组,如果不考虑“b”也是如此。如果“a”不是可迭代的并且未定义“b”,则引发TypeError

my_f((1, 2, 3)) # (1, 2, 3)
my_f(1, 2, 3)   # (1, 2, 3)

答案 2 :(得分:3)

Pythonic 方式是使用duck typing。只有在您确定没有任何扩展参数可以迭代时,这才有效。

def f(*args):
    def g(*args):
        # play with guaranteed expanded arguments

    if len(args) == 1:
        try:
            iter(args[0])
        except TypeError:
            pass
        else:
            return g(*args[0])
    return g(*args)

对于传入单个非元组参数的情况,这个实现对@ Ev.Kounis的答案略有改进。它也可以很容易地转换为@WillemVanOnsem描述的等效装饰器。如果您有多个这样的函数,请使用装饰器版本。