Loading

Python生成器及常用内置函数补充

一、生成器与yield

若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象:

自定义range功能:

>>> def my_range(start, stop=None, step=1):
...     print('start...')
...     if not stop:
...         stop = start
...         start = 0
...     while start < stop:
...         yield start
...         start += step
...     print('end...')
... 
>>> g = my_range(0,3)
>>> g
<generator object my_range at 0x104105678>

生成器内置有_ _ iter _ _ 和 _ _ next _ _方法,所有生成器本身就是一个迭代器:

>>> g.__iter__
<method-wrapper '__iter__' of generator object at 0x1037d2af0>
>>> g.__next__
<method-wrapper '__next__' of generator object at 0x1037d2af0>

因而我们可以用next(生成器)触发生成器所对应函数的执行:

>>> next(g)  # 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
start...
0
>>> next(g)  # 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
1
>>> next(g)  # 周而复始...
2
>>> next(g)  # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代
end...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:

>>> for i in my_range(4):
...     print(i)
... 
0
1
2
3

有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值;

yield与return对比如下:

yield:

  • 可以返回值(支持多个并且组织成元组形式);
  • 函数体代码遇到yield不会结束而是"停住"(挂起);
  • yield可以将函数变成生成器 并且还支持外界传值;

return:

  • 可以返回值(支持多个并且组织成元组形式);
  • 函数体代码遇到return直接结束;

666 抖音动图表情包_动图_抖音_666表情

二、yield表达式应用

在函数内可以采用表达式形式的yield:

>>> def eater():
...     print('Ready to eat')
...     while True:
...         food = yield
...         print('get the food: %s, and start to eat' % food)
... 

可以弄到函数的生成器对象持续为函数体send值,如下:

>>> g = eater()  # 得到生成器对象
>>> g
<generator object eater at 0x101b6e2b0>
>>> next(g)  # 需要事先”初始化”一次,让函数挂起在food = yield,等待调用g.send()方法为其传值
Ready to eat
>>> g.send('包子')
get the food: 包子, and start to eat
>>> g.send('鸡腿')
get the food: 鸡腿, and start to eat

针对表达式形式的yield,生成器对象必须事先先被初始化一次,让函数挂起在food=field的位置,等待调用g.send()方法为函数体传值,g.send(None)等同于next(g)。

我们可以编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作,如下:

def init(func):
    def wrapper(*args, **kwargs):
        g = func(*args, **kwargs)
        next(g)
        return g
    return wrapper
 
@init
def eater():
    print('Ready to eat')
    while True:
        food = yield
        print('get the food: %s, and start to eat' % food)

表达式形式的yield也可以用于返回多次值,即变量名=yield值的形式,如下:

>>> def eater():
...     print('Ready to eat')
...     food_list = []
...     while True:
...         food = yield food_list
...         food_list.append(food)
... 
>>> e = eater()
>>> next(e)
Ready to eat
[]
>>> e.send('蒸羊羔')
['蒸羊羔']
>>> e.send('蒸熊掌')
['蒸羊羔', '蒸熊掌']
>>> e.send('蒸鹿尾儿')
['蒸羊羔', '蒸熊掌', '蒸鹿尾儿']

熊猫头相机拍照动图表情包_动图_相机_熊猫_拍照表情

三、生成器表达式

创建一个生成器对象有两种方式,一种是调用带yield关键字的函数,另一种就是生成器表达式,与列表生成式的语法格式相同,只需要将[]换成(),即:

(expression for item in iterable if condition)

对比列表生成式返回的是一个列表,生成器表达式返回的是一个生成器对象:

>>> [x * x for x in range(3)]
[0, 1, 4]
>>> g = (x * x for x in range(3))
>>> g
<generator object <genexpr> at 0x101be0ba0>

对比列表生成式,生成器表达式的优点自然是节省内存(一次只产生一个值在内存中)

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)  # 抛出异常StopIteration

如果我们要读取一个大文件的字节数,应该基于生成器表达式的方式完成:

with open('db.txt','rb') as f:
    nums = (len(line) for line in f)
    total_size = sum(nums)  # 依次执行next(nums),然后累加到一起得到结果

ä¸è¦ç»™æˆ‘è¯´ä»€ä¹ˆåº•å±‚åŽŸç†ã€æ¡†æž¶å…§æ ¸ï¼è€å¤«æ•²ä»£ç å°±æ˜¯ä¸€æŠŠæ¢­ï¼å¤åˆ¶ï¼ç²˜è´´ï¼æ‹¿èµ°é”®ç›˜å°±æ˜¯å¹²ï¼

四、常用内置函数补充

