我想在以下代码中对
L
中的项目进行循环:
L = [1,2,5,2,1,1,3,4]
L_unique = []
for item in L:
if item not in L_unique:
L_unique.append(item)
列表理解如下:
L_unique = [ item for item in L if item not in ???self??? ]
这在Python中可能吗?如果可以的话,怎样才能做到呢?
这是可能的。这是一个可以做到这一点的黑客,但我不会在实践中使用它,因为它很讨厌并且依赖于可能改变的实现细节,而且我相信它也不是线程安全的。 只是为了证明这是可能的。
你的观点基本上是正确的“某个地方必须存在一个存储理解的当前状态的对象”(尽管它不一定必须是Python列表对象,Python可以以其他方式存储元素并且之后才创建列表对象)。
我们可以在垃圾回收跟踪的对象中找到新的列表对象。在创建推导式列表之前收集列表的 ID,然后再次查看并获取之前不存在的列表。 演示:
import gc
L = [1,2,5,2,1,1,3,4]
L_unique = [
item
for ids in ({id(o) for o in gc.get_objects() if type(o) is list},)
for self in (o for o in gc.get_objects() if type(o) is list and id(o) not in ids)
for item in L
if item not in self
]
print(L_unique)
输出(在线尝试!
在从 Python 3.7 到 Python 3.11 的多个版本中进行了测试和工作。
对于与您要求的样式完全相同的替代方案,仅替换您的
???self???
,请参阅 Mechanic Pig 的更新答案。
gc
是一个疯狂但可行的选择。抱歉之前我的夸张,使用
gc
的解决方案附在最后):>>> [locals().copy() for i in range(3)]
[{'.0': <range_iterator at 0x207eeaca730>, 'i': 0}, # does not contain the built list
{'.0': <range_iterator at 0x207eeaca730>, 'i': 1},
{'.0': <range_iterator at 0x207eeaca730>, 'i': 2}]
>>> dis('[i for i in iterable]')
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x00000211FEAFD000, file "<dis>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_NAME 0 (iterable)
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x00000211FEAFD000, file "<dis>", line 1>:
1 0 BUILD_LIST 0 # build an empty list and push it onto the stack
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 4 (to 14)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LIST_APPEND 2 # get the built list through stack and index
12 JUMP_ABSOLUTE 2 (to 4)
>> 14 RETURN_VALUE
对于您提供的示例,您可以使用 list(dict.fromkeys(L))
在 Python 3.7+ 中获得相同的结果。这里我使用
dict
而不是 set
,因为 dict
可以保留插入顺序:>>> list(dict.fromkeys(L))
[1, 2, 5, 3, 4]
根据@KellyBundy,我目前找到的方法是使用gc.get_objects
,但是这个操作非常昂贵(因为它收集了超过1000个对象)并且我无法确定其准确性:
>>> [item for item in L if item not in gc.get_objects(0)[-1]]
[1, 2, 5, 3, 4]
通过缓存降低操作成本:
>>> lst = None
>>> [item for item in L if item not in (lst := gc.get_objects(0)[-1] if lst is None else lst)]
[1, 2, 5, 3, 4]
L_unique
不会
存在,但您可以使用set理解。
L = [1,2,5,2,1,1,3,4]
L_unique = {x for x in L}
如果您希望将其他功能应用于
x
,这是灵活的,但在这种简单的形式中,您最好只使用:
L = [1,2,5,2,1,1,3,4]
L_unique = set(L)
如果需要,可以将
set
转换回
list
。L = [1,2,5,2,1,1,3,4]
L_unique = list(set(L))
与原始列表相比,使用集合可能会改变元素的顺序。
3.8版本更改:新生成参数。
引发审核事件 gc.get_objects 并生成参数。
不使用
gc
L = [1,2,5,2,1,1,3,4]
L_unique = []
# This returns just a list of None
_ = [L_unique.append(i) for i in L if i not in L_unique]
L_unique
输出:
[1, 2, 3, 4, 5]
或者你可以使用这个:
L = [1,2,5,2,1,1,3,4]
list(set(L))
输出:
[1, 2, 3, 4, 5]
unique_everseen()
库中的
more_itertools
函数:>>> from more_itertools import unique_everseen
>>> L = [1,2,5,2,1,1,3,4]
>>> list(unique_everseen(L))
[1, 2, 5, 3, 4]
这类似于其他几个人建议的
set()
方法,但保证保留顺序。