我如何进行列表理解中的作业?

问题描述 投票:43回答:6

我想在列表理解中使用赋值运算符。我该怎么办?

以下代码是无效的语法。我的意思是将lst[0]设置为空字符串'',如果它与pattern相匹配:

[ lst[0] = '' for pattern in start_pattern if lst[0] == pattern ]

谢谢!

python list-comprehension
6个回答
25
投票

似乎您在Python中将list comprehensionlooping constructs混淆了。

列表理解产生-列表!它不会将自己分配给现有列表中的单个分配。 (尽管您可以通过折磨语法来做到这一点...)

虽然不清楚您要从代码中做什么,但我认为它更类似于遍历列表(流控制)而不是生成列表(列表理解)

像这样遍历列表:

for pattern in patterns:
   if lst[0] == pattern: lst[0]=''

这是一种合理的方法,这是您在C,Pascal等中所要做的。但是您也可以只测试列表中的一个值并更改它:

if lst[0] in patterns: lst[0] = ''

或者,如果您不知道索引:

i=lst.index[pattern]
lst[i]=''

或者,如果您有一个列表列表,并且想要更改每个子列表的每个第一个元素:

for i, sublst in enumerate(lst):
    if sublst[i][0] in patterns: sublist[i][0]=''

等,等等,等等

如果要对列表的每个元素应用某些内容,则可以使用列表推导,映射或Python套件中许多其他工具之一来进行查看。

个人,我通常倾向于更多地使用列表理解来创建列表:

 l=[[ x for x in range(5) ] for y in range(4)]  #init a list of lists...

比哪个更自然:

l=[]
for i in range(4):
   l.append([])
   for j in range(5):
      l[i].append(j)      

但是要修改相同的列表列表,哪个更容易理解?

此:

l=[['new value' if j==0 else l[i][j] for j in range(len(l[i]))] for i in range(len(l))]

或此:

for i,outter in enumerate(l):
    l[i][0]='new value'               

YMMV

Here是一个很棒的教程。


12
投票

Python 3.8将引入Assignment Expressions

这是一个新符号::=允许进行(尤其是)理解中的分配。

这将带来很多潜在的节省成本。计算/内存,如上述链接的PEP(适用于SO的格式)的以下代码段所示:

语法和语义

在大多数情况下,可以使用任意Python表达式,出现命名的表达式。格式为NAME := expr,其中expr是除无括号外的任何有效Python表达式元组,NAME是一个标识符。

这样的命名表达式的值与合并的表达式相同表达式,另外还有目标是分配了该值:

  1. 处理匹配的正则表达式

    if (match := pattern.search(data)) is not None:
        # Do something with match
    
  2. 不能使用2-arg iter()简单重写的循环

    while chunk := file.read(8192):
        process(chunk)
    
  3. 重新使用计算起来很昂贵的值

    [y := f(x), y**2, y**3]
    
  4. 在理解过滤器子句及其输出之间共享一个子表达式

    filtered_data = [y for x in data if (y := f(x)) is not None]
    

这在最近发布的alpha版本中已经可用(不建议用于生产系统!)。您可以找到the release schedule for Python 3.8 here


9
投票

Python语言对表达式和语句有不同的概念。

[Assignment是一条语句,即使语法有时使您误以为它是一个表达式(例如a=b=99可以工作,但是是一种特殊的语法情况,并不意味着b=99是一个表达式,例如在C语言中)。

列表推导是表达式,因为它们返回一个值,从某种意义上说,它们执行的循环是一个事件,而重点是返回的列表。

一个语句可以包含表达式,但是一个表达式不能包含语句。

但是表示列表项归属在内部转换为方法调用(以允许创建类似列表的对象),而方法调用是表达式。因此,您可以技术上在表达式中使用列表项分配:

[ lst.__setitem__(0, '') for pattern in start_pattern if lst[0] == pattern ]

然而,这被认为是不好的,因为它会损害可读性,并且在Python语言中,主要焦点易于阅读源代码。例如,您应该写...

for pattern in start_pattern:
    if lst[0] == pattern:
        lst[0] = ''

通过in运算符,这相当于更易读的]

if lst[0] in start_pattern:
    lst[0] = ''

列表推导用于其返回值,它们在内部创建一个循环...如果您想要的是循环,则只需编写一个循环...谁将阅读代码以尝试理解其功能,将不胜感激(以及几周内包括您在内的任何人)。


6
投票

简而言之:您没有。列表推导用于生成列表,而不是修改现有列表。如果要修改列表,请使用for循环,因为这就是它们的作用。

Python编写该代码的方式类似于:

for pattern in start_pattern:
    if lst[0] == pattern:
        lst[0] = ''
        #the following assumes that pattern will never be ''
        #if that's possible, you can ignore this bit
        break

但是,如果您确实真的想在一个内部进行赋值,并且不介意每一个Python程序员都永远讨厌您的代码,那么可以使用一些功能:

  • 如果要分配给的变量是全局变量,则可以这样做

        globals().update(var=value)
    
  • 如果您要分配给的变量是可变序列或映射(例如列表或字典)

        list.__setitem__(index, value)
    

0
投票

如果您要提出的问题是:

[ lst[0] = '' for lst in listOfLists if lst[0] == pattern ]

或用于模式列表

[ lst[0] = '' for lst in listOfLists if lst[0] in patterns ]

实际上可以轻松完成

[ [''] + lst[1:] for lst in listOfLists if lst[0] == pattern ]

或再次输入模式列表

[[''] + lst[1:] for lst in listOfLists if lst[0] in patterns ]

0
投票

也许这并不是您想要的,但我认为值得介绍这种情况。

假设您有list个字典,例如:

>>> fruits
[{'name': 'apple', 'quantity': 5}, {'name': 'banana', 'quantity': 4}]

通过普通的列表理解,您可能会找到想要的对象:

>>> [d for d in fruits if d['name'] == 'apple'] 
[{'name': 'apple', 'quantity': 5}]

结果,由于上面的if条件,您的列表只有一个元素。

因此,您可以索引唯一的元素,访问其中一个字典键并分配值:

>>> [d for d in fruits if d['name'] == 'apple'][0]['quantity'] += 1

这里是结果:

>>> fruits
[{'name': 'apple', 'quantity': 6}, {'name': 'banana', 'quantity': 4}]
© www.soinside.com 2019 - 2024. All rights reserved.