在 Python 中从序列里移除重复项且保持元素顺序的方法

在 Python 中从序列里移除重复项且保持元素顺序的方法:

1. 可哈希元素的处理

如果序列中的元素是可哈希的(比如整数、字符串等),可以利用集合set记录已出现的元素,结合生成器yield来实现:

python

运行

def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)
  • 原理:seen集合用于存储已经遍历过的元素,遇到未在seen中的元素时,通过yield返回该元素,并将其加入seen
  • 示例:

    python

    运行

    a = [1, 5, 2, 1, 9, 1, 5, 10]
    print(list(dedupe(a)))  # 输出 [1, 5, 2, 9, 10]
    

2. 不可哈希元素的处理

如果序列中的元素是不可哈希的(比如列表),需要通过key参数将元素转为可哈希类型来判断重复:

python

运行

def dedupe(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)
  • 原理:key参数接收一个函数,用来把不可哈希的元素转换成可哈希的 “标识”,再通过这个标识判断是否重复。

总结:核心是利用集合的成员查询高效性in操作时间复杂度 O (1)),结合生成器保持元素顺序,分别处理可哈希和不可哈希元素的去重场景。


是否需要我补充该代码的实际应用场景示例(比如对字典列表去重)?

我不知道yied是什么操作,为什么要写生成器,函数怎么工作呢

1. yield是什么?

yield是 Python 里定义生成器函数的关键字,和return有本质区别:

  • return会直接结束函数并返回值,函数执行到return就 “终止” 了;
  • yield是 “暂停” 函数,返回一个值给调用方,下次调用生成器时,函数会从暂停的位置继续执行。

简单说,yield让函数变成了一个 “可迭代的生产器”,每次调用只生成一个值,而不是一次性生成所有结果。

2. 为什么用生成器?

这里用生成器主要是为了节省内存

  • 如果直接用列表存储去重后的结果,当原序列非常大时,列表会占用大量内存;
  • 生成器是 “按需生成” 值,每次只处理一个元素、返回一个元素,内存占用始终很小。另外,生成器返回的结果可以直接用list()转换成列表,也能直接迭代,用法灵活。

3. 这个去重函数的工作过程(以第一个dedupe为例):

python

运行

a = [1, 5, 2, 1, 9, 1, 5, 10]
gen = dedupe(a)  # 调用函数,此时函数不会执行,只是返回一个生成器对象
print(next(gen)) # 第一次调用next(),函数开始执行:
                 # 1. seen是空集合,item=1不在seen里,执行yield 1,函数暂停,返回1
print(next(gen)) # 第二次调用next(),函数从暂停处继续:
                 # 2. 把1加入seen,接着循环item=5,5不在seen里,yield 5,暂停,返回5
# 以此类推,直到循环结束,生成器耗尽

当用list(dedupe(a))时,list()会自动不断调用next(),把生成器的所有结果收集成列表。

总结yield让函数变成生成器,实现 “边遍历边输出”,既省内存又能保持元素顺序,这也是这个去重函数的核心设计思路。


是否需要我用分步调试的方式,把这个函数每一步的执行过程详细列出来?

为什么不是append再return

1. 用append+return的写法

如果不用生成器,改用列表appendreturn,代码会是这样:

python

运行

def dedupe(items):
    seen = set()
    result = []
    for item in items:
        if item not in seen:
            result.append(item)
            seen.add(item)
    return result

这样写也能实现去重,但和生成器写法有明显区别。

2. 为什么不选append+return

  • 内存效率不同:如果原序列非常大(比如有百万、千万个元素),result列表会把所有去重后的元素都存起来,占用大量内存;而生成器是 “用一个值生成一个值”,不会一次性存储所有结果,内存占用始终很小。
  • 使用灵活性不同:生成器返回的是可迭代对象,你可以直接迭代它(比如for x in dedupe(a):),也可以按需取部分值(比如用next()取前几个);而return列表的话,必须等函数把所有结果都计算完、存到列表里,才能使用。

3. 什么时候可以用append+return

如果序列的元素数量很少,内存消耗可以忽略,用append+return完全没问题;但如果处理的是大数据量的序列,生成器的优势就非常明显了。

简单说,生成器是 “按需产出”,列表是 “一次性产出”,这里用生成器是为了更高效地利用内存。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值