【问题标题】:How to transform string of space-separated key,value pairs of unique words into a dict如何将空格分隔的键值对字符串转换为字典
【发布时间】:2016-06-07 16:42:54
【问题描述】:

我有一个字符串,其中的单词由空格分隔(所有单词都是唯一的,没有重复)。我把这个字符串变成列表:

s = "#one cat #two dogs #three birds"
out = s.split()

并计算创建了多少值:

print len(out) # Says 192 

然后我尝试从列表中删除所有内容:

for x in out:
     out.remove(x)

然后再数一遍:

print len(out) # Says 96 

谁能解释一下为什么它说的是 96 而不是 0?

更多信息

每一行都以“#”开头,实际上是一对以空格分隔的单词:对中的第一个是键,第二个是值。

所以,我正在做的是:

for x in out:
     if '#' in x: 
          ind = out.index(x) # Get current index 
          nextValue = out[ind+1] # Get next value 
          myDictionary[x] = nextValue
          out.remove(nextValue)
          out.remove(x) 

问题是我无法将所有键值对移动到字典中,因为我只遍历了 96 个项目。

【问题讨论】:

  • “然后我尝试从列表中删除所有内容”——让我们在这里更深入地挖掘。为什么要这样做?
  • 因为我将列表中的值成对移动到字典中,比如第一个值是键,第二个值是值。问题是我的字典不完整,因为我无法完整地遍历列表 - 它说只有 96 个索引。
  • 如果您可以使用您正在使用的字符串和字典发布代码,我们将更容易提供解决方案。问题可能出在您目前提供给我们的其他地方。
  • 你不应该从你正在迭代的列表中删除项目..这是未定义的行为..更好地使用列表理解

标签: python list loops split iteration


【解决方案1】:

首先,您拆分“#”以获取每条记录(一串键值对)。然后你在空间上分割每个 o,给你一个 [key,value] 的列表。 dict() 允许您直接从键值对列表中构造字典。所以:

>>> dict( k_v.split() for k_v in s.split('#')[1:] )
{'one': 'cat', 'two': 'dogs', 'three': 'birds'}

(注意:我们必须使用s.split('#')[1:] 来跳过第一条(空白)记录)

