• python之序列去重以及生成器、生成器函数、生成器表达式与迭代器浅谈


    首先要明确序列值类型是否可哈希,因为可哈希的值很简单就可以用 in /not in 写个生成器去判断,如果是不可哈希的就要去转换为可哈希的再用 in/not in 去判断

    原地不可变类型(可哈希):

    • 数字类型:int, float, decimal.Decimal, fractions.Fraction, complex
    • 字符串类型:str, bytes
    • tuple
    • frozenset
    • 布尔类型:True, False
    • None

    原地可变类型(不可哈希):

    • list
    • dict
    • set
    举例可哈希序列去重
    b=[1,2,3,1,2,5]
    def debque1(items):
        result=set()
        for item in items:
            if item not in result:
                yield item
                result.add(item)
    
    print(list(debque1(b))) #[1, 2, 3, 5]
    举例不可哈希序列去重
    a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
    def debque2(items,key=None):
        result=set()
        for item in items:
            val=item if key is None else key(item)
            if val not in result:
                yield item
                result.add(val)
    
    print(list(debque2(a,key=lambda item:(item["x"],item["y"]))))   # [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]

    这方法利用了生成器,科普一下生成器

    如果列表元素可以按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间,所以zai在Python中,就出现了这种一边循环一边计算的机制,称为生成器:generator

    生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

    要创建一个generator,有很多种方法,第一种方法很简单,只有把一个列表生成式的[]中括号改为()小括号,就创建一个generator

    # 列表生成式
    lis = [x*x for x in range(10)]
    print(lis)
    # 生成器
    generator_ex = (x*x for x in range(10))
    print(generator_ex)
     
    # 结果:
    # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    # <generator object <genexpr> at 0x000002A4CBF9EBA0>

    如果要一个个打印出来,可以通过next()函数获得generator的下一个返回值:

    但是一般不这么做,正确的方法是使用for循环,因为generator也是可迭代对象

      生成器函数:也是用def定义的,利用关键字yield一次性返回一个结果,阻塞,重新开始

       生成器表达式:返回一个对象,这个对象只有在需要的时候才产生结果

    ——生成器函数

    为什么叫生成器函数?因为它随着时间的推移生成了一个数值队列。一般的函数在执行完毕之后会返回一个值然后退出,但是生成器函数会自动挂起,然后重新拾起急需执行,他会利用yield关键字关起函数,给调用者返回一个值,同时保留了当前的足够多的状态,可以使函数继续执行,生成器和迭代协议是密切相关的,可迭代的对象都有一个__next__()__成员方法,这个方法要么返回迭代的下一项,要买引起异常结束迭代。

    # 函数有了yield之后,函数名+()就变成了生成器
    # return在生成器中代表生成器的中止,直接报错
    # next的作用是唤醒并继续执行
    # send的作用是唤醒并继续执行,发送一个信息到生成器内部
    '''生成器'''
     
    def create_counter(n):
        print("create_counter")
        while True:
            yield n
            print("increment n")
            n +=1
     
    gen = create_counter(2)
    print(gen)
    print(next(gen))
    print(next(gen))
     
    结果:
    <generator object create_counter at 0x0000023A1694A938>
    create_counter
    2
    increment n
    3
    Process finished with exit code 0

     yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行

    ——生成器表达式

    生成器表达式来源于迭代和列表解析的组合,生成器和列表解析类似,但是它使用尖括号而不是方括号

    >>> # 列表解析生成列表
    >>> [ x ** 3 for x in range(5)]
    [0, 1, 8, 27, 64]
    >>>
    >>> # 生成器表达式
    >>> (x ** 3 for x in range(5))
    <generator object <genexpr> at 0x000000000315F678>
    >>> # 两者之间转换
    >>> list(x ** 3 for x in range(5))
    [0, 1, 8, 27, 64]

    --迭代器(迭代就是循环)

    可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

    可迭代对象有:

    一类是集合数据类型,如list,tuple,dict,set,str等

    一类是generator,包括生成器和带yield的generator function

    这些可以直接作用于for 循环的对象统称为可迭代对象:Iterable

    但是必须可以被next() 函数调用病不断返回下一个值! 的  可迭代对象,才是迭代器

    # 判断下列数据类型是可迭代对象or迭代器
    s='hello'     #字符串是可迭代对象,但不是迭代器
    l=[1,2,3,4]     #列表是可迭代对象,但不是迭代器
    t=(1,2,3)       #元组是可迭代对象,但不是迭代器
    d={'a':1}        #字典是可迭代对象,但不是迭代器
    set={1,2,3}     #集合是可迭代对象,但不是迭代器
    # *************************************
    f=open('test.txt') #文件是可迭代对象,是迭代器

    可以用isinstance 判断

    print(isinstance(s,Iterator))     #判断是不是迭代器
    print(isinstance(s,Iterable))       #判断是不是可迭代对象

    listdictstrIterable变成Iterator可以使用iter()函数

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True

    Python3的for循环本质上就是通过不断调用next()函数实现的,例如:

    for x in [1, 2, 3, 4, 5]:
        pass

    实际上完全等价于

    # 首先获得Iterator对象:
    it = iter([1, 2, 3, 4, 5])
    # 循环:
    while True:
        try:
            # 获得下一个值:
            x = next(it)
        except StopIteration:
            # 遇到StopIteration就退出循环
            break

    感谢:https://www.cnblogs.com/wj-1314/p/8490822.html

     
  • 相关阅读:
    .NET C# Tostring() format 格式化字符串大全
    国美秒杀器分享帐号
    .NET 调用JS:WebBrowser.Document.InvokeScript 方法抛出“指定的转换无效”异常的原因
    非接触式IC卡性能简介(M1)
    使用Tesseract (OCR)实现简单的验证码识别(C#)+窗体淡入淡出效果
    C# .NET entity下sqlite部署问题
    sqlite3自增key设定(创建自增字段)
    .NET在winform中使用UrlEncode进行字符串编码字母大写
    HttpWebRequest 二三事
    SQLite在字符串比较中的大小写问题
  • 原文地址:https://www.cnblogs.com/zzy-9318/p/10414367.html
Copyright © 2020-2023  润新知