是否有 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")
答案 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描述的等效装饰器。如果您有多个这样的函数,请使用装饰器版本。