假设我有一个函数可以将可迭代/迭代器或非可迭代作为参数。使用try: iter(arg)
检查Iterability。
根据输入是否是可迭代的,方法的结果将是不同的。不是当我想传递一个不可迭代的可迭代输入时,很容易做到:我只用一个元组包装它。
当我想传递一个iterable(例如一个字符串)但是希望函数将它视为不可迭代时,我该怎么办?例如。使iter(str)
失败。
编辑 - 我的初衷:
我想概括zip
函数,因为它可以使用非迭代的zip可迭代。然后,非迭代将repeat
本身,就像其他迭代没有完成一样。
我现在唯一的解决方案似乎是,我不应该检查general_zip
函数内部(因为字符串问题);但是我必须在调用repeat
之前将zip
迭代器添加到参数中。 (这实际上使我免于发明general_zip
函数 - 尽管我仍然可能因为使用非可迭代作为输入而没有额外的重复将是明确的。)
答案 0 :(得分:3)
我想的越多,似乎不可能不进行类型检查或将参数传递给函数。
但是,根据功能的用途,处理它的一种方法可能是:
from itertools import repeat
func(repeat(string_iterable))
func
仍然看到一个可迭代但它不会遍历字符串本身的字符。实际上,这个论证就好像它是一个不可迭代的常量。
答案 1 :(得分:2)
喔!看起来你希望能够将iterables作为iterables传递,iterables作为noniterables,noniterables作为iterables,noniterables作为noniterables。 既然你希望能够处理所有可能性,并且计算机无法(尚未)读取思想,那么你将不得不告诉函数你希望如何处理这个参数:
def foo_iterable(iterable):
...
def foo_noniterable(noniterable):
...
def foo(thing,isiterable=True):
if isiterable:
foo_iterable(thing)
else:
foo_noniterable(thing)
将foo应用于可迭代的
foo(iterable)
将foo应用于不可迭代的迭代:
foo_noniterable(iterable) # or
foo(iterable, isiterable=False)
将foo应用于noniterable作为noniterable:
foo_noniterable(noniterable) # or
foo(noniterable,isiterable=False)
将foo应用于noniterable作为可迭代:
foo((noniterable,))
PS。我相信小功能可以很好地完成一项工作。它们更容易调试和单元测试。一般来说,我会建议避免根据类型行为不同的单片函数。是的,它给开发人员带来了额外的负担,可以完全调用预期的功能,但我认为调试和单元测试方面的优势不仅仅是弥补它。
答案 2 :(得分:0)
专业化。
def can_iter(arg):
if isinstance(arg, str):
return False
try:
...
答案 3 :(得分:0)
好吧,告诉函数你想如何处理它的参数的一种方法是使用合理的默认值(使函数默认按原始类型处理所有内容),同时能够舒适地指定你喜欢的任何调整(即使用短而缺少的默认fmt
字符串),例如:
def smart_func(*args, **kw):
"""If 'kw' contains an 'fmt' parameter,
it must be a list containing positions of arguments,
that should be treated as if they were of opposite 'kind'
(i.e. iterables will be treated as non-iterables and vise-versa)
The 'kind' of a positional argument (i.e. whether it as an iterable)
is inferred by trying to call 'iter()' on the argument.
"""
fmt = kw.get('fmt', [])
def is_iter(it):
try:
iter(it)
return True
except TypeError:
return False
for i,arg in enumerate(args):
arg_is_iterable = is_iter(arg)
treat_arg_as_iterable = ((not arg_is_iterable)
if (i in fmt) else arg_is_iterable)
print arg, arg_is_iterable, treat_arg_as_iterable
这给出了:
>>> smart_func()
>>> smart_func(1, 2, [])
1 False False
2 False False
[] True True
>>> smart_func(1, 2, [], fmt=[])
1 False False
2 False False
[] True True
>>> smart_func(1, 2, [], fmt=[0])
1 False True
2 False False
[] True True
>>> smart_func(1, 2, [], fmt=[0,2])
1 False True
2 False False
[] True False
扩展此函数(找到最长迭代的长度等),可以构造一个你正在谈论的smart-zip
。
[P.S。] 的 另一种方法是以下列方式调用该函数:
smart_func(s='abc', 1, arr=[0,1], [1,2], fmt={'s':'non-iter','some_arr':'iter'})
并使函数与您提供的参数名称匹配('s'
和'arr'
,注意,函数签名中没有这样的名称,因为它与上面相同)到'fmt'
“类型提示”(即'iter'
使一个参数被视为可迭代,而'non-iter'
被视为不可迭代的)。当然,这种方法可以与上述“切换式”方法结合使用。
答案 4 :(得分:0)
不检查可迭代性。让函数检查有关其元素类型/功能的内容是错误的,以便让单个函数执行不同的任务。如果你想做两件不同的事情,可以做两个不同的功能。
听起来你自己已经得出了这个结论,并提供了一致的API,你可以在哪里做到
from itertools import repeat
zip([1, 2, 3], repeat(5), "bar")
请注意,执行此操作几乎总是无用的,因为您可以这样做
five = 5
for number, letter in zip([1, 2, 3], "bar")
# Just use five here since it never changes
除非您将此内容提供给已使用zip
的内容。