Pythonic方式将变量转换为列表

21 投票
8 回答
31491 浏览
提问于 2025-04-15 14:17

我有一个函数,它的输入参数可以是一个元素,也可以是一个元素的列表。如果这个参数是单个元素,我就把它放进一个列表里,这样我就可以以一致的方式来处理输入。

现在我有这样的代码:

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

我在使用一个现有的API,所以我不能改变输入参数。用isinstance()来判断感觉有点不太好,有没有什么更“正规”的方法来做到这一点呢?

8 个回答

4

首先,没有一种通用的方法可以区分“单个元素”和“元素列表”,因为根据定义,列表本身可以是另一个列表的一个元素。

我认为你需要先定义一下你可能会遇到的数据类型,这样你可以有:

  • 任何列表的子类与其他任何东西进行比较
    • 可以用 isinstance(input, list) 来测试(所以你的例子是正确的)
  • 除了字符串以外的任何序列类型(在 Python 2.x 中是 basestring,在 Python 3.x 中是 str
    • 使用序列元类:isinstance(myvar, collections.Sequence) and not isinstance(myvar, str)
  • 某些序列类型与已知的情况进行比较,比如 intstrMyClass
    • 可以用 isinstance(input, (int, str, MyClass)) 来测试
  • 任何可迭代的对象,除了字符串:
    • 可以进行测试

.

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

通常情况下,字符串(包括普通字符串和Unicode字符串)是你希望当作“单个元素”来处理的唯一可迭代对象。basestring这个内置类型就是为了让你可以用isinstance来检查这两种字符串,所以在这种特殊情况下,它非常好用;-)。

所以我建议的最通用的方法是:

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

这就是处理除了字符串以外的所有可迭代对象的方法,把字符串、数字和其他不可迭代的东西当作单个值(转化为单项列表)。

我明确地把每种可迭代对象都转成一个列表,这样你就知道可以进行各种列表操作,比如排序、重复遍历、添加或删除项目以便于遍历等等,而不会改变实际输入的列表(如果它确实是一个列表的话;-)。如果你只需要一个简单的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))

这样的话,复杂的规范化逻辑就只在一个地方,正如它应该的那样!-)

10

我很喜欢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

撰写回答