【讨论】:

    【解决方案2】:

    至于 for 循环中实际发生了什么:

    来自Python for statement documentation

    表达式列表被评估一次;它应该产生一个可迭代的 目的。为expression_list 的结果创建一个迭代器。 然后,该套件对由 迭代器,按索引升序。每个项目依次是 使用标准分配规则分配给目标list, 然后执行套件。 物品用完时(即 当序列为empty时立即),else 子句中的套件, 如果存在,则执行,loop 终止

    我认为最好借助插图来展示它。

    现在,假设您有一个像这样的iterable object(例如list):

    out = [a, b, c, d, e, f]
    

    当您执行for x in out 时会发生什么,它创建内部索引器,如下所示(我用符号^ 说明它):

    [a, b, c, d, e, f]
     ^  <-- here is the indexer
    

    通常发生的情况是:当您完成循环的一个循环时,索引器会向前移动,如下所示:

    [a, b, c, d, e, f] #cycle 1
     ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 2
        ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 3
           ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 4
              ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 5
                 ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 6
                    ^  <-- here is the indexer
    
    #finish, no element is found anymore!
    

    如您所见,索引器会一直向前移动,直到结束 列表,不管列表发生了什么

    因此,当您执行remove 时,这就是内部发生的情况:

    [a, b, c, d, e, f] #cycle 1
     ^  <-- here is the indexer
    
    [b, c, d, e, f] #cycle 1 - a is removed!
     ^  <-- here is the indexer
    
    [b, c, d, e, f] #cycle 2
        ^  <-- here is the indexer
    
    [c, d, e, f] #cycle 2 - c is removed
        ^  <-- here is the indexer
    
    [c, d, e, f] #cycle 3
           ^  <-- here is the indexer
    
    [c, d, f] #cycle 3 - e is removed
           ^  <-- here is the indexer
    
    #the for loop ends
    

    请注意,那里只有 3 个循环,而不是 6 个循环(!!)(这是原始列​​表中的元素数)。这就是为什么您留下了原来len一半 len,因为这是在每个循环中从循环中删除一个元素时完成循环所需的循环数。 p>


    如果您想清除列表,只需执行以下操作:

    if (out != []):
        out.clear()
    

    或者,或者,要逐个删除元素,您需要反过来 - 从结尾到开头。使用reversed

    for x in reversed(out):
        out.remove(x)
    

    现在,reversed 为什么会起作用?如果索引器继续前进,reversed 是否也不应该工作,因为每个周期的元素数量都会减少一个?

    不,不是这样的,

    因为reversed方法改变了内部索引器的方式 作品!当您使用 reversed 方法时发生的事情是 内部索引器向后移动(从末尾)而不是 前进

    为了说明,这是通常发生的情况:

    [a, b, c, d, e, f] #cycle 1
                    ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 2
                 ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 3
              ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 4
           ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 5
        ^  <-- here is the indexer
    
    [a, b, c, d, e, f] #cycle 6
     ^  <-- here is the indexer
    
    #finish, no element is found anymore!
    

    因此,当您每个周期执行一次删除时,它不会影响索引器的工作方式:

    [a, b, c, d, e, f] #cycle 1
                    ^  <-- here is the indexer
    
    [a, b, c, d, e] #cycle 1 - f is removed
                    ^  <-- here is the indexer
    
    [a, b, c, d, e] #cycle 2
                 ^  <-- here is the indexer
    
    [a, b, c, d] #cycle 2 - e is removed
                 ^  <-- here is the indexer
    
    [a, b, c, d] #cycle 3
              ^  <-- here is the indexer
    
    [a, b, c] #cycle 3 - d is removed
              ^  <-- here is the indexer
    
    [a, b, c] #cycle 4
           ^  <-- here is the indexer
    
    [a, b] #cycle 4 - c is removed
           ^  <-- here is the indexer
    
    [a, b] #cycle 5
        ^  <-- here is the indexer
    
    [a] #cycle 5 - b is removed
        ^  <-- here is the indexer
    
    [a] #cycle 6
     ^  <-- here is the indexer
    
    [] #cycle 6 - a is removed
     ^  <-- here is the indexer
    

    希望插图可以帮助您了解内部发生的情况......

    【讨论】:

    • 这是我见过的在迭代期间修改的最好解释之一。我希望我能不止一次地对此表示赞同!
    • 您对我的回答给出了明确的解释!非常感谢!
    • 正要发布一个小例子,感谢您解决实际问题!
    【解决方案3】:

    您遇到的问题是在迭代列表时修改列表的结果。当一个项目被删除时,它之后的所有内容都会向前移动一个索引,但迭代器不考虑更改并继续增加它上次访问的索引。因此,迭代器会跳过列表中的每个第二个元素,这就是为什么您只剩下一半元素的原因。

    对您的问题最简单的直接解决方案是使用切片表示法遍历out副本

    for x in out[:]:
        # ...
        out.remove(x)
    

    但是,这里有一个更深层次的问题:为什么需要从列表中删除项目?使用您的算法,您一定会得到一个空列表,这对您没有用处。只遍历列表而不删除项目会更简单、更有效。

    当您完成列表后(在 for 循环块之后),您可以显式删除它(使用 del 关键字)或将其留给 Python 的垃圾回收系统处理。

    还有一个问题:您正在将列表上的直接迭代与基于索引的引用结合起来。 for x in out 的使用通常应仅限于您希望独立于其他元素访问每个元素的情况。如果您想使用索引,请使用 for i in range(len(out)) 并使用 out[i] 访问元素。

    此外,您可以使用字典推导在一行pythonic表达式中完成您的整个任务:

    my_dictionary = {out[i]: out[i + 1] for i in range(len(out)) if "#" in out[i]}
    

    另一个pythonic替代方法是利用每个偶数元素是一个键,每个奇数元素是一个值的事实(你必须假设str.split()的列表结果始终遵循这种模式),并在偶数和奇数子列表上使用zip

    my_dictionary = dict(zip(out[::2], out[1::2]))
    

    【讨论】:

      【解决方案4】:

      问题是每当您从列表中删除一个值时,该特定列表会动态恢复其值。 即当你执行out.remove(ind)out.remove(ind+1)时,这些索引中的值被删除, 但它们被替换为新值,这些值是先前值的前身。

      因此,为避免这种情况,您必须按如下方式实现代码:

      out = []
      out = '#one cat #two dogs #three birds'.split()
      
      print "The list is : {0} \n".format(out)
      myDictionary = dict()
      
      for x in out:
      
          if '#' in x:
              ind = out.index(x)  # Get current index
              nextValue = out[ind+1]  # Get next value
              myDictionary[x] = nextValue
      
      out = []  # #emptying the list
      print("The dictionary is : {0} \n".format(myDictionary))
      

      因此,在您完成将值从列表传输到字典后,我们可以安全地清空 out 使用out = []

      【讨论】:

        【解决方案5】:

        我相信你想要关注。

        >>> a = '#one cat #two dogs #three birds'
        >>> b = { x.strip().split(' ')[0] : x.strip().split(' ')[-1] for x in a.strip().split('#') if len(x) > 0 }
        >>> b
        {'three': 'birds', 'two': 'dogs', 'one': 'cat'}
        

        甚至更好

        >>> b = [ y   for x in a.strip().split('#') for y in x.strip().split(' ') if len(x) > 0 ]
        >>> c = { x: y for x,y  in zip(b[0::2],b[1::2]) }
        >>> c
        {'three': 'birds', 'two': 'dogs', 'one': 'cat'}
        >>> 
        

        【讨论】:

          【解决方案6】:

          如果你只需要清除列表,

          使用 out = [] 要么 out.clear()

          反正你说的那是因为removelist 的函数影响了list。

          out = ['a', 'b', 'c', 'd', 'e', 'f']
          for x in out:
              out.remove(x)
              print(x)
          

          那么结果如下所示:

          一个 C e

          它正好是完整列表的一半。所以,在你的情况下,你从 192 得到了 96(192 的一半)。

          【讨论】:

            【解决方案7】:

            问题是您在迭代时使用了 remove(x) 。 'out' 变量在 remove 函数和 for 循环中都引用。

            随便用

            for i in range(len(out)):
                 out.remove(out[i]);
            

            【讨论】:

              【解决方案8】:

              我认为你实际上想要这样的东西:

              s = '#one cat #two dogs #three birds'
              out = s.split()
              entries = dict([(x, y) for x, y in zip(out[::2], out[1::2])])
              

              这段代码在做什么?让我们分解一下。首先,我们用空格将s 拆分为out,就像你一样。

              接下来我们遍历out 中的对,称它们为“x, y”。这些对成为元组/对的listdict() 接受大小为 2 的元组列表并将它们视为 key, val

              这是我尝试后得到的结果:

              $ cat tryme.py
              
              s = '#one cat #two dogs #three birds'
              out = s.split()
              entries = dict([(x, y) for x, y in zip(out[::2], out[1::2])])
              
              from pprint import pprint
              pprint(entries)
              
              $ python tryme.py
              {'#one': 'cat', '#three': 'birds', '#two': 'dogs'}
              

              【讨论】:

              • 是的,这行得通!万分感谢。有没有办法我也可以在这个过程中包含一个条件?因为我需要在将某些值传递到字典之前对其进行修改。我需要摆脱一个井号,通过 entries = dict([(x[1:], y) for x, y in zip(out[::2], out[1::2])] ) 但在某些情况下,我需要修改某些字符,例如,如果单词包含“关键字”,我需要将其替换为另一个单词。
              • 将“列表理解”重写为传统的for-loop。第一行就像“for key, val in zip(out[::2], out[1::2]): 然后你可以对keyval 做任何你想做的事情。然后像这样将它们添加到你的字典中:entries[key] = val。不要忘记在开头创建字典, entries = {},例如
              • 谢谢!非常感谢您的帮助!
              【解决方案9】:

              你没有说具体。你为什么要删除列表中的所有内容?如果您需要做的就是清除 out-list,为什么不这样做:

              out = []
              

              【讨论】:

                猜你喜欢
                • 2019-02-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-01-23
                • 1970-01-01
                相关资源
                最近更新 更多