简体   繁体   English

为什么这个列表理解不能做我期望它做的事情?

[英]Why doesn't this list comprehension do what I expect it to do?

The original list project_keys = sorted(projects.keys()) is [101, 102, 103, 104, 105, 106, 107, 108, 109, 110] where the following projects were deemed invalid this year: 108, 109, 110. 原始列表project_keys = sorted(projects.keys())[101, 102, 103, 104, 105, 106, 107, 108, 109, 110] ,其中以下项目今年被视为invalid :108,109,110 。

Thus: 从而:

for project in projects.itervalues():
# The projects dictionary is mapped to the Project class
    if project.invalid:
    # Where invalid is a Bool parameter in the Project class
     project_keys.remove(project.proj_id)  

print project_keys

This will return a list of integers (which are project id's) as such: 这将返回一个整数列表(项目ID为):

[101, 102, 103, 104, 105, 106, 107]

Sweet. 甜。

Now, I wanted it try the same thing using a list comprehension. 现在,我希望它使用列表理解尝试相同的事情。

project_keys = [project_keys.remove(project.proj_id) for project in projects.itervalues() if project.invalid  

print project_keys

This returns: 返回:

[None, None, None]

So I'm populating a list with the same number as the removed elements but they're None s? 所以我填充的列表与删除的元素具有相同的数字,但它们是None

Can someone point out what I'm doing wrong? 有人可以指出我做错了吗?

Additionally, why would I use a list comprehension over the for-if block at the top? 另外,为什么我会使用列表理解而不是顶部的for-if块? Conciseness? 简明? Looks nicer? 看起来更好?

Your list comprehension works using side-effects. 您的列表理解使用副作用。 Just executing it should update project_keys to give the result you want. 只是执行它应该更新project_keys以提供您想要的结果。

[project_keys.remove(project.proj_id)
 for project in projects.itervalues()
 if project.invalid]

The return value from remove is None . remove的返回值为None Assigning the result of the list comprehension to project_keys is where you are going wrong. 将列表project_keys的结果分配给project_keys是您出错的地方。

A simple loop is probably clearer here though. 一个简单的循环可能在这里更清楚。 A list comprehension that uses side-effects can be confusing. 使用副作用的列表理解可能会令人困惑。

However you can solve your problem in a slightly different way: 但是,您可以稍微不同的方式解决您的问题:

project_keys = sorted(project.proj_id
                      for project in projects.itervalues()
                      if not project.invalid)

This keeps the projects you are interested in, instead of removing those that you're not interested in. The example I gave above uses a generator expression instead of a list comprehension, but it would work with either. 这会保留您感兴趣的项目,而不是删除您不感兴趣的项目。上面给出的示例使用生成器表达式而不是列表推导,但它可以使用。

You, sir, have misunderstood list comprehensions. 先生,您误解了列表理解。

What you probably wanted (in words) 你可能想要什么(用语言)

I want to remove all project ids that are invalid. 我想删除所有无效的项目ID。

What you wrote 你写的是什么

project_keys = [project_keys.remove(project.proj_id)
                for project in projects.itervalues() if project.invalid]

What is actually going on 实际上是怎么回事

dummy = []
for project in projects.itervalues():
  if project.invalid:
    dummy.append(project_keys.remove(project.proj_id)) #what are you
project_keys = dummy                                   #removing items from?
del dummy                                            

What is actually going on (now with more "functional") 实际发生了什么(现在更具“功能性”)

mapped-fun = lambda project: project_keys.remove(project.proj_id)
filtering-fun = lambda project: project.invalid
project_keys = map(mapped-fun, filter(filtering-fun, projects.itervalues()))

As you can see, list comprehensions are not syntactical sugar around for loops. 正如您所看到的,列表推导不是围绕for循环的语法糖。 Rather, list comprehensions are syntactical sugar around map() and filter() : apply a function to all items in a sequence that match a condition and get a list of results in return. 相反, 列表推导是围绕map()filter()语法糖 :将函数应用于与条件匹配的序列中的所有项,并获得返回的结果列表

Here, by function it is actually meant a side-effect-free transformation of input into output. 在这里,通过功能,它实际上是指输入到输出的无副作用转换。 This means that you "cannot" use methods that change the input itself, like list.sort() ; 这意味着你“不能”使用改变输入本身的方法,如list.sort() ; you'll have to use their functional equivalents, like sorted() . 你必须使用它们的功能等价物,比如sorted()

By "cannot", however, I don't mean you'll get error messages or nasal demons ; 但是,“不能”,我并不是说你会得到错误信息或鼻子恶魔 ; I mean you are abusing the language. 我的意思是你在滥用这种语言。 In your case, the evaluation of the list comprehension that happens as you assign it to a variable does indeed produce the intended side-effects -- but does it produce them on the intended variables? 在您的情况下,在将变量分配给变量时对列表理解的评估确实会产生预期的副作用 - 但它是否会在预期变量上产生它们?

See, the only reason why this can execute without an error is that before this list comprehension, there was another list called project_keys and it's that list you are actually changing! 看,这可以在没有错误的情况下执行的唯一原因是,在此列表理解之前,还有另一个名为project_keys的列表,而这个列表实际上正在改变!

Lists comprehensions are a result of functional programming, which rejects side effects. 列表理解是函数式编程的结果,它拒绝副作用。 Keep that in mind when using lists comprehensions. 使用列表推导时请记住这一点。


So here's a thought process you can use to actually get the list comprehension you wanted. 所以这是一个思考过程,你可以用来实际获得你想要的列表理解。

What you actually wanted (in words) 你真正想要的是什么(用语言)

I want all project ids that are valid (= not invalid.) 我想要所有有效的项目ID(=无效。)

What you actually wanted 你真正想要的是什么

dummy = []
for project in projects.itervalues():
  if not project.invalid:
    dummy.append(project.proj_id)
project_keys = dummy
del dummy

What you actually wanted (now with more functional) 你真正想要的是什么(现在功能更强大)

mapped-fun = lambda project: project.proj_id
filtering-fun = lambda project: not project.invalid
project_keys = map(mapped-fun, filter(filtering-fun, projects.itervalues()))

What you actually wanted (now as a list comprehension) 你真正想要的是什么(现在作为列表理解)

project_keys = [project.proj_id for project in projects.itervalues()
                if not project.invalid]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM