【问题标题】:Why wont my code work for a single list but works for a nested list?为什么我的代码不适用于单个列表但适用于嵌套列表?
【发布时间】:2013-11-26 06:56:55
【问题描述】:

当有多个列表传递给函数时,第一个打印系统工作。但是,当只传入一个列表时,我收到错误“AttributeError:'int' object has no attribute 'pop'”

此代码正在尝试从列表中删除一项,以查看该弹出项是否仍存在于剩余列表中。

def check_row(p):
    for e in p:
        while e:
            x = e.pop()
            if x in e:
                return False
    return True


print check_row([[8,2,3,4,5],
              [2,3,1,5,6],
              [4,0,2,3,1]])

print check_row([1,2,3,4,5])

非常感谢。

【问题讨论】:

    标签: python list nested-lists


    【解决方案1】:

    您正在从 元素 中弹出项目,而不是外部列表。如果你的元素不是列表,那么不要试图这样对待它们。

    但是,您不能在循环遍历外部列表的同时从外部列表中删除项目,并期望循环不会跳转项目。

    如果要查看某个项目是否在列表中出现多次,请改为比较列表的set() 的长度:

    def check_row(row):
        return len(row) == len(set(row))
    

    这仅适用于可散列值,嵌套列表不适用,但至少不会像您的代码那样改变现有的列表。

    您仍然可以使用列表扫描,但至少使用list.index() 将搜索限制为超出当前位置的起始索引:

    def check_row(row):
        for i, elem in enumerate(row):
            try:
                row.index(elem, i + 1)
                return False  # dupe found
            except ValueError:
                pass  # no dupe found
        return True
    

    但是,这假设您只想测试外部列表的重复项。在同一代码中支持嵌套结构和平面结构,而无需详细说明每种情况下您期望发生的情况要复杂得多。

    【讨论】:

    • 我很困惑...您的示例代码适用于平面列表案例,但不适用于嵌套列表案例,因为 list 不可散列...
    • @mgilson:该死,这是真的;这仅适用于列表中的可散列值。
    • @mgilson:但是,我怀疑 OP 并不是要在嵌套列表上使用它,只是发现代码不起作用除非他使用嵌套列表。
    • 是的,我不完全确定 OP 究竟想在这里做什么......但我认为这可能值得指出。此外,我并不是每天都可以要求大忍者本人对答案做出澄清。 :-P
    • @mgilson:在那里,添加了一个低效的“扫描”版本,至少不会破坏过程中的列表。
    【解决方案2】:

    在单个(非嵌套)列表的情况下,您在不是列表的元素 (e) 上调用 .pop(),因此可能没有 .pop 方法。

    【讨论】:

      【解决方案3】:

      那是因为e 是您列表中的一个元素。在嵌套的一个中,e 是一个列表,而在第二个中,e 是一个整数。所以e.pop对第二个无效。

      你必须让它总是嵌套:

      >>> print(check_row([[1, 2, 3, 4, 5]]))
      True
      

      这样,传递给 check_row 的值始终是一个嵌套列表,即使它只有一个元素。

      但至于检查元素是否还在其他列表中,我会先展平列表,然后检查列表中是否有重复元素。

      import collections
      def flatten(l):
          for el in l:
              if isinstance(el, collections.Iterable) and not isinstance(el, str):
                  for sub in flatten(el):
                      yield sub
              else:
                  yield el
      
      def check_row(p):
          flat = list(flatten(p))
          return len(flat) == len(set(flat))
      

      这样,check_row 将始终产生您想要的结果,忽略它是一个列表或嵌套列表的事实 :)

      希望这会有所帮助!

      【讨论】:

        【解决方案4】:

        您对自己的命名感到困惑。您调用的函数check_row 实际上检查了一个 list 行,尽管名称如此,因此传递单行失败。您使用无意义的单字母名称这一事实也无济于事。让我们更清楚地重写它:

        如果你想要一个检查单行的函数,

        def check_rows(rows):
            for row in rows:
                while row:
                    element = row.pop()
                    if element in row:
                        return False
            return True
        

        现在应该更清楚它失败的原因:您将row 作为rows 传递给它,所以for row in rows 获取的是元素而不是行,这一切都从那里走下坡路。


        您可能想要的是一个在单行上工作的check_row 函数,然后是一个在每一行上调用check_rowcheck_rows

        def check_row(row):
            while row:
                element = row.pop()
                if element in row:
                    return False
            return True
        
        def check_rows(rows):
            for row in rows:
                if not check_row(row):
                    return False
            return True
        

        但实际上,我根本不知道你为什么想要这个功能。它破坏性地修改行,删除每个元素直到第一个重复。你为什么要那个?例如,Martijn Pieters 的解决方案更简单、更高效,并且是非破坏性的:

        def check_row(row):
            return len(set(row)) == len(row)
        

        在此过程中,让我们使用all 函数而不是check_rows 的显式循环:

        def check_rows(rows):
            return all(check_row(row) for row in rows)
        

        【讨论】:

          猜你喜欢
          • 2016-06-03
          • 1970-01-01
          • 2022-01-18
          • 2013-10-08
          • 1970-01-01
          • 2016-01-23
          • 1970-01-01
          • 2013-06-23
          • 1970-01-01
          相关资源
          最近更新 更多