异常
什么是异常
程序发生错误,既程序非正常终止,则为异常。异常会让程序终止,这样的程序很不友好,我们希望程序异常结束时能给出提示。
1/0
# 结果为:
# Traceback (most recent call last):
# File "E:/workspace/pycharm/pycharm/Demo.py", line 1, in <module>
# 1/0
# ZeroDivisionError: division by zero
捕获异常
try ... except ... 用于捕获异常,处理异常,程序在一次运行中只会产生一个异常。
格式
try:
pass
except:
pass
一个try 可以配合多个except 使用,每一个except 认为是一个分支。
try:
1 / 0
except:
print("发生异常程序,即将退出")
# 结果为:
# 发生异常程序,即将退出
捕获异常,作出提示,程序的友好性有所提升。
捕获异常的方式
- 单分支捕获指定异常
- 单分支捕获指定多个异常
- 多分支捕获异常
- 单分支捕获常见异常
捕获指定异常
捕获指定异常(一个分支捕获一个异常)
try:
1 / 0
except ZeroDivisionError:
print("被除数不能为 0")
# 结果为:
# 被除数不能为 0
如果需要获得异常的一些信息怎么办?使用 as 为异常对象起一个别名
try:
1 / 0
except ZeroDivisionError as exc:
print("被除数不能为 0 ,"
"异常信息:%s ,"
"异常代码所在的行数:%d" % (exc, exc.__traceback__.tb_lineno))
# 结果为:
# 被除数不能为 0 ,异常信息:division by zero ,异常代码所在的行数:2
单分支指定捕获多个异常
当多个异常处理逻辑相同时,可以放在一个分支中进行处理。
try:
# max() # TypeError
# max([]) # ValueError
pass
except (TypeError, ValueError) as exc:
print("异常信息:%s ,"
"异常代码的行数:%d" % (exc, exc.__traceback__.tb_lineno))
通过元组的形式可以在一个分支指定捕获多个异常,但是每次只有一个异常被捕获处理,为啥?因为一次只会产生一个异常。
多分支捕获异常
try:
max() # TypeError
# max([]) # ValueError
except ValueError as exc:
print("异常信息:%s ,"
"异常代码的行数:%d" % (exc, exc.__traceback__.tb_lineno))
except TypeError as exc:
print("异常信息:%s ,"
"异常代码的行数:%d" % (exc, exc.__traceback__.tb_lineno))
单分支捕获常见异常
try:
max() # TypeError
# max([]) # ValueError
except Exception as exc: # 单分支捕获常见异常
print("异常信息:%s ,"
"异常代码的行数:%d" % (exc, exc.__traceback__.tb_lineno))
# Exception 的直接子类
# ArithmeticError(Exception)
# AssertionError(Exception)
# AttributeError(Exception)
# WindowsError(Exception)
# BufferError(Exception)
# Warning(Exception)
# EOFError(Exception)
# ImportError(Exception)
# SyntaxError(Exception)
# LookupError(Exception)
# MemoryError(Exception)
# NameError(Exception)
# RuntimeError(Exception)
# ReferenceError(Exception)
# StopAsyncIteration(Exception)
# StopIteration(Exception)
# SystemError(Exception)
# TypeError(Exception)
# ValueError(Exception)
# 19 个
异常的传递
def func1():
1 / 0
def func2():
func1()
def func3():
func2()
func3()
# 结果为:
# Traceback (most recent call last):
# File "E:/workspace/pycharm/pycharm/Demo.py", line 10, in <module>
# func3()
# File "E:/workspace/pycharm/pycharm/Demo.py", line 8, in func3
# func2()
# File "E:/workspace/pycharm/pycharm/Demo.py", line 5, in func2
# func1()
# File "E:/workspace/pycharm/pycharm/Demo.py", line 2, in func1
# 1 / 0
# ZeroDivisionError: division by zero
通过错误日志可以看出来在第十行调用了func3 ,func3 调用了 func2 ,func2 调用了func1 ,在行2 也就是func1 里面出错,没有处理错误,错误向上传递到func2 ,再传给func3 ,直到func3 的调用处都没有处理异常,程序异常终止。
自定义异常
class UserDefinedException(Exception): # 继承Exception类
'''自定义的异常类'''
def __init__(self):
pass
raise UserDefinedException() # 使用raise 抛出一个UserDefinedException 异常
模块
什么是模块
一个 .py 文件就是一个模块,作用:分工,一个人负责某几个模块。
模块的导入
什么是模块名
前面已经说了一个 .py 文件就是一个模块,所以模块名就是 .py 文件的文件名。文件名将会用于标识符,所以文件名必须满足标识符的命名规则,否则不能导入。
导入
- import 模块名
- form 模块名 import 功能
模块 piece.py 的内容
def func():
print("------ piece func -----------")
使用import 导入
import piece # impotent 模块名,直接找人帮忙
piece.func() # 模块名.功能名 使用,功能包括模块中的方法、变量、类等都可以使用。
# 结果为:
# ------ piece func -----------
# as 定义别名,结合import 使用
import piece as p # 使用as 给模块名 定义别名
p.func() # 别名.功能名
# 结果为:
# ------ piece func -----------
import 导入可以理解为交了一个朋友(导入模块),比如说你的朋友会做做菜(模块中的功能),如果你需要做菜(想使用模块中的功能),你得先找到你的朋友(通过模块名),然后让他帮你做菜(调用功能)
form 模块名 import 功能
from piece import func # form 模块名 import 功能,只能导入指定的功能
func() # 直接使用
# 结果为:
# ------ piece func -----------
# as 结合 form import 使用
# form 模块名 import 功能 as 别名,功能包括模块中的方法、变量、类等都可以使用。
from piece import func as f
f() # 直接使用
# 结果为:
# ------ piece func -----------
form import 的形式:还是用做菜来说吧,你有一个朋友会做菜(模块名),一顿烧烤,你从他那里学会了几道菜(功能),你学会了之后,想做菜了,还需要找朋友来帮忙吗?!你要是觉得别人帮你理所应当我也没法!!!继续,你都会了,当然是自己下厨了(使用功能)。
from 模块名 import *
导入模块中的所有公开部分
from piece import * # from 模块名import 星号
func() # 直接使用
看上面的代码和导入指定的功能没什么区别,那为什么说 星号 是导入所有公开部分和import 模块有什么区别呢?
首先,说一下from 模块名 import * 怎么理解,可以理解为你的朋友会做菜,会修车等等,你一顿烧烤学会做菜(公共的功能),但是他没教你修车(公共功能,但是我不想教你),所以你只会做菜(只能使用做菜,不能使用修车)。
通过上面的叙述应该可以明白 星号 是导入所有公开部分和import 模块的区别。区别在于找朋友帮忙,只要朋友会他就能帮你做(import)。但是你从朋友那里学 (from import *),他不教,你就不会。
那么有一个问题,星号是怎么知道你的朋友有哪些是想教给你,哪些不想的呢?__all__ 变量
__all__ 变量
通过前面可以知道__all__ 变量 的作用是控制 星号 可以导入的功能,也只对 星号 起作用。
piece.py
__all__ = ["func","func3"] # __all__ 变量,赋值列表,列表中的每个元素都是功能名称的字符串
def func():
print("------ piece func -----------")
def func2():
print("------ piece func -----------")
def func3():
print("------ piece func -----------")
使用 星号 导入
from piece import *
func()
func3()
func2()
# 结果为:
# ------ piece func -----------
# Traceback (most recent call last):
# ------ piece func3 -----------
# File "E:/workspace/pycharm/pycharm/Demo.py", line 7, in <module>
# func2()
# NameError: name 'func2' is not defined
# 结果分析:
# 通过结果可以看出,__all__ 变量中的功能都正常运行,不在__all__ 中的功能不能使用
模块与路径
导入模块,会了,那么导入模块是根据什么导入的呢?比如说系统有个random 模块,我的工程目录下有一个random 模块,是导入我工程目录下的random ,还是 系统中的random ?往下看
import piece
import sys
import os
print(piece.__file__) # 模块有一个__file__属性,可以查看当前导入的模块的存放路径
print(os.getcwd()) # 获取当前工作路径
print(sys.path) # 获取系统搜索导入模块会查看的路径
# 结果为:
# E:\workspace\pycharm\pycharm\piece.py
# E:\workspace\pycharm\pycharm
# ['E:\\workspace\\pycharm\\pycharm',
# 'E:\\workspace\\pycharm\\pycharm', 'D:\\software\\Python36\\python36.zip',
# 'D:\\software\\Python36\\DLLs', 'D:\\software\\Python36\\lib',
# 'D:\\software\\Python36', 'D:\\software\\Python36\\lib\\site-packages']
# 结果分析:
# sys.path 搜索路径是一个列表,列表可以增删改查,列表的第一个元素是当前的工作路径,
# 所以当工作路径下有一个和系统模块重名的模块时,会导入工作路径下的模块。
# 问重名会不会有影响?首先不建议重名,那我怎么知道有没有重名?你不需要知道!为什么不需要知道?
# 首先你知道系统有random 模块,你才会用系统的,如果说系统没有这个模块或者你不知道有这个模块,
# 你还会用吗? 不会用吧?!那么在工作目录下创建一个random 模块来使用,有影响吗?
自定义模块与测试
自定义模块
通俗点说就是自己写一个 .py 的文件,文件中包含一些功能。
测试
在自己的模块中写测试代码,测试模块功能。
piece.py
def func():
print("------ piece func -----------")
print(" piece 的测试")
# 结果:
# piece 的测试 # 假设当前模块测试通过
piece.py 被当做模块导入
Demo.py 中导入piece 模块,直接运行Demo.py 文件
import piece
# 直接运行
# 结果为:
# piece 的测试
Demo.py文件中只有一句import ,为什么会有输出?输出的结果是不是有点眼熟?这不是模块中的测试代码吗?
怎么解决这个问题?__name__变量
修改模块piece
def func():
print("------ piece func -----------")
if __name__ == "__main__":
print(" piece 的测试")
# 运行结果为:
# __main__
# piece 的测试
修改Demo.py
# import piece
# import piece as p
# 分别以 import形式 以及 别名形式导入
# 运行结果:
# piece
# piece
# 分析在piece 中直接运行的结果与piece 被当做模块导入Demo 文件中运行的结果可以发现
# 在piece 直接piece 本身时,__name__的值为__main__,
# 当piece 被当做模块导入Demo 文件中,运行Demo 文件时,piece 的__name__值为模块名,且不受别名的影响。
import 没有继承
piece1.py
def func():
print("------ piece1 func -----------")
piece.py
import piece1
def func():
print("------ piece func -----------")
if __name__ == '__main__':
piece1.func()
Demo.py
import piece
if __name__ == '__main__':
piece.func()
piece1.func()
# 执行结果:
# ------ piece func -----------
# Traceback (most recent call last):
# File "E:/workspace/pycharm/pycharm/Demo.py", line 5, in <module>
# piece1.func()
# NameError: name 'piece1' is not defined
怎么理解呢?朋友(piece)的朋友(piece1)并不是你(Demo)的朋友
包
什么是包
包就是文件夹或者目录,作用:归类,将功能相类似或者相关联的模块放在同一包下,方便管理
在工程目录下新建文件夹,如果使用集成开发工具pytcharm 新建python package会自带__init__.py 文件,
这个文件有什么作用呢?
__init__.py文件
__init__.py 是包的一个初始化文件,可以提供一个__all__变量(和模块中的__all__变量类似),初始化模块所需环境,python2中必须有这个文件。
包名的格式
com.XXX1.XXX2.XXX3
. 的作用是分隔目录,如上有三个. 分隔了四层目录,com 下有XXX1,XXX1 下有XXX2 ,XXX2 下有XXX3
导入包中的模块
- import 包名.模块名
- from 包名.模块名 import 功能名
com.dragon_fang.demo 包下的模块 piece.py 的内容
def func():
print("------ piece func -----------")
使用import 导入
com.dragon_fang 包下的Demo.py
import demo.piece # impotent 包名.模块名,去找你的中国(包名)朋友(模块名)帮忙,导入指定的模块
demo.piece.func() # 包名.模块名.功能名 使用,功能包括模块中的方法、变量、类等都可以使用。
# 结果为:
# ------ piece func -----------
# as 定义别名,结合import 使用
import demo.piece as p # 使用as 定义别名,当包的层级比较深时,很好用
p.func() # 别名.功能名
# 结果为:
# ------ piece func -----------
form 模块名 import 功能
from demo import piece # form 包名 import 模块名 ,导入指定的模块
piece.func() # 直接使用
# 结果为:
# ------ piece func -----------
# as 结合 form import 使用
# from demo import piece as 别名,为指定的模块起一个别名
from demo import piece as p
p.func() # 别名.功能
# 结果为:
# ------ piece func -----------
from包名 import *
导入包中的所有公开部分
from demo import * # from 包名 import 星号
piece.func() # 模块.功能
导入包中的模块的 import 和 from import 与 导入模块的 import 和 from import 类似,不做赘述。
说一下 *(星号) 前面说过,包下有一个__init__.py 文件,文件中有一个__all__ 变量控制 *(星号) 可以导入包中的哪些模块,
所有在__all__ 变量列表中以字符串存储的功能名才能被 *(星号) 导入。
到此结 DragonFang 2018.4.16