迭代器与生成器
1、迭代器
迭代是 Python 中访问集合元素的一种方式。,而迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器:
实例输出结果为:
注:超出所有数据项,故再次运行next(it)会报错
(1)使用内置函数进行迭代(iter() )
迭代器对象可以使用常规for语句进行遍历,也可以使用 next() 函数
1)常规for语句进行遍历
实例输出结果为:
2)使用 next() 函数
实例输出结果为:
3)推导式生成迭代器
python推导式是一种独特的数据处理方式,是可以从一个数据序列构建成另一个新的数据序列的结构体,一般常用的是:列表推导式和元组推导式,列表推导式是生成列表元素的而元组推导式生成的是迭代器。
实例输出结果为:
(2)自定义迭代器
利用类创建一个迭代器,在新的迭代器中需要使用两个魔法方法 __iter__() 与 __next__() ,__iter__() 方法在迭代开始的时候运行一次接下来就是循环调用 __next__ 直到遇到 raise StopIteration 为止(即迭代器数据项已耗尽)。
实例输出结果为:
iterator- 迭代器解释(来源网络)
iterator- 迭代器用来表示一连串数据流的对象。重复调用迭代器的next ()方法(或将其传给内置函数 next())将逐个返回流中的项。当没有数据可用时则将引发 StopIteration 异常。到这时迭代器对象中的数据项已耗尽,继续调用其 next()方法只会再次引发 stopIteration 异常。迭代器必须具有 iter ()方法用来返回该迭代器对象自身,因此迭代器必定也是可迭代对象,可被用于其他可选代对象适用的大部分场合。一个显著的例外是那些会多次重复访问迭代项的代码。容器对象(例如 list)在你每次向其传入iter()函数或是在 for 循环中使用它时都会产生一个新的迭代器。如果在此情况下你尝试用迭代器则会返回在之前迭代过程中被耗尽的同一迭代器对象,使其看起来就像是一个空容器。
(3)StopIteration
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
2、生成器
(1)概念:在 Python 中,使用了 yield 的函数被称为生成器(generator)
(2)生成器函数
1)使用 yield
实例输出结果为:
2)不使用 yield
实例输出结果为:
注:不使用yield,函数只是简单执行,没有返回迭代器。
3、迭代器与生成器的区别
(1)迭代器
迭代器的实现是基于类, 要使得类的实例是一个生成器, 需要满足2个条件:
①定义一个__iter__方法, 内容要 return self
②定义一个__next__方法(Python 2中 是 next 方法)
可以使用关键字iter() 将一个可迭代对象转换成迭代器.
(2)生成器
生成器可以认为是一个简化版的迭代器, 生成器的实现是基于函数。在函数中使用关键字“yield”。可以在函数中使用 while True语句, 使其成为永远不会结束的生成器。
(3)结论
① 所有的生成器都是迭代器.
② 迭代器(iterator)用类实现, 生成器(generator)用函数实现
迭代器与生成器对比
迭代器 Iterator | 生成器 Generator |
用类实现 | 用函数实现 |
迭代器使用 iter() 和 next() 函数 | 生成器使用 yield 关键字 |
每个生成器都是一个迭代器 |
装饰器
1、装饰器概念
装饰器:指增加一些额外的功能或者给代码的外表添加一些装饰文本。即从生活中的语文上面来说就是形容词的意思,在没改变语句的本质含义的情况下,去添加一些美化词语。
2、装饰器的作用
可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器本质上是一个闭包函数。
注1:闭包函数指在外部函数里面定义一个内部函数,并且这个内部函数中用到了外部函数的非全局变量,那么将这个内部函数和用到的变量统称为闭包。
注2:闭包的构成条件:①在函数中嵌套一个内函数;②内层嵌套函数中必须要使用一个外部的非全局变量;③在外层函数中必须写一个return关键字,并且该关键字的值是内层函数的函数名。
3、装饰器的装饰过程
装饰器的装饰过程是通过传参的方式装饰,即是把真正要执行的业务函数(被装饰)的函数名作为参数传到装饰器函数里面去。
4、装饰器的写法
(1)直接在装饰器函数的调用语句】里面将业务函数的名字写成实参传给装饰器函数来调用装饰。
①没有装饰函数时:
实例输出结果为:
②增加装饰函数时:
实例输出结果为:
这里的装饰函数实现了(功能):
a、在用户进游戏登录玩耍之前,加一句欢迎词和提示语:“欢迎来到做饭小游戏,请先注册,再登录游戏!!!”
b、在用户注册完成之后再加一句提示语:“您已经注册成功,可以去进行登录了。”'
(2)通过语法@decorator_name 来进行装饰。
实例输出结果为:
注1:使用 @decorator_name 前缀在 自定义函数 定义前,Python会自动将 target_function 作为参数传递给 装饰函数,然后将返回的值输出。
(3)装饰器函数也可以接受参数
实例输出结果为:
以上代码中 repeat 函数是一个带参数的装饰器,它接受一个整数参数 n,即在原有的函数外再次嵌套一层函数。
- 类装饰器
类装饰器是包含 __call__ 方法的类,它接受一个函数作为参数,并返回一个新的函数
实例输出结果为:
异常
1、错误类型
在python中错误一般分成两类:
(1)语法错误:一般是当代码不符合python的语法规则时,python解释器在解析时就会报出SyntaxError;
语法错误可以理解为是代码的构成或格式有问题,如2a = 1(这里是标识符(变量名)不能以数字开头,触发了命名语法规则,所以弹出弹窗是SyntaxError错误)等。
(2)运行时错误(逻辑错误):即程序在语法上都是正确的,但在运行时发生了错误,在python中,一般把这种运行时产生错误的情况叫做异常(Exceptions)。
异常是多种情况的,所以有多种单词去代表不同的错误,如NameError(使用一个还未被赋予对象的变量,即变量未被定义),TypeError (传入对象类型与要求的不符合)等。
2、异常处理
当发生异常报错时,如果不提前去进行某种处理,那么在发生的时候就会导致整个程序(系统)崩溃(后面的代码不能运行),如果不想出现这种程序崩溃,导致后面不能运行的状况时,python中提供了一种专门根据异常做出某种特定的逻辑处理,一般将这种处理叫做异常处理,这个处理是有特定的语句去进行的
(1)异常处理语句
异常处理语句是一个捕获异常的过程语句,大致分成三步:怀疑(猜测)、蹲点(给出位置)、抓捕(处理);异常处理语句(try语句)分成三大类:
1)try-except语句
异常捕捉通常使用 try-except 语句。
语法格式
实例输出结果为:
注1:当不知道某个代码当中到底是哪种异常情况时,可以使用Exception这个单词来表示任意异常,然后使用repr()函数来查看异常信息,然后用print()打印出来看即可知晓。
实例输出结果为:
注1:上面代码的as e:是给异常类型取别名(相当于变量赋值,方便后续使用),e是行业内的习惯称呼,可以自定义
注2:一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行,最后一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。
实例输出结果为:
注3:一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。
实例输出结果为:
2)try-except-else语句
try-except-else语句中,else子句必须放在所有的 except 子句之后。else 子句将在 try 子句没有发生任何异常的时候执行。
语法格式
实例输出结果为:
注1:使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。
注2:异常处理并不仅仅处理那些直接发生在 try 子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常。
实例输出结果为:
3)try-(except-else)-finally语句。
try-(except-else)-finally 语句无论是否发生异常都将执行最后的代码。
语法格式
实例输出结果为:
3、抛出异常
Python 使用 raise 语句抛出一个指定的异常。
语法格式
一般自定义异常都是结合if判断来实现:当某种情况下会报错
实例输出结果为:
注1:如果你写一个raise语句只是想要给你的程序制定一个警告规则,但是页面不报错,所以就可以结合try异常处理语句一起使用。
实例输出结果为:
4、用户自定义异常
用户通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类,可以直接继承,或者间接继承
实例输出结果为:
注1:大多数的异常的名字都以"Error"结尾,就跟标准的异常命名一样。
断言
Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
断言一般用来进行测试bug,是使用assert关键字来判断bug,可以把断言看成是raise的简化版本。
语法格式
实例输出结果为:
等价于:
实例输出结果为:
模块
通常,函数、变量、类存储在被称为模块的.py文件中,一组模块文件又组成了包。包和模块文件,可以在其他不同程序中进行复用,还可以使用其他人开发的第三方依赖库。
1、模块
模块是包含 Python 定义和语句的文件。以.py为后缀的文件名就是模块名称。
注1:这里fibo.py就是一个模块,fib、fib2是fibo模块中的函数。
(1)导入模块方式
① 导入整个模块(import module_name)
实例输出结果为:
②导入模块中的特定函数(from module_name import function_name)
实例输出结果为:
注1:若使用这种语法,调用函数时就无需使用模块名+句点。因为在import语句中已经显式地导入了函数fib和fib2,因此调用它时只需指定其名称。
③导入模块中的所有函数(from module_name import *)
实例输出结果为:
注1:这种方式会导入除可下划线 (__)开头的名称以外的所有函数。
:④给导入的模块一个别名(import module as m)
利用as给导入模块一个别名,简化代码中的调用写法。
(2)单独运行模块
如果我们想单独测试下模块,可以在模块中使用__name__变量和__main__变量,
当我们运行别的文件的时候,__name__是不会被设置成__main__的,这样会让我们在别的文件里面,对刚刚的模块进行导包时,测试语句不被输出。
(3)加速模块加载
为了加快模块的加载速度,Python 会将每个模块的编译版本(如*.pyc)会缓存在__pycache__下的目录中。生成编译文件pyc的详细过程
(4)* 和__all__
①* 导入:
在Python中,使用 from module_name import * 表示导入模块中的所有内容。
这会导入模块中的所有变量、函数和类,我们可以在当前命名空间中直接使用它们。
注1:这种方法可以快速地导入模块中的所有内容,但可能导致命名冲突,因此通常不推荐使用。
②__all__:
__all__ 是模块中的一个特殊变量,它定义了模块中应该被导入的内容。
当使用 from module_name import * 时,只有 __all__ 指定的内容会被导入。(即__all__ 限制了 * 导入的行为)
这时候我们在其他代码中采用from module_name import *导入时,使用fib2就会报错,因为__all__变量限制了*的导入
2、包
包可以理解为一组模块的容器,并用(Package.Module)的方式来构建命名空间以文件系统来类比的话,可以将包视为文件系统上的目录(文件夹),而将模块视为目录(文件夹)中的文件。包的本质上是一个包含了一个或多个模块的文件夹,其中还包含一个特殊的文件 __init__.py(双下划线开头和结尾)。
利用这样的方法,可以避免一些多模块的包之间命名发生冲突的问题。
假设创建一个名为 my_package 的包,文件系统目录结构如下:
注1:my_package 是包的根文件夹。
注2:__init__.py 是一个空文件,它告诉 Python 解释器这是一个包。
注3:module1.py、module2.py 等是包中的模块文件。
注4:subpackage 是一个子包,也是一个包含 __init__.py 的文件夹。
注5:submodule1.py、submodule2.py 等是 subpackage 中的模块文件。
(1)引用包(Package)中的模块
使用点号(.)来表示包的层级关系。
①直接导入包中的模块可采用
②导入的模块起个别名,也可以用as
③使用 from ... import 来导入模块中的函数、类或变量
(2)利用相对路径引用包和模块
相对导入是相对于当前模块的路径来导入其他模块。它使用点号 . 来表示相对路径
(3)包的初始化和清理(来源网络)
__init__.py 文件在包的结构中具有特殊的作用。当 Python 导入一个包时,它会自动执行包中的 __init__.py 文件。这个文件可以包含包的初始化代码,以及定义包的公开接口(可供外部使用的模块、函数、类等)。
包的初始化:__init__.py 可以用于在包被导入时执行一些初始化工作。这可能包括设置包级别的变量、加载子模块或子包,或执行其他初始化操作。比如说,在导入包时加载一些资源或配置信息。
定义包的公开接口:另一个常见的用法是通过 __init__.py 定义包的公开接口,即定义哪些模块、函数、类是包的外部接口,可以被其他模块导入和使用。
在 __init__.py 中,可以使用特殊的变量 __all__ 来明确指定包的公开接口。这个变量是一个包含字符串的列表,指定了哪些模块应该在使用 from package import * 时被导入。
(4)常见的第三方包管理工具
①pip
pip 是 Python 的包管理工具,它允许你从PyPI(Python Package Index)安装和管理包。
一些常用的 pip 命令包括:
②virtualenv 和 venv
virtualenv 和 Python 3.3+ 中内置的 venv 是用于创建和管理独立 Python 环境的工具。它们允许你在同一台计算机上拥有多个独立的 Python 环境,每个环境可以有自己的包和依赖。
一些常用的 venv 命令包括:
(5)如何发布自己的包(了解)
如果你编写了自己的 Python 包,并希望与其他人分享,你可以将它发布到 PyPI(Python Package Index)。以下是发布自己的包的基本步骤:
①编写脚本文件。
②构建包:在包含脚本文件的目录下运行命令:python 脚本文件名.py sdist bdist_wheel。
③注册 PyPI 账户:在 PyPI 上注册一个账户。
④上传包:使用 twine 工具上传包到 PyPI(确保已安装 twine),然后运行命令:twine upload dist/*。
输入 PyPI 账户信息后,twine 将会上传你的包到 PyPI。