最后,下面会补充介绍一些Python里的常用内置函数,这些都是Python提供给你直接可以拿来使用的,之前有些我们已经用过了,有些我们还没用到过,还有一些是被封印了,需要等我们学了新知识才能解开封印的,这里先介绍一些我们现阶段可以掌握的内置函数。

  1. abs():绝对值

    >>> abs(123)
    123
    >>> abs(-123)
    123
    
  2. all()和any():

    lis = [11, 22, 33, 0]
    print(all(lis))  # 所有的元素都为True结果才是True
    print(any(lis))  # 所有的元素只要有一个为True结果就为True
    

    结果为:

    False
    True
    
  3. bin():二进制转换 oct():八进制转换 hex(): 十六进制转换

    print(bin(123))
    print(oct(123))
    print(hex(123))
    

    结果为:

    0b1111011
    0o173
    0x7b
    
  4. bytes()和str():

    这两个内置函数为我们提供了另外一种字符的编码与解码的方式:

    res = 'jason 最牛逼'
    res1 = bytes(res, 'utf8')
    print(res1)
    res2 = str(res1, 'utf8')
    print(res2)
    

    结果为:

    b'jason \xe6\x9c\x80\xe7\x89\x9b\xe9\x80\xbc'
    jason 最牛逼
    
  5. callable():判断是否可调用(能不能加括号运行)

    s1 = 'jason'
    def index():
        pass
    
    print(callable(s1))
    print(callable(index))
    

    结果为:

    False
    True
    
  6. chr()和ord():

    print(chr(65))  # 根据ASCII码将数字转成字符
    print(ord('Z'))  # 根据ASCII码将字符转成数字
    

    结果为:

    A
    90
    
  7. complex():用于创建一个值为 real + imag * j 的复数或者转化一个字符串或数为复数。

    >>> complex(1, 2)
    (1 + 2j)
     
    >>> complex(1)  
    (1 + 0j)
    
  8. dir():不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。

    >>> dir()   #  获得当前模块的属性列表
    ['__builtins__', '__doc__', '__name__', '__package__', 'arr', 'myslice']
    >>> dir([ ])    # 查看列表的方法
    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
    >>>
    
  9. divmod():接收两个数字类型(非复数)参数,返回一个包含商和余数的元组(a // b, a % b)。

    >>> divmod(7, 2)
    (3, 1)
    >>> divmod(8, 2)
    (4, 0)
    
  10. eval():用来执行一个字符串表达式,并返回表达式的值。只能识别简单的语法 exec():执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。

    >>> x = 7
    >>> eval( '3 * x' )
    21
    >>> eval('pow(2, 2)')
    4
    
    >>> exec ("""for i in range(5):
    ...     print ("iter time: %d" % i)
    ... """)
    iter time: 0
    iter time: 1
    iter time: 2
    iter time: 3
    iter time: 4
    
  11. isinstance():判断是否属于某个数据类型:

    print(isinstance(123, float))  
    print(isinstance(123, int))  
    

    结果为:

    False
    True
    
  12. pow():返回 (x的y次方) 的值。

    >>> print(pow(4, 3))
    64
    
  13. round():该方法返回浮点数 x 的四舍五入值,准确的说保留值将保留到离上一位更近的一端(四舍六入)。

    第二个参数表示小数点位数,默认值为 0。

    print ("round(70.23456) : ", round(70.23456))
    print ("round(56.659,1) : ", round(56.659,1))
    print ("round(80.264, 2) : ", round(80.264, 2))
    print ("round(100.000056, 3) : ", round(100.000056, 3))
    print ("round(-100.000056, 3) : ", round(-100.000056, 3))
    

    以上实例运行后输出结果为:

    round(70.23456) :  70
    round(56.659,1) :  56.7
    round(80.264, 2) :  80.26
    round(100.000056, 3) :  100.0
    round(-100.000056, 3) :  -100.0
    

    看下面的一个例子:

    >>> round(2.675, 2) 
    2.67
    

    按我们的想法返回结果应该是2.68,可结果却是2.67,为什么?

    这跟浮点数的精度有关。我们知道在机器中浮点数不一定能精确表达,因为换算成一串 1 和 0 后可能是无限位数的,机器已经做出了截断处理。那么在机器中保存的2.675这个数字就比实际数字要小那么一点点。这一点点就导致了它离 2.67 要更近一点点,所以保留两位小数时就近似到了 2.67。

  14. sum():对序列进行求和计算。第二个参数为指定相加的参数,如果没有设置这个值,默认为0。

    >>> sum([0, 1, 2])  
    3  
    >>> sum((2, 3, 4), 1)        # 元组计算总和后再加 1
    10
    >>> sum([0, 1, 2, 3, 4], 2)      # 列表计算总和后再加 2
    12
    

    昨日收入:0每天稳定今日收入:0的收入才是我装逼的动力承让承让_动力_每天_承让_装逼_收入表情

posted @ 2021-11-22 22:04  JZEason  阅读(62)  评论(0编辑  收藏  举报