81

我想在列表理解中使用赋值运算符。我怎样才能做到这一点?

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

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

谢谢!

4

8 回答 8

79

Python 3.8 将引入赋值表达式

这是一个新符号::=它允许在(除其他外)理解中赋值。这个新的算子也被称为海象算子

从上面链接的 PEP 的以下片段中可以看出,它将引入大量潜在的计算/内存节省(适用于 SO 的格式):

语法和语义

在可以使用任意 Python 表达式的大多数上下文中,可以出现命名表达式。这是除无括号元组之外的任何有效 Python 表达式的形式,并且NAME := expr是 标识符。exprNAME

这种命名表达式的值与合并的表达式相同,具有额外的副作用,即为目标分配该值:

  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 版本中可用(不推荐用于生产系统!)。您可以在此处找到 Python 3.8 的发布时间表

于 2019-02-04T10:56:51.203 回答
37

看起来您将列表理解与Python 中的循环结构混淆了。

列表推导产生——一个列表!它不适合现有列表中的单个分配。(虽然你可以折磨语法来做到这一点......)

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

像这样循环列表:

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

是一个很棒的教程。

于 2012-04-24T05:11:56.670 回答
13

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

赋值是一个语句,即使语法有时会诱使您认为它是一个表达式(例如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] = ''

列表推导用于它们的返回值,它们在内部创建一个循环......如果你想要的是循环,那么只需编写一个循环......任何阅读代码试图理解它的作用的人都会非常感激(并且几周后包括你自己的人)。

于 2012-04-24T06:20:03.977 回答
9

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

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

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)
    
于 2012-04-24T05:14:37.163 回答
1

正如上面的答案中提到的,列表理解功能中不可能直接分配,但是有一种可破解的方法可以使用该exec方法来实现分配。

不仅assignments我们可以在方法中传递任何 python 表达式exec来评估它。

在你的情况下,它可以通过

  [ exec(f"lst[0] = ''") for pattern in start_pattern if lst[0] == pattern ]

如果我们有一个对象列表,并且我们想在特定条件下为对象的特定属性分配一个值,那么它可能类似于:

[ exec("p.age_status=child") for p in persons_obj_list if p.age <= 18 ]

注意:这将更改列表中现有对象的状态,并且不会返回对象列表,因为它在正常使用列表理解时返回。

于 2020-12-31T15:50:51.070 回答
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 ]
于 2018-07-31T01:08:23.207 回答
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}]
于 2019-11-08T06:26:52.323 回答
0

我可以建议你,使用类而不是使用变量来赋值。当你使用一个类时,你可以简单地改变类的值,然后返回结果。

我添加了一个我做过的例子:

def foo():
class result:
    def __init__(self):
        self.key = 0
        self.value = 0
        
    def change(self, key, value):
        self.key= key
        self.value= value
        return key

result_object= result()
class_result= [result_object.change(key, value) for key, value in temp_dict.items() if value > result_object.value][-1]
return class_result
于 2021-12-03T22:16:14.303 回答