生成器对象(自定义迭代器),自定义range方法,模块

自定义迭代器

一 、生成器与yield

  '''
    我们得到一个迭代器通常都是调用可迭代对象的__iter__方法 ,例如 list.iter() 得到一个迭代器,
但是当list很大时候,就违背了python的初衷,假设我们要创造一个包含1000w个值的可迭代对象,总不可能先把
这1000w个值放入列表,然后调用__iter__() 方法吧,太占内存 ,必须得提供一种机制打破python内置的产生
迭代器的方式 ,所以自定义迭代器应运而生,即生成器。
在Python中, 一边循环一边计算的机制, 称为生成器:generator, 若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象
  '''

自定义range函数

  def my_range(start,stop,step=1):   # 自定义range函数
      print('start...')
      while start < stop:
          yield start
          start+=step
      print('end...')

  g=my_range(0,3)
  print(g)
  <generator object my_range at 0x01E2A808>
  如何得到自定义的迭代器:
  在函数内一旦存在yield关键字,调用函数并不会执行函数体代码
  会返回一个生成器对象,生成器即自定义的迭代器

  """
  如果函数体代码中含有多个yield关键字  执行一次__next__返回后面的值并且让代码停留在yield位置 
  再次执行__next__基于上次的位置继续往后执行到下一个yield关键字处
  如果没有了 再执行也会报错  StopIteration
  """

生成器本身就是一个迭代器

  print(g.__iter__) # <method-wrapper '__iter__' of generator object at 0x01AFB878>
  print(g.__next__) # <method-wrapper '__next__' of generator object at 0x01AFB878>
  '''生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器'''

用next(生成器)触发生成器所对应函数的执行

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

  既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:
  for i in g:
    print(i)
  执行结果:
  start...
  0
  1
  2
  end...

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

yield关键字作用

yield最简单的解释

  yield可以理解为一个return操作,但是和return又有很大的区别,执行完return,当前函数就终止了,
函数内部的所有数据,所占的内存空间,全部都没有了。而yield在返回数据的同时,还保存了当前的执行
内容,当你再一次调用这个函数时,他会找到你在此函数中的yield关键字,然后从yield的下一句开始执行。

例一

    def num_print():
        print('yield_1')
        yield 'yield_1 return'
        print('yield_2')
        yield 'yield_2 return'
        print('yield_3')
        yield 'yield_3 return'
        print('yield_4')
        yield 'yield_4 return'

    for i in num_print():
        print('i:',i)
    执行结果:
    yield_1
    i: yield_1 return
    yield_2
    i: yield_2 return
    yield_3
    i: yield_3 return
    yield_4
    i: yield_4 return
  '''
    上边在循环处一直在调用函数,可以看出每次调用函数,他会执行到yield进行返回,然后下一次调用时
他会从上一次执行yield的后边继续执行
  '''

例二(可以接收外界的传值(了解))

  def play():
    while True:
        game = yield
        print(f'玩{game}')
  res = play()
  res.__next__()
  res.send('LOL')
  执行结果:
  玩LOL

生成器表达式

语法

  (返回值 for 元素 in 可迭代对象 if 条件)
  返回一个生成器,生成器也是一个对象,属于中间值
  g = (i ** 2 for i in range(5)) # 将使用生成器表达式得到的生成器对象赋给一个变量g
  print(g)

  '''输出:
  <generator object <genexpr> at 0x109835b10>
  '''
  print(g.__next__())
  """生成器内部的代码只有在调用__next__迭代取值的时候才会执行"""
  主要功能是,代码优化,节省存储空间。

生成器表达式和列表生成式的区别

模块

简介

  在Python中有一个概念叫做模块(module),这个和C语言中的头文件以及Java中的包很类似,比如在
Python中要调用sqrt函数,必须用import关键字引入math这个模块
  说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块

模块的来源

  目前有3个来源
  1.内置的模块
  	无需下载 解释器自带  直接导入使用即可
  2.自定义模块
  	自己写的代码 封装成模块 自己用或者发布到网上供别人使用
  3.第三方模块
  	别人写的发布到网上的 可以下载使用的模块(很多牛逼的模块都是第三方)

导入模块的方法

第一种:import

    在Python中用关键字import来引入某个模块,比如要引用模块math,就可以在文件最开始的地方用
import math来引入。
    当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。

    在调用math模块中的函数时,必须这样引用:
    模块名.函数名
    这种方式必须加上模块名调用,因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如
果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用
函数必须加上模块名。
    
    有时候我们只需要用到模块中的某个函数,只需要引入该函数即可,此时可以用下面方法实现:
    from模块名import函数名1,函数名2....  
    '''
      通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同
名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也
有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时
候,是去执行模块B中的function函数。
    '''    
    如果想一次性引入math中所有的东西,还可以通过from math import *来实现

第二种:from…import

  Python的from语句让你从模块中导入一个指定的部分到当前命名空间中

  语法如下:

  from 模块名 import name,func1,func2(需要导入的函数或者变量)

  例如,要导入模块fib的fibonacci函数,使用如下语句:

  from fib import fibonacci

  注意:
  不会把整个fib模块导入到当前的命名空间中,它只会将fib里的fibonacci单个引入
  """
  1.重复导入也只会导入一次
  2.使用模块名称空间中的名字不需要加模块名前缀 直接使用即可
  3.但是from...import的句式会产生名字冲突的问题
      在使用的时候 一定要避免名字冲突
  4.使用from...import的句式 只能使用import后面出现的名字
      from...import...可以简单的翻译成中文
          从...里面拿...来用 没有提到的都不能用      指名道姓
  """

补充

  导入模块起别名,导入模块是对模块进行重命名,也就是给模块起一个别名。
  import 模块名 as mod

  '''
    在后续使用时就可以使用这个别名
    比如模块名或者变量名很复杂 可以起别名简写
  '''

  from 模块名 import *
  '''
  *表示md里面所有的名字   from...import的句  式也可以导入所有的名字
  如果模块文件中使用了__all__限制可以使用的名字  那么*号就会失效 依据__all__后面列举的名字
  '''

图文介绍两种导包

posted @ 2022-03-24 18:46  春游去动物园  阅读(125)  评论(0编辑  收藏  举报