【问题标题】:Python: Referring to a list comprehension in the list comprehension itself?Python:在列表推导本身中引用列表推导?
【发布时间】:2016-02-13 10:14:38
【问题描述】:

这个想法刚刚出现在我的脑海中。无论出于何种原因,您都想通过 Python 中的列表推导式获取列表的唯一元素。

[i if i in {created_comprehension} else 0 for i in [1, 2, 1, 2, 3]

[1, 2, 0, 0, 3]

我不知道,我真的没有这样做的目的,但如果可以在创建时引用理解,那就太酷了。

(例如How to remove duplicate items from a list using list comprehension? 是一个类似的问题)

【问题讨论】:

  • 除非有一些我不知道的魔法,否则你最好使用 for 循环并在那里更新另一个列表。

标签: python list list-comprehension


【解决方案1】:

我假设 i in {created_comprehension} 应该是 i not in {created_comprehension}。至少数据表明是这样的。

所以这是一个有趣的可怕滥用,我不相信它总是有效的。主要是为了证明“这是不可能的,因为它还没有被赋值”这个论点是错误的。虽然列表对象确实尚未分配,但它确实在构建时已经存在

>>> import gc
>>> [i if i not in self else 0
     for ids in [set(map(id, gc.get_objects()))]
     for self in [next(o for o in gc.get_objects() if o == [] and id(o) not in ids)]
     for i in [1, 2, 1, 2, 3]]
[1, 2, 0, 0, 3]

这会获取为垃圾收集跟踪的所有对象的 ID,在创建新列表之前,然后在创建新列表之后,我们通过搜索新列表来找到它跟踪的空列表。调用它self 然后你就可以使用它了。所以中间的两行是一个通用的配方。我也成功地将它用于this question,但在我发帖之前它就被关闭了。

更好的版本:

>>> [i if i not in self else 0
     for old in [ids()] for self in [find(old)]
     for i in [1, 2, 1, 2, 3]]
[1, 2, 0, 0, 3]

使用了这些辅助函数:

def ids():
    import gc
    return set(map(id, gc.get_objects()))

def find(old):
    import gc
    return next(o for o in gc.get_objects() if o == [] and id(o) not in old)

【讨论】:

  • 哇。这……太可怕了。以一种有趣的方式,但仍然令人恐惧。 :-)
  • @ShadowRanger 现在添加了更好的版本。我会说它几乎可以将它放在 PyPI 上。
  • 哈。 (傻)建议:在ids的开头加上gc.disable(),在find的末尾加上gc.enable(),这样你就可以防止中间的代更迭,并且可以只看最年轻的一代(调用gc.get_objects(0))来减少检查对象的数量。显然,性能是这种疯狂中最重要的事情。 :-)(不确定这是否能保证工作;应检查gc 是否可能在BUILD_LISTids() 调用之间发生;可能有助于将ids() 包装为(ids(),) 以避免产生list) .
  • @ShadowRanger 是的,这听起来很棒,因为最年轻的一代通常相当小。我还考虑通过仅存储空列表的 id 来优化空间。我正在使用 3.9,认为 in [ids()] 已经没有建立列表,而是 directly assigns。事实证明两者都不是,而是BUILD_TUPLE(以及对它的迭代)。奇怪的。对于[find(old)],它是STORE_FAST,正如预期的那样。
【解决方案2】:

免责声明:这纯粹是我的猜测,我没有数据支持它

我认为您不能在构建列表理解时引用它。 Python 将首先必须创建列表、分配内存或向其添加元素,然后才能将其绑定到变量名。因此,如果您尝试引用该列表,我认为您最终会得到一个 NameError,而它是在 list-comp 中构建的

因此,您最终可能想要一个 set 来保存您的唯一身份,并从那里建立您的列表(天哪!这太老套了):

In [11]: L = [1, 2, 1, 2, 3]

In [12]: s = set(L)

In [13]: answer = [sub[0] for sub in [(i,s.remove(i)) if i in s else (0,0) for i in L]]

In [14]: answer
Out[14]: [1, 2, 0, 0, 3]

In [15]: s
Out[15]: set()

【讨论】:

  • 在 CPython 2.6 之前,可以使用 locals()['_[1]']: [x if x not in locals()['_[1]'] else 0 for x in L] 访问列表。为此,我发现好的 ol' seen set hack 更容易理解:seen = set(); [x if x not in seen and not seen.add(x) else 0 for x in L].
【解决方案3】:

免责声明:这只是一个实验。我比较了list comprehensionlist 内的list comprehension

我希望x 包含来自[1,2,1,2,3,4,5] 的元素,前提是这些元素在这个list comprehension [e for e in range(3,6)] 中,应该是[3,4,5]

x = [i for a in [e for e in range(3,6)] for i in [1,2,1,2,3,4,5] if i == a]

输出是对的:

[3, 4, 5]

【讨论】:

    猜你喜欢
    • 2019-11-14
    • 1970-01-01
    • 2012-05-04
    • 2010-11-07
    • 2017-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多