模块包和异常
Posted mChenys
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模块包和异常相关的知识,希望对你有一定的参考价值。
目录
1.模块
模块是 Python 程序架构的一个核心概念
- 每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块
- 模块名 同样也是一个 标识符,需要符合标识符的命名规则
- 在模块中定义的 全局变量 、函数、类 都是提供给外界直接使用的 工具
- 模块 就好比是 工具包,要想使用这个工具包中的工具,就需要先 导入 这个模块
import 导入
import 模块名 是 一次性 把模块中 所有工具全部导入,并且通过 模块名/别名 访问
import 模块名1
import 模块名2
注意:在导入模块时,每个导入应该独占一行
导入之后 通过 模块名.
使用 模块提供的工具 —— 全局变量、函数、类。
如果模块名字太长还可以使用as
指定模块的别名, 例如:
import 模块名1 as 模块别名
from…import 导入
如果希望 从某一个模块 中,导入 部分 工具,就可以使用 from … import 的方式
# 从 模块 导入 某一个工具
from 模块名1 import 工具名
导入之后
- 不需要 通过 模块名.调用
- 可以直接使用 模块提供的工具 —— 全局变量、函数、类
注意:
如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数;
开发时 import 代码应该统一写在 代码的顶部,更容易及时发现冲突,一旦发现冲突,可以使用 as 关键字 给其中一个工具起一个别名;
不建议使用from…import *的方式导入某个模块的所有内容,因为函数重名并没有任何的提示,出现问题不好排查。
使用示例:
假设现在有2个模块, 分别是:
lz_01_测试模块1.py
# 全局变量
title = "模块1"
# 函数
def say_hello():
print("我是 %s" % title)
# 类
class Dog(object):
pass
lz_02_测试模块2.py
# 全局变量
title = "模块2"
# 函数
def say_hello():
print("我是 %s" % title)
# 类
class Cat(object):
pass
(1) import导包的使用方式如下:
import lz_01_测试模块1
import lz_02_测试模块2
lz_01_测试模块1.say_hello()
lz_02_测试模块2.say_hello()
dog = lz_01_测试模块1.Dog()
print(dog)
cat = lz_02_测试模块2.Cat()
print(cat)
(2) import as导包的使用方式如下:
import lz_01_测试模块1 as DogModule
import lz_02_测试模块2 as CatModule
DogModule.say_hello()
CatModule.say_hello()
dog = DogModule.Dog()
print(dog)
cat = CatModule.Cat()
print(cat)
(3) from…import导包的使用方式如下:
from lz_01_测试模块1 import Dog
from lz_02_测试模块2 import say_hello
# 直接使用导入后的方法
say_hello()
# 直接使用导入后的类
wangcai = Dog()
print(wangcai)
2. 模块的搜索顺序
Python 的解释器在 导入模块 时,会:
- 首先搜索 当前目录 指定模块名的文件,如果有就直接导入
- 如果没有,再搜索 系统目录
在开发时,给文件起名,不要和 系统的模块文件 重名;
Python 中每一个模块都有一个内置属性 file 可以 查看模块 的 完整路径, 例如:
print(DogModule.file), 输出结果:
/Users/chenyousheng/workspace/python/Learn/day01/lz_01_测试模块1.py
如下是一个导入系统random模块的用法
import random
# 生成一个 0~10 的数字
rand = random.randint(0, 10)
print(rand)
假设当前目录下,存在一个 random.py 的文件,程序就无法正常执行了!
因为, Python 的解释器会优先 加载当前目录 下的 random.py 而不会加载 系统的 random 模块
3. __name__属性的使用
首先明确下模块的导入原则:
- 一个 独立的 Python 文件 就是一个 模块
- 在导入文件时,文件中 所有没有任何缩进的代码 都会被执行一遍!
假设某个模块内定义了一些测试代码, 不想让别人导入的时候被执行了,只允许在当前文件中直接执行,那么就可以用到__name__属性了.
__name__
是 Python 的一个内置属性,记录着一个 字符串:
- 如果 是被其他文件导入的,
__name__
就是 模块名 - 如果 是当前执行的程序
__name__
是 main
例如: lz_xiaoming_模块.py定义如下:
# 输出当前执行模块的名字
# 如果直接执行模块,__main__
print("当前模块名字:%s" % __name__)
def say_hello():
print("这里是测试逻辑...")
if __name__ == "__main__":
# 文件被导入时,能够直接执行的代码不需要被执行!
print("小明写的测试代码")
say_hello()
直接运行该模块,输出结果如下:
当前模块名字:__main__
小明写的测试代码
这里是测试逻辑...
然后在 test.py中导入模块使用
import lz_xiaoming_模块
print("-" * 50)
输出结果如下:
当前模块名字:lz_xiaoming_模块
--------------------------------------------------
可以看到, 导入后运行,输出的结果和直接执行的结果是不一样的.
4. 包
包 是一个 包含多个模块 的 特殊目录,目录下有一个 特殊的文件 init.py,包名的 命名方式和变量名一致,小写字母 + _,使用 import 包名 可以一次性导入 包 中 所有的模块
包的使用步骤
- 新建一个 lz_message 的 包
- 在目录下,新建两个文件 send_message.py 和 receive_message.py
- 在 send_message 文件中定义一个 send 函数
- 在 receive_message 文件中定义一个 receive 函数
- 创建__init__.py声明对外导出的模块列表
- 在外部直接导入 lz_message 的包使用
目录结构如下:
send_message.py和receive_message.py定义如下:
# send_message.py
def send(text):
print("正在发送 %s..." % text)
# receive_message.py
def receive():
return "这是来自 100xx 的短信"
__init__.py
定义如下:
from . import send_message
from . import receive_message
使用方式如下:
# 导入包
import lz_message
# 使用 包名.模块名.调用模块的方法
lz_message.send_message.send("hello")
txt = lz_message.receive_message.receive()
print(txt)
输出结果:
正在发送 hello...
这是来自 100xx 的短信
5. 发布模块
如果希望自己开发的模块,分享 给其他人,可以按照以下步骤操作
以发布上面创建的lz_message包为例
(1) 创建 setup.py
首先需要在模块外创建setup.py文件,内容如下:
from distutils.core import setup
setup(name="lz_message", # 包名
version="1.0", # 版本
description="lz's 发送和接收消息模块", # 描述信息
long_description="完整的发送和接收消息模块", # 完整描述信息
author="mchenys", # 作者
author_email="mchenys@gmail.com", # 作者邮箱
url="lz.com", # 主页
py_modules=["lz_message.send_message",
"lz_message.receive_message"])
有关字典参数的详细信息,可以参阅官方网站:
(2) 构建模块
在控制台中切换到setup文件同级目录,我这里是pycharm项目的根目录,控制台输入如下命令:
python3 setup.py build
执行结果如下:
成功后会在当前目录下生成build目录
(3) 生成发布压缩包
控制台执行如下命令:
python3 setup.py sdist
执行结果如下:
此时根目录下会生成一个dist目录,里面有一个lz_message-1.0.tar.gz的压缩包
6. 安装模块
当你从别人那里得到一个已发布的模块压缩包后,我们就可以安装到python的内置目录内, 以上面发布的lz_message-1.0.tar.gz为例, 安装步骤如下:
假设我已经把lz_message-1.0.tar.gz复制到了桌面,那么需要在控制台上切换到~/Desktop目录
tar -zxvf lz_message-1.0.tar.gz
cd lz_message-1.0
sudo python3 setup.py install
执行结果如下:
可以看到安装的模块被导入到/opt/homebrew/lib/python3.9/site-packages目录内, 进入该目录也可以查看到新安装的模块
模块安装完后, 就可以直接在import来使用了.
7. 卸载模块
直接从安装目录下,把安装模块的 目录 删除就可以
cd /opt/homebrew/lib/python3.9/site-packages
sudo rm -r lz_message*
8. pip 安装第三方模块
pip 是一个通用的 Python 包管理工具,提供了对 Python 包的查找、下载、安装、卸载等功能
例如针对pygame 模块的安装和卸载命令如下:
# 将模块安装到 Python 2.x 环境
$ sudo pip install pygame
$ sudo pip uninstall pygame
# 将模块安装到 Python 3.x 环境
$ sudo pip3 install pygame
$ sudo pip3 uninstall pygame
安装效果:
如果在mac上提示sudo: pip: command not found, 按如下步骤进行安装:
- curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
- sudo python get-pip.py
- sudo easy_install pip
安装后通过pip --version 可以查看版本及安装目录
9. 异常处理
程序在运行时,如果 Python 解释器 遇到 到一个错误,会停止程序的执行,并且提示一些错误信息,这就是 异常。
程序停止执行并且提示错误信息 这个动作,我们通常称之为:抛出(raise)异常。
异常捕获
(1) 基本语法
如果 对某些代码的执行不能确定是否正确,可以增加 try(尝试) 来捕获异常, 语法格式如下:
try:
尝试执行的代码
except:
出现错误的处理
用法示例:
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except:
print("请输入正确的数字")
(2) 错误类型捕获
在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了,语法如下:
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except (错误类型2, 错误类型3):
# 针对错误类型2 和 3,对应的代码处理
pass
except Exception as result:
print("未知错误 %s" % result)
用法示例:
try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")
(3) 捕获未知错误
在开发时,要预判到所有可能出现的错误,还是有一定难度的,如果希望程序 无论出现任何错误,都不会因为 Python 解释器 抛出异常而被终止,可以再增加一个 except, 例如:
except Exception as result:
print("未知错误 %s" % result)
(4) 异常捕获完整语法
在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下:
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except 错误类型2:
# 针对错误类型2,对应的代码处理
pass
except (错误类型3, 错误类型4):
# 针对错误类型3 和 4,对应的代码处理
pass
except Exception as result:
# 打印错误信息
print(result)
else:
# 没有异常才会执行的代码
pass
finally:
# 无论是否有异常,都会执行的代码
print("无论是否有异常,都会执行的代码")
- else 只有在没有异常时才会执行的代码
- finally 无论是否有异常,都会执行的代码
用法示例:
try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")
except Exception as result:
print("未知错误 %s" % result)
else:
print("正常执行")
finally:
print("执行完成,但是不保证正确")
异常的传递
- 异常的传递 —— 当 函数/方法 执行 出现异常,会 将异常传递 给 函数/方法 的 调用一方
- 如果 传递到主程序,仍然 没有异常处理,程序才会被终止
def demo1():
return int(input("请输入一个整数:"))
def demo2():
return demo1()
try:
print(demo2())
except ValueError:
print("请输入正确的整数")
except Exception as result:
print("未知错误 %s" % result)
输出结果:
请输入一个整数:aaa
请输入正确的整数
抛出 raise 异常
在开发中,除了 代码执行出错 Python 解释器会 抛出 异常之外,还可以根据 应用程序 特有的业务需求 主动抛出异常。
Python 中提供了一个 Exception 异常类,在开发时,如果满足 特定业务需求时,希望抛出异常,可以这样处理:
- 创建 一个 Exception 的 对象
- 使用 raise 关键字 抛出 异常对象
用法示例:
def input_password():
# 1. 提示用户输入密码
pwd = input("请输入密码:")
# 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码
if len(pwd) >= 8:
return pwd
# 3. 密码长度不够,需要抛出异常
# 使用异常的错误信息字符串作为参数
raise Exception("密码长度不够")
try:
user_pwd = input_password()
print(user_pwd)
except Exception as result:
print("发现错误:%s" % result)
输出结果:
请输入密码:aaa
发现错误:密码长度不够
可以看到捕获的异常结果就是抛出异常的字符串内容。
自定义异常
只需要继承Exception基类,在构造方法__init__
中赋值message即可,例如:
class MyException(Exception):
def __init__(self, message):
self.message = message
def test(number):
if number == 100:
raise MyException("数字不能是100")
else:
return number
def main(number):
try:
return test(number)
except MyException as e:
return e
if __name__ == '__main__':
print(main(50))
print(main(100))
输出结果:
50
数字不能是100
断言
断言的关键字是assert, 作用是判断表达式是否相等, 不相等则抛出异常
用法: assert expression,[message]
参数:
expression是表达式, 一般是判断相等或者判断是某种数据类型的bool判断
message是具体的错误信息,可选 ,加上会根据直观的看到错误
if __name__ == '__main__':
assert 1 == 1
assert 1 > 2, "1不可能大于2"
输出结果:
Traceback (most recent call last):
File "/Users/chenyousheng/workspace/python/Learn/day01/main.py", line 281, in <module>
assert 1 > 2, "1不可能大于2"
AssertionError: 1不可能大于2
以上是关于模块包和异常的主要内容,如果未能解决你的问题,请参考以下文章
#yyds干货盘点#Python爬虫实战,pyecharts模块,Python实现中国地铁数据可视化