2010-03-20 78 views
5

假设我有一个函数,它可以将iterable/iterator或非iterable作为参数。使用try: iter(arg)检查可用性。删除迭代的Python方法

根据输入是否是可迭代的,方法的结果将会不同。不是当我想将非迭代作为可迭代输入传递时,很容易做到:我只是用一个元组来包装它。

当我想要传递一个iterable(例如一个字符串),但希望该函数将它当作不可迭代时,我该做什么?例如。使iter(str)失败。

编辑 - 我的初衷:

我想概括zip功能,它可以与非iterables拉链iterables。不可迭代将会像其他迭代器尚未完成一样经常为repeat本身。

我现在唯一的一般解决方案似乎是,我不应该检查general_zip函数(因为字符串问题);但是相反,我必须将repeat迭代器添加到参数之前调用zip。 (这实际上可以节省我发明general_zip的功能 - 尽管我仍然可能会因为使用非迭代作为输入而无需多余的重复,这将是明确的。)

+3

Python的方式是调用者是显式的,并将非迭代转换为迭代。 'zip(my_list,itertools.repeat(42))' 这与添加int和字符串时必须编写42 + int('100')'相同。 添加魔术转换会导致猜测和混淆。 – 2010-03-20 15:31:08

+0

是的,没错。但是我需要多次调用这个函数,然后每次调用之前都必须进行检查。这看起来有点多余。 - 所以,我需要这个功能的原因是相当狭窄和明确的,但是我希望尽可能普遍地具有该功能的潜在能力。 – Debilski 2010-03-20 17:46:30

回答

3

我越想它,似乎无法进行类型检查或将参数传递给函数。

然而,根据功能的意图,一个方法来处理它可能是:

from itertools import repeat 
func(repeat(string_iterable)) 

func仍然看到了一个迭代的,但它不会通过串本身的构成特征迭代。实际上,这个论证的作用就好像它是一个不变的不可迭代的。

+0

'repeat(string_iterable)'将无限地返回字符串。你的意思是'[string_iterable]'(它只会返回一次字符串)? – jfs 2010-03-20 15:58:57

+0

不,对于我的问题[string_iterable]将是错误的解决方案。这个比喻应该是在一个维度上的点 - 当你在两个维度上展开时 - 对应于一整行点而不仅仅是一个点。 – Debilski 2010-03-20 17:36:41

0

专门化它。

def can_iter(arg): 
    if isinstance(arg, str): 
    return False 
    try: 
    ... 
+0

但是这意味着我需要在功能内部做出决定,不能从偶然的场合做出新的决定。 – Debilski 2010-03-20 13:11:55

2

Wh!看起来你希望能够将迭代器件作为迭代器件进行传递,将可迭代器件作为非能量器件进行迭代,将非迭代器件作为迭代器件进行迭代,将非可用事件作为非可用事件进行传递。 既然你要能够处理所有的可能性,可以和电脑(还)没有读心术,你将不得不告诉函数要如何参数进行处理:

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到一个可迭代作为noniterable:

foo_noniterable(iterable)  # or 
foo(iterable, isiterable=False) 

应用FOO到noniterable作为noniterable:

foo_noniterable(noniterable)  # or 
foo(noniterable,isiterable=False) 

应用FOO到noniterable视为可迭代:

foo((noniterable,)) 

PS。我是一个能够很好地完成单项工作的小职能的信徒。他们更容易调试和单元测试。一般来说,我会建议避免单片函数的行为因类型而异。是的,它给开发人员带来了一点额外的负担,要完全调用预期的功能,但我认为调试和单元测试方面的优势不仅仅是弥补它的缺点。

+0

问题是,该函数可能有几个参数;那么会变得有点复杂。 – Debilski 2010-03-20 13:12:56

+0

@Debilski:为什么不给“foo”添加几个参数?也许我对你的情况不够了解。为什么它很复杂? – unutbu 2010-03-20 13:26:09

+0

我不知道。它看起来像这样:'foo([1,2,3],[1,2,3],“abc”,isiterable1 = True,isiterable2 = True,isiterable3 = False)'。或甚至更多的论据。我认为你的解决方案只会有一两个固定的参数就没问题;我的情况是,我想让它更一般。 (否则,我认为我不会问关心,只会或多或少地像你提出的那样做。) – Debilski 2010-03-20 13:32:32

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你在说什么。

[PS] 另一种方法是调用下列方式功能:

smart_func(s='abc', 1, arr=[0,1], [1,2], fmt={'s':'non-iter','some_arr':'iter'}) 

和具备的功能与您提供的参数名称('s''arr'注,有在函数签名中没有这样的名称,因为它与上面的)到'fmt'“类型提示”(即'iter'使参数被认为是可迭代的,并且'non-iter'是不可迭代的)相同。当然,这种方法可以与上述“切换式”结合使用。

+0

没有理由在那里使用嵌套函数。嵌套函数对于制作闭包很有用,但是使用它们在本地定义常量函数是愚蠢的。 – 2010-03-20 15:46:58

+1

@Mike Graham原因是履行函数的合约(在doc btw中指定)。确实有问题的函数可能会被移出(例如,使其可重用),因为它实际上并不依赖于本地参数。但是不管这是愚蠢的...好吧,让大家为他自己选择那个,先生:) – mlvljr 2010-03-20 15:56:03

+0

好吧,现在我看到'smart_func'可能太聪明了[to upvoted :) :) – mlvljr 2010-03-24 17:05:09

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

+0

这个输入给我五个'也可以是一个清单。这发生在我的代码中的几个点,所以我不想在我调用压缩函数之前检查这一点,而是在里面进行检查。我的问题是如何处理边缘情况。 – Debilski 2010-03-20 17:42:23

+0

@Debilski如果你想让你的函数用可迭代/不可迭代的检查逻辑进行“预装”,并提供“覆盖”/交换参数处理方式的能力,为什么不使用简单的默认)格式说明符? – mlvljr 2010-03-20 18:02:38

+0

@Debilski,当然是。显然你已经发现了通过你真正需要的方式 - 一个反复产生相同价值的反复。 – 2010-03-20 18:21:03