python模块、异常、日志
1. python 模块
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。(有点像java的class文件,一个class文件包含多个方法)
一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。
当我们使用import语句的时候,Python解释器是怎样找到对应的文件的呢?这就涉及到Python的搜索路径,搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块。
例如:模块结构如下:
代码如下:
StringUtils.py
title = "字符串工具栏" def isBlank(str): if str is None or '' == str: return True; else: return False; def isNotBlank(str): return isBlank(str) == False;
NumberUtils.py
title = "数字工具栏" def add(*args): result = 0; for ele in args: result += ele; return result;
下面的测试代码都是基于ModuleTest.py源代码进行
1. import语句
想使用 Python 源文件,只需在另一个源文件里执行 import 语句,语法如下:
import module1[, module2[,... moduleN]
例如:
import cn.qlq.a.StringUtils as myStringUtils, cn.qlq.a.NumberUtils if __name__ == '__main__': print(myStringUtils.isNotBlank('')); print(myStringUtils.title); print(cn.qlq.a.NumberUtils.add(1, 2));
结果:
False
字符串工具栏
3
注意:
引入带包的模块的时候(比如上面的两个工具类),第一种方式用as起一个别名之后用别名访问(参考StringUtils);第二种是用包模块名全路径访问(参考NumberUtils)
不建议一行引入多个模块
2. from ... import ...语句
Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,语法如下:
from modname import name1[, name2[, ... nameN]]
例如:
import cn.qlq.a.NumberUtils from cn.qlq.a.StringUtils import title from cn.qlq.a.StringUtils import isBlank as isBlankFun if __name__ == '__main__': print(title); print(isBlankFun('2')); print(cn.qlq.a.NumberUtils.add(1, 2));
结果:
字符串工具栏
False
3
3. from … import * 语句
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
例如:
import cn.qlq.a.NumberUtils from cn.qlq.a.StringUtils import * if __name__ == '__main__': print(title); print(isBlank('2')); print(cn.qlq.a.NumberUtils.add(1, 2));
结果:
字符串工具栏
False
3
这种方式有一个缺点就是最后引入的模块的方法或属性会覆盖之前引入的,例如:(后引入的NumberUtils的title属性会覆盖前面的)
from cn.qlq.a.StringUtils import * from cn.qlq.a.NumberUtils import * if __name__ == '__main__': print(title); print(isBlank('2')); print(add(1, 2));
结果
数字工具栏
False
3
4. 深入模块
模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。
例如:
StringUtils.py修改为:
title = "字符串工具栏" def isBlank(str): if str is None or '' == str: return True; else: return False; def isNotBlank(str): return isBlank(str) == False; print("StringUtils 模块被引入")
NumberUtils.py修改为:
title = "数字工具栏" def add(*args): result = 0; for ele in args: result += ele; return result; print("NumberUtils 模块被引入")
ModuleTest.py测试:
from cn.qlq.a.StringUtils import * from cn.qlq.a.NumberUtils import * if __name__ == '__main__': print(title); print(isBlank('2')); print(add(1, 2));
结果:
StringUtils 模块被引入
NumberUtils 模块被引入
数字工具栏
False
3
5. __name__属性
一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。
例如:
title = "数字工具栏" def add(*args): result = 0; for ele in args: result += ele; return result; if __name__ == '__main__': print("自身主函数运行"); else: print("NumberUtils 模块被引入")
运行自身:
自身主函数运行
通过ModuleTest.py引入模块:
from cn.qlq.a.StringUtils import * from cn.qlq.a.NumberUtils import * if __name__ == '__main__': print(title); print(isBlank('2')); print(add(1, 2));
结果:
StringUtils 模块被引入
NumberUtils 模块被引入
数字工具栏
False
3
6. dir() 函数
内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回。如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称。
ModuleTest.py代码如下:
import cn.qlq.a.StringUtils if __name__ == '__main__': print(dir(cn.qlq.a.StringUtils)) print(dir())
结果:
StringUtils 模块被引入
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'isBlank', 'isNotBlank', 'title']
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'cn']
2. Python异常处理
每个语言都有异常相关的处理,js、java等语言中都有。
如果发生异常不处理,程序就会异常终止,但是发生了异常如果catch到了就不会异常处理。
1. python标准异常
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
2.异常处理
(1)捕捉异常可以使用try/except语句。
try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在try里捕获它。
语法:
以下为简单的try....except...else的语法:
try: <语句> #运行别的代码 except <名字>: <语句> #如果在try部份引发了'name'异常 except <名字>,<数据>: <语句> #如果引发了'name'异常,获得附加的数据 else: <语句> #如果没有异常发生
try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。
- 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。
- 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。
- 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。
例如:
#!/usr/bin/python try: result = 1 / 0; except Exception: print("发生异常") else: print("未异常")
结果:
发生异常
try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。
打印异常堆栈信息的最简单的方法是:
#!/usr/bin/python import logging try: result = 1 / 0; except Exception as e: logging.exception(e) else: print("未异常")
结果:
ERROR:root:division by zero
Traceback (most recent call last):
File ".\str.py", line 5, in <module>
result = 1 / 0;
ZeroDivisionError: division by zero
补充:使用except而带多种异常类型
正常的操作 ...................... except(Exception1[, Exception2[,...ExceptionN]]]): 发生以上多个异常中的一个,执行这块代码 ...................... else: 如果没有异常执行这块代码
(2)try-finally 语句
try-finally 语句无论是否发生异常都将执行最后的代码。
#!/usr/bin/python import logging try: result = 1 / 0; except Exception as e: logging.exception(e) else: print("未异常") finally: print("finally代码块")
结果:
ERROR:root:division by zero
Traceback (most recent call last):
File ".\str.py", line 5, in <module>
result = 1 / 0;
ZeroDivisionError: division by zero
PS C:\Users\Administrator\Desktop\pytest> python .\str.py
ERROR:root:division by zero
Traceback (most recent call last):
File ".\str.py", line 5, in <module>
result = 1 / 0;
ZeroDivisionError: division by zero
finally代码块
3. 自定义异常与手动触发异常
可以通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类,可以直接继承,或者间接继承。
raise 可以抛出异常,raise之后的代码不会被执行(类似于Java的throw关键字,throw之后的代码也不会被执行)
例如:模拟自定义用户数超出异常
class UserExceedException(Exception): # 覆盖构造方法 def __init__(self, msg, count): self.msg = msg; self.count = count; def __str__(self): return repr("%s ,user count is %s" % (self.msg, self.count)) if __name__ == '__main__': try: # 模拟从数据库中查出用户数量 # 抛出用户数超出异常 raise UserExceedException("用户数量超出", 5); except UserExceedException as e: print(e)
补充:
python中的 __str__ 函数类似于java的toString函数,返回值是String类型,用于直接打印对象的时候打印返回值。
repr() 函数将对象转化为供解释器读取的形式。
4. python断言
Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
(1)语法格式如下:
assert expression
等价于:
if not expression: raise AssertionError
例如:
assert 1==1 # 条件为 true 正常执行
结果:
Traceback (most recent call last):
File "E:/pythonWorkSpace/FirstProject/cn/qlq/b/ModuleTest.py", line 2, in <module>
assert 1 == 13
AssertionError
(2)assert 后面也可以紧跟参数:
assert expression [, arguments]
等价于:
if not expression: raise AssertionError(arguments)
例如:
if __name__ == '__main__': assert 1 == 13, "1!=13"
结果:
Traceback (most recent call last):
File "E:/pythonWorkSpace/FirstProject/cn/qlq/b/ModuleTest.py", line 2, in <module>
assert 1 == 13, "1!=13"
AssertionError: 1!=13
3. 日志记录
这里只是简单记录下日志使用方法,和java 的日志挺像的。
一般在主main 函数入库配置logging 相关配置:
# 修改日志级别和格式 logging.basicConfig(format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%m-%Y %H:%M:%S", level=logging.INFO)
使用的时候正常使用就好了。 普通的日志打印可以用占位符:(注意是小写方法名称)
logging.info("当前登录的用户: %s", usernameSpan.text)
打印异常可以用:
except Exception as e: logging.exception(e) return