模块与包
模块
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py
。 可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。
一个模块只会被导入一次,不管你执行了多少次import。
Python的搜索路径,搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块。
搜索路径被存储在sys
模块中的path
变量
import sys
print(sys.path) #输出是一个列表,其中第一项是当前目录
import
语句:导入整个模块from... import
语句:从模块中导入指定的部分到当前命名空间中。这种导入方法不会把被导入的模块的名称放在当前的字符表中。
from fibo import fib, fib2 # fibo这个名称在当前命名空间中没有定义
from...import *
语句:从模块中导入所有项目,不推荐使用。这种方法会导入所有名字(除了单一下划线开头的),这很可能会覆盖当前文件中已有的定义。
每个模块都有一个__name__
属性,当其值是__main__
时,表明该模块自身在运行,否则是被引入。
内置的函数 dir()
可以找到模块内定义的所有名称。以一个字符串列表的形式返回:
import fibo
a = [1, 2, 3, 4, 5]
fib = fibo.fib
dir() # 得到当前模块中定义的属性列表 ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
包
包实际是从一个目录中引用模块,目录结构中必须带有一个 __init__.py
文件。
可以采用from package import item
方式,item可以是包的子模块,或者包里的其他名称,如函数,类,变量名;
也可以采用 import item.subitem.subsubitem
这种方式,除了最后一项可以是模块或者包外(不能是函数,类,变量名),其余都必须是包。
导入语句遵循如下规则:
- 如果包定义文件
__init__.py
存在一个叫做__all__
的列表变量,那么在使用from package import *
的时候就把这个列表中的所有名字作为包内容导入。 - 如果
__all__
没有定义,那么使用from sound.effects import *
这种语法的时候,就不会导入包sound.effects
里的任何子模块。他只是把包sound.effects
和它里面定义的所有内容导入进来(可能运行__init__.py
里定义的初始化代码)。这会把__init__.py
里面定义的所有名字导入进来。
输入与输出
输出格式
1 . 想将输出的值转成字符串,可以使用 repr()
或 str()
函数来实现。
x = 10 * 3.25
y = 200 * 200
s = 'x 的值为: ' + repr(x) + ', y 的值为:' + repr(y) + '...'
print(s) # x 的值为: 32.5, y 的值为:40000...
2 . 字符串对象的 rjust()
方法, 它可以将字符串靠右, 并在左边填充空格。还有类似的方法, 如 ljust()
和 center()
。
3 . 另一个方法 zfill()
, 它会在数字的左边填充 0
print('12'.zfill(5)) # 00012
4 . str.format()
的基本使用
print('{0} site: "{1}"'.format('ipine', 'https://ipine.me/'))
在括号中的数字用于指向传入对象在 format()
中的位置;
也可以使用关键字参数:
print('{name} site: "{site}"'.format(name='ipine', site='https://ipine.me/'))
!a
(使用 ascii()), !s
(使用 str()) 和 !r
(使用 repr()) 可以用于在格式化某个值之前对其进行转化:
import math
print('常量 PI 的值近似为: {!r}。'.format(math.pi))
对值进行更好的格式化,可使用可选项 :
和格式标识符(如f
, d
等)
import math
print('常量 PI 的值近似为 {0:.3f}。'.format(math.pi)) # 常量 PI 的值近似为 3.142。
在 :
后传入一个整数, 可以保证该域至少有这么多的宽度。
读写文件
假设有一个叫做f的文件:
f = open("filepath", "r")
1 . 调用 f.read(size)
读取一定数目的数据,当 size
被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。
str = f.read()
print(str)
2 . f.readlines()
将返回该文件中包含的所有行。也可以通过迭代文件对象来读取每行:
# 打开一个文件
f = open("filepath", "r")
for line in f:
print(line, end='')
# 关闭打开的文件
f.close()
更好的打开文件的方式是使用with
语句,它可以保证文件f
总是会关闭,即使在处理过程中出问题了。
with open("myfile.txt") as f:
for line in f:
print(line, end="")
3 . f.write(string)
将 string 写入到文件中, 然后返回写入的字符数。
如果要写入一些不是字符串的东西, 那么需要用 str()
进行转换后再写入。
4 . f.tell()
返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。
5 . 如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what)
函数。
第二个参数from_what
如果是 0 表示开头(默认情况), 如果是 1 表示当前位置, 2 表示文件的结尾,例如:
- seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
- seek(x,1) : 表示从当前位置往后移动x个字符
- seek(-x,2): 表示从文件的结尾往前移动x个字符
6 . Pythonopen()
方法用于打开一个文件,使用该方法一定要保证关闭文件对象,即调用close()
方法。
其完整语法格式为:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
经常会遇到的读文件错误
Python3读CSV文件,出现 UnicodeDecodeError: ‘utf-8’ codec cant’t decode byte 0xd0 in position 0: invalid continuation byte
出现该错误原因:系统默认为UTF8编码,但文件不是UTF8编码。
解决方法:
一:修改文件对应的编码方式,以记事本打开CSV文件,在文件
菜单中选择另存为
,可以看到文件原来的保存类型是ASCII
,在下拉框中选择UTF8
编码。
二:从文件打开方式上解决
open("filename", encoding='ascii', errors='ignore')
异常处理
try... except
语句工作方式:先执行try
后面的子句,若没有异常发生,该子句执行后就结束(若有else
子句,会执行完它后面的语句才结束),忽略except。若执行时发生异常,异常之后的try
子句被忽略,将异常与except
后的名称进行匹配,匹配成功后执行except
后面的子句,最后执行try
子句剩余的代码。若有finally
子句,无论是否发生异常,该子句都会被执行。
一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。
最后一个except子句可以忽略异常的名称,它将被当作通配符使用。可使用它打印错误信息,并把异常抛出:
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
抛出异常,使用raise
语句,后面的参数指明要抛出的异常,该异常必须是一个异常的实例或者是Exception
的子类
面向对象
1 . 类有一个名为 __init__()
的特殊方法(构造方法),该方法 在类实例化时会自动调用。
2 . 类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是self
。
self
代表的是类的实例,代表当前对象的地址,而self.class
则指向类。
class Test:
def prt(self):
print(self) # <__main__.Test instance at 0x100771878>
print(self.__class__) # __main__.Test
t = Test()
t.prt()
3 . 单继承与多继承时,基类必须与派生类定义在一个作用域内。
除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用
class DerivedClassName(modname.BaseClassName):
4 . 多继承时需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 — 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
5 . 继承时,若子类对父类的方法重写了,子类实例化,调用该方法时是调用重写方法;若想用子类对象调用父类已被覆盖的方法,使用 super()
函数
6 . 类的私有变量和私有方法都是以两个下划线开头,类的实例 不能访问类的私有变量也 不能调用类的私有方法。
标准库
1 . os
(操作系统)模块与sys
(命令行参数)模块的不同:
os
模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;
sys
模块负责程序与Python解释器的交互,提供了一系列的函数和变量供用户操作Python运行时的环境。
2 . timeit
模块,可以查看不同方法的性能差异。
例如:使用元组封装和拆封来交换元素看起来要比使用传统的方法要诱人的多,timeit
证明了现代的方法更快一些。
from timeit import Timer
print(Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()) # 0.20183640400000002
print(Timer('a,b = b,a', 'a=1; b=2').timeit()) # 0.15459948599999995
3 . 此外还有:
- 文件通配符模块
glob
,用于从目录通配符搜索中生成文件列表; - 字符串正则匹配模块
re
,为复杂的匹配和处理提供简介的方法; - 数学模块
math
,调用一些底层C数学函数方便运算;常用的random
模块,用于生成随机数; - 访问互联网的模块,最常见的是处理从urls接收的数据的
urllib.request
,以及用于发送电子邮件的smtplib
; - 日期和时间模块
datetime
,可以更有效地处理和格式化输出; - 数据压缩模块,
zlib
,gzip
,bz2
,zipfile
等; - 测试模块
doctest
, 扫描模块并根据程序中内嵌的文档字符串执行测试;