如何将字符串空格分隔的键,唯一字的值对转换为字典

时间:2016-02-25 04:24:41

标签: python list loops split iteration

我有一个字符串,其中的单词用空格分隔(所有单词都是唯一的,没有重复)。我将此字符串转换为列表:

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个项目。

9 个答案:

答案 0 :(得分:13)

至于 for 循环中实际发生的事情:

  

来自 Python for statement documentation

     

表达式列表的评估一次;它应该产生一个可迭代的   宾语。为expression_list的结果创建了一个迭代器。   然后,对于由提供的每个项目,一次执行该套件   迭代器,按升序索引。每个项目依次是   使用分配的标准规则分配给目标列表,   然后套件被执行。 当项目耗尽时(即   当序列为空时立即,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的原因,因为这是你删除一个元素时完成循环所需的周期数从每个周期开始。

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

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

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

答案 1 :(得分: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()接受包含两个元组大小的列表,并将其视为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'}

答案 2 :(得分:3)

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

out = []

答案 3 :(得分:2)

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

对您的问题最简单的直接解决方案是使用切片表示法迭代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 :(得分:2)

我相信你想跟随。

>>> 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'}
>>> 

答案 5 :(得分:1)

如果您只需要清除列表,

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

无论如何,你说的是因为列表的remove功能会影响列表。

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

然后结果显示如下:

一 C ë

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

答案 6 :(得分:1)

问题在于,无论何时从列表中删除值,该特定列表都会动态恢复其值。 也就是说,当您执行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 = []

答案 7 :(得分:0)

问题是你在迭代时使用remove(x)。 &#39;出&#39;变量在remove函数和for循环中都引用。

只需使用

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

答案 8 :(得分:0)

首先,您对'#'进行分割以获取每条记录(一串键,值对)。然后,将每个o分割成空格,以提供[key,value]的列表。 dict()允许您直接从键,值对列表中构建字典。所以:

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

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