我有一个功能的输入参数可以是一个元素或一系列的要素。如果这一论点是一个单一的元素,然后我把它放在一个列表中的所以我可以迭代过输入以一致的方式。

目前,我有这个:

def my_func(input):
    if not isinstance(input, list): input = [input]
    for e in input:
        ...

我的工作与现有的API所以我不能改变输入参数。使用isinstance()感到哈克,这样是否有一个 正确的 的方式来做到这个吗?

有帮助吗?

解决方案

我喜欢Andrei Vajna对 hasattr(var,'__ iter __')的建议。请注意一些典型Python类型的结果:

>>> hasattr("abc","__iter__")
False
>>> hasattr((0,),"__iter__")
True
>>> hasattr({},"__iter__")
True
>>> hasattr(set(),"__iter__")
True

这具有将字符串视为不可迭代的附加优点 - 字符串是灰色区域,因为有时您希望将它们视为元素,其他时候将其视为字符序列。

请注意,在Python 3中, str 类型 具有 __ iter __ 属性,但这不起作用:

>>> hasattr("abc", "__iter__")
True

其他提示

通常,字符串(plain和unicode)是您想要将其视为“单个元素”的唯一迭代。 - basestring builtin特别存在,允许你用 isinstance 测试任何一种字符串,所以对于那种特殊情况它是非常难以理解的; - )。

因此,我对最常见案例的建议方法是:

  if isinstance(input, basestring): input = [input]
  else:
    try: iter(input)
    except TypeError: input = [input]
    else: input = list(input)

这是将每个可迭代的EXCEPT字符串直接作为列表处理,字符串和数字以及其他非迭代作为标量(将其标准化为单项列表)的方法。

我明确地列出了各种可迭代的列表,因此你知道你可以进一步执行各种列表技巧 - 排序,迭代不止一次,添加或删除项目以便于迭代等等,所有这些都无需改变ACTUAL输入列表(如果列表确实是;-)。如果您只需要一个简单的 for 循环,那么最后一步是不必要的(如果输入是一个巨大的打开文件,实际上没有用),我建议使用辅助生成器:

def justLoopOn(input):
  if isinstance(input, basestring):
    yield input
  else:
    try:
      for item in input:
        yield item
    except TypeError:
      yield input

现在,在你需要这种参数规范化的每一个函数中,你只需使用:

 for item in justLoopOn(input):

即使在另一种情况下你也可以使用辅助规范化功能(你需要一个真实的清单用于进一步的邪恶目的);实际上,在这种(更罕见的)情况下,您可以这样做:

 thelistforme = list(justLoopOn(input))

这样(不可避免地)有点毛茸茸的归一化逻辑只是在一个地方,就像应该的那样! - )

第一,没有一般的方法,可以告诉一个"单元",由"的要素清单",因为通过清单可以是一个元件的另一个列表。

我想说你需要定义的数据类型可能会有的,所以,你可能有:

  • 任何的后裔 list 对别的东西
    • 测试 isinstance(input, list) (让你的例子是正确的)
  • 任何序列类型除外串(basestring 在蟒蛇2.x, str 在Python3.x)
    • 使用顺序元类: isinstance(myvar, collections.Sequence) and not isinstance(myvar, str)
  • 一些序列类型的针对已知的情况下,喜欢 int, str, MyClass
    • 测试 isinstance(input, (int, str, MyClass))
  • 任何可迭代,除了:
    • 测试

.

    try: 
        input = iter(input) if not isinstance(input, str) else [input]
    except TypeError:
        input = [input]

你可以在你的论证之前放*,这样你就会得到一个元组:

def a(*p):
  print type(p)
  print p

a(4)
>>> <type 'tuple'>
>>> (4,)

a(4, 5)
>>> <type 'tuple'>
>>> (4,5,)

但这会迫使你用可变参数调用你的函数,我不知道你是否可以接受它。

您可以使用 type()进行直接类型比较。

def my_func(input):
    if not type(input) is list:
        input = [input]
    for e in input:
        # do something

但是,您拥有它的方式将允许 list 类型派生的任何类型传递。从而防止任何派生类型被意外包裹。

你的方式对我来说似乎是正确的。

它类似于你在Lisp中使用 atom?时迭代列表并检查当前项目是否是列表,因为如果它是你想要处理的列表它的项目也是。

所以,是的,没有看到任何错误。

这是一种可行的方法(不要忘记包含元组)。

但是,您可能还想考虑参数是否具有__iter__ 方法或__getitem__ 方法。 (注意字符串有__getitem__而不是__iter __。)

hasattr(arg, '__iter__') or hasattr(arg, '__getitem__')

这可能是类似列表类型的最常见要求,而不仅仅是检查类型。

这似乎是一种合理的方法。你想测试元素是否是一个列表,这直接完成了。如果你想支持其他类似列表的数据类型,它会变得更加复杂,例如:

isinstance(input, (list, tuple))

或更一般地说,摘要问题:

def iterable(obj):
  try:
    len(obj)
    return True
  except TypeError:
    return False

但总的来说,你的方法简单而正确,听起来不错!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top