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

问题描述 投票:9回答:9

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

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

python list loops split iteration
9个回答
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'}

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

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


3
投票

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

out = []

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

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]))

1
投票

如果您只需要清除列表,

使用out = []out.clear()

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

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

然后结果如下所示:

a c e

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


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 = []


0
投票

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

只是用

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

0
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.