【问题标题】:pythonic way to convert variable to listpythonic将变量转换为列表的方法
【发布时间】:2010-11-27 20:21:42
【问题描述】:

我有一个函数,它的输入参数可以是一个元素或元素列表。如果这个参数是单个元素,那么我将它放在一个列表中,这样我就可以以一致的方式迭代输入。

目前我有这个:

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

我正在使用现有 API,因此无法更改输入参数。使用 isinstance() 感觉很麻烦,那么有没有正确的方法来做到这一点?

【问题讨论】:

  • 回复:正确。在 Python 中,通常的术语是“Pythonic”。它承认有很多方法可以做某事,但有些方法更符合语言的精神。

标签: python list arguments


【解决方案1】:

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

【讨论】:

  • 你不是说我的建议吗?
  • 我对彼得的回答的评论是第一位的。哈! :P
【解决方案2】:

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

我会说您需要定义您可能拥有的数据类型,以便您可能拥有:

  • list 的任何后代反对其他任何东西
    • 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]

【讨论】:

    【解决方案3】:

    这是一个不错的方法(不要忘记包含元组)。

    但是,您可能还需要考虑参数是否具有 __iter__ method 或 __getitem__ method。 (注意字符串有 __getitem__ 而不是 __iter__。)

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

    这可能是类列表类型最普遍的要求,而不是只检查类型。

    【讨论】:

      【解决方案4】:

      通常,字符串(纯字符串和 unicode)是唯一您希望将其视为“单个元素”的可迭代对象 - 内置 basestring 专门用于让您使用 isinstance 测试任何一种字符串,所以对于那种特殊情况,这真是太不可思议了;-)。

      所以对于最一般的情况,我建议的方法是:

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

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

      我明确地从每种可迭代项中列出了一个列表,因此您知道您可以进一步执行每种列表技巧 - 排序、多次迭代、添加或删除项目以促进迭代等,所有这些都无需更改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))
      

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

      【讨论】:

      • 调用iter(input)并忽略其结果不是浪费资源吗?如果只检查__iter__ 属性的存在不是更好吗?
      • @Cristian,我认为这并不昂贵,可能是一个函数调用,它的作用与您建议的几乎相同,而且它不依赖于检查魔术属性 __iter__跨度>
      • try/iter(x)/except 并不是特别昂贵。并且,iter(x) 对没有 __iter__ 的某些对象成功(例如,带有合适的 __getitem__ 的对象接受数字键但不接受 __iter__)。
      • 这非常有用,我希望它是标准库的一部分。
      【解决方案5】:

      你可以把 * 放在你的论点之前,这样你总是会得到一个元组:

      def a(*p):
        print type(p)
        print p
      
      a(4)
      >>> <type 'tuple'>
      >>> (4,)
      
      a(4, 5)
      >>> <type 'tuple'>
      >>> (4,5,)
      

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

      【讨论】:

      • 这对他不起作用,因为他说他不能更改参数并且有人可以输入 [1,2] 并且它会被双重包装为 ([1,2],)
      • 呵呵,对不起,我没有看到他说他正在开发 api 的部分。我的错。
      【解决方案6】:

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

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

      所以,是的,看不出有什么问题。

      【讨论】:

        【解决方案7】:

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

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

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

        【讨论】:

        • 这种直接比较不是一个好主意 - 例如,如果用户将list 子类化,您基于type() 的比较将立即中断。 isinstance() 更接近 OP 想要的。
        • 没错,我在提供替代方案,但我的“但是”评论告诉他,他的方式仍然更好。
        【解决方案8】:

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

        isinstance(input, (list, tuple))
        

        或更笼统地说,抽象出问题:

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

        不过,总而言之,您的方法简单而正确,这对我来说听起来不错!

        【讨论】:

        • 我认为您可以使用 hasattr(a, '__iter__') 之类的东西来查看它是否是“类似列表”的数据类型。
        • -1。可迭代对象不一定有 len,也不一定是有限的。
        猜你喜欢
        • 2019-01-25
        • 2014-10-23
        • 2011-12-11
        • 1970-01-01
        • 2019-04-17
        • 2015-04-05
        • 1970-01-01
        • 1970-01-01
        • 2014-07-03
        相关资源
        最近更新 更多