3.6 模块和包
3.6.1 模块
1.模块
函数是可以重复调用的程序块。在程序中使用他人已经编写好的函数代码,或者使自己编写的函数能被其他程序使用的方法是:导入(import)该函数所在的模块(mudule)。
Python模块(mudule)就是包含Python语句的、文件名后缀(文件扩展名)是.py的文件。这个文件中可以包含变量、函数和类等定义。
例如,创建一个叫作hi的模块,也就是创建一个hi.py文件:
def hello(name): print("hello") print(name)
模块hi里包含了一个叫作hello()的函数。
2.导入(import)模块
在程序中如果需要使用其他模块中的函数等功能,就需要用关键字import导入相应的模块。导入模块的一般格式是:
import模块名
要使用上面的模块hi中的hello()函数,首先要import(导入)hi模块,格式如下:
import hi
注意
导入的模块只要说明模块名即可,不能有文件扩展名.py。程序代码中如果要使用模块中的对象,如函数,则需要用.运算符,即使用“模块名.函数名”访问具体的函数。例如,使用hi.hello访问模块hi中的函数hello():
import hi hi.hello(’小白’)
输出:
hello 小白
Python提供了许多标准模块,这些模块文件可以在Python安装目录的lib文件夹中找到。和导入自己编写的模块一样,可以导入那些别人编写好的模块。例如,导入一个用于数学计算的模块math,其中有一个表示圆周率的变量pi:
import math print(math.pi) print(math.sin(1.57)) #1.57约等于math.pi/2 print(math.sin(math.pi/2))
输出:
3.141592653589793 0.9999996829318346 1.0
如果忘记在函数名前添加“模块名.”,则会报错:“名字xxx未定义”。例如:
import math print(sqrt(2)) #sqrt是求平方根函数
输出:
---------------------------------------------------------------------- NameError Traceback(most recent call last) <ipython-input-8-692e4d48a502> in <module>() 1 import math ----> 2 print(sqrt(2)) #sqrt是求平方根函数 NameError: name 'sqrt' is not defined
3.重命名导入模块
可以用“import ...as”对一个导入进来的模块重新命名。例如:
import math as m #将导入的模块math命名为m print(m.pi) #通过m而不是math去引用其中的名字
输出:
3.141592653589793
此时,只能用重命名的m.pi而不能用math.pi去访问math中的pi。
4.导入单独名字
可以用“from...import”从一个模块导入一个单独的名字,导入的这个名字就不用在前面添加“模块名.”前缀了。例如:
from math import sqrt #从math模块导入名字sqrt print(sqrt(2)) #sqrt是求平方根函数
输出:
1.4142135623730951
5.导入所有名字
还可以通过“from…import *”导入模块中的所有名字,该模块中的名字就可以直接使用,而不再需要模块名前缀了:
from math import * print(pi) #math模块中的pi变量 print(sin(1.57)) #math模块中的sin函数 print(sqrt(2)) #math模块中的sqrt函数
输出:
3.141592653589793 0.9999996829318346 1.4142135623730951
模块中的对象,如常量、函数、类等,可被不同的程序代码重复使用,即模块使得代码可以“复用”(如重复多次调用一个模块中的函数)。例如,前面多次使用的函数print()、math模块中的函数sqrt()等。
模块还有一个好处就是可以避免“名字冲突”,因为程序代码中不可避免地会使用他人的代码,如果不同的人使用了同一个名字就会引起冲突,但如果这些名字属于不同的模块,就可以通过模块名来区分它们。
因此,为了避免名字冲突,应该尽量避免“from...import”和“from ... import *”这种将名字直接导入的方法,而推荐使用“import ...”导入模块的方法。
前面介绍过Python的内置函数type()可以用于查询一个对象的类型,此外,还有两个函数dir()和help(),可分别用于显示一个对象的所有属性和查询一个对象(如函数)的文档字符串。通过这三个“内省函数”可以获得一个对象的帮助信息。
6.函数dir()
内置函数dir()返回一个对象的所有属性的名字。其函数格式是:
dir([object])
如果没有指定可选的参数对象object,则返回当前作用域中的所有名字,否则就返回指定的参数对象的object的所有属性。例如:
>>>dir()
返回当前作用域中的所有名字:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
假如继续执行:
>>>alist=[2,5,8] >>>dir()
则可以看到返回的名字的列表里多了一个新的名字alist:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'alist']
假如再执行:
>>>b=3.14 >>>dir()
则可以看到返回的名字的列表里又多了一个新的名字b:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'alist', 'b']
假如在调用函数dir()时输入一个对象,则函数dir()返回这个对象的所有属性。例如:
>>>dir(alist)
将显示list类型对象alist的所有属性:
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__sub- classhook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
假如再执行:
>>>dir(b)
将显示float类型对象b的所有属性:
['__abs__', '__add__', '__bool__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getformat__', '__getnewargs__', …]
如果输入一个模块名,则显示这个模块中的所有属性,如模块中的函数名、类名、变量名等。例如:
>>>import sys >>>dir(sys)
将显示sys模块的所有名字(属性):
['__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', …]
如果输入自定义的模块名hi:
>>>import hi >>>dir(hi)
则显示hi模块的所有属性:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'hello']
7.函数help()
函数help()打印一个对象的帮助信息。例如,通过函数help()了解函数math.sin()的函数说明:
import math help(math.sin) Help on built-in function sin in module math: sin(…) sin(x) Return the sine of x(measured in radians).
如果通过一个对象的__doc__属性直接打印一个对象的文档串,则显示类似的帮助文档。例如:
print(math.sin.__doc__)
输出:
sin(x) Return the sine of x(measured in radians).
读者还可完成以下练习。
执行如下的help和dir命令,查询这些模块或函数的帮助文档或属性。
help(len)——内置函数len()的帮助文档,注意参数是len而不是len()。
help(sys)——系统模块sys(必须先导入sys)的帮助文档。
dir(sys)——系统模块sys里定义的符号(属性)。
help(sys.exit)——sys模块里的函数exit()的帮助文档。
help('xyz'.split)——字符串的split()方法的帮助文档,可以通过str类型或str类型的对象,如xyz调用这个方法help('xyz'.split),等价于help(str.split)。
help(list)——list对象的帮助文档。
dir(list)——list对象的所有属性,包括对象的所有方法和属性。
help(list.append)——list对象的append()方法。
通过这些帮助信息,就可以了解如何使用某个函数或对象的方法或属性。例如,使用list的append()方法可以向一个list对象的最后添加一个元素:
alist=[1,2,3] alist.append('hello') print(alist)
输出:
[1, 2, 3, 'hello']
8.模块的__name__属性
每个模块都有一个__name__属性,当这个模块被其他程序(模块)通过import方式导入时,其值就是这个模块的模块名。例如:
>>> print(hi.__name__)
输出:
hi
但如果一个模块作为主程序执行,即在控制台窗口通过Python解释器运行这个脚本时,这个模块的属性__name__就是main,即说明这个模块是主程序。例如,编写如下的脚本文件hi.py:
def hello(name): print("hello") print(name) print(__name__) #打印该模块自身的__name__属性
在控制台窗口运行这个脚本:
python hi.py
输出结果如下:
__main__
因此,在编写脚本文件时,经常在程序中根据__name__==__main__判断脚本是否作为主程序运行从而执行特定的程序代码。例如,修改上述的hi.py代码:
def hello(name): print("hello") print(name) print(__name__) if(__name__=='__main__'): hello('Li ping') else: print('hi.py作为模块导入而不是作为主程序运行!')
该程序通过if(__name__=='__main__')判断脚本是否在主程序执行,如果作为主程序执行,则执行hello(‘Li Ping'),否则就执行另外的print()语句。例如,执行:
python hi.py
输出:
__main__ hello Li Ping
3.6.2 sys模块(Python解释器接口)
sys模块负责与Python解释器的交互,提供一系列的函数和变量,用于操控Python的运行时环境。
1.sys.argv
在Python解释器下执行一个脚本时,会通过sys.argv变量向这个脚本传递一个命令行参数列表。其中,第一个元素sys.argv[0]是脚本程序的完整路径或文件名(取决于操作系统)。例如,编写一个叫作abc.py的脚本:
import sys print(’脚本名:', sys.argv[0])
在Python解释器中执行这个脚本:
python abc.py
在Windows系统上将输出下面的信息:
脚本名:abc
如果执行这个脚本时,新提供了其他的参数,那么从第二个元素sys.argv [1]起就是这些新提供的参数。例如,abc.py脚本内容如下:
import sys print(’脚本名:', sys.argv[0]) print('Hello ', sys.argv[1])
在Python解释器中执行如下脚本:
python abc.py wang
程序代码中sys.argv[0]是模块名abc,而sys.argv[1]是参数wang,在Windows系统上将输出下面的信息:
脚本名:abc Hello wang
2.sys.path(模块搜索路径)
当import一个模块时,Python会在一些位置查找是否存在相应名字的模块,首先查找是否在内在(built-in)模块中,如未找到就在当前目录查找,如仍未找到,就在sys.path变量指定的目录里查找。
sys.path是一个字符串列表,用于指定模块的搜索路径,包括环境变量PYTHONPATH里的目录和安装目标的默认值。Python解释器在导入模块时,会在这些路径中查找相应的模块。
例如,可输出sys.path中的目录内容:
import sys print(sys.path)
输出:
['', 'E:\\Anaconda\\python36.zip', 'E:\\Anaconda\\DLLs', 'E:\\Anaconda\\lib', 'E:\\Anaconda', 'E:\\Anaconda\\lib\\site-packages', 'E:\\Anaconda\\lib\\ site-packages\\win32', 'E:\\Anaconda\\lib\\site-packages\\win32\\lib', 'E:\\Anaconda\\lib\\site-packages\\Pythonwin', 'E:\\Anaconda\\lib\\ site-packages\\IPython\\extensions', 'C:\\Users\\s\\.ipython']
sys.path是一个字符串的list,其中,每个元素表示一个文件路径,也可以在自己编写的程序中将特定的文件路径通过list的append()添加到这个list中。
sys.path.append("python/my_code") print(sys.path)
输出:
['', 'E:\\Anaconda\\python36.zip', 'E:\\Anaconda\\DLLs', 'E:\\Anaconda\\lib', 'E:\\Anaconda', 'E:\\Anaconda\\lib\\site-packages', 'E:\\Anaconda\\lib\\ site-packages\\ win32', 'E:\\Anaconda\\lib\\site-packages\\win32\\lib', 'E:\\Anaconda\\lib\\site-packages\\ Pythonwin', 'E:\\Anaconda\\lib\\ site-packages\\IPython\\extensions', 'C:\\Users\\s\\ .ipython', 'python/my_code']
3.sys.exit()(退出函数)
退出函数sys.exit()用于退出Python脚本程序,该函数可以带一个整数作为参数,用于表示程序退出的状态(不同的整数表示不同的退出情形)。不同操作系统用不同整数的表示程序退出状态。通常,传递整数0表示程序正常退出。当调用函数sys.exit()时,它将引发SystemExit异常,该异常允许清理函数在异常处理的try / except模块的finally子句中起作用。例如:
import sys sys.exit(0)
抛出错误异常:
An exception has occurred, use %tb to see the full traceback. SystemExit: 0
如果要退出解释器而不是单个脚本程序,则可以使用内置函数exit(),该内置函数直接退出并关闭解释器:
exit(0)
4.sys.executable
sys.executable中保存Python解释器的完整路径。例如:
import sys sys.executable
输出:
'C:\\Users\\s\\Anaconda3\\python.exe'
5.sys.platform
sys.platform值为平台标识符。例如:
import sys sys.platform
输出:
'win32'
通过检查sys.platform值,可根据不同平台导入不同的模块,或者根据不同平台执行不同的处理代码。例如:
os=sys.platform if os=="win32": # 使用Windows平台相关的代码或模块 pass elif os.startswith('linux'): # 使用Linux平台相关的代码或模块 import subprocess subprocess.Popen(["ls, -l"])
6.sys.getrefcount()
函数sys.getrefcount()返回一个对象的引用计数,该计数通常比用户预期的多一个,因为它包含作为函数getrefcount()形参的临时引用。例如:
import sys a=20489 #a引用的20489的引用计数为1 sys.getrefcount(a) #实参a传递给函数形参,使20489的引用计数为2
输出:
2
继续执行下面的命令:
b=a sys.getrefcount(b) #b引用了a引用的对象,20489的引用计数变为3
输出:
3
当a=20489引用创建的字符串对象20489时,该对象的引用计数为1;将a作为实参传递给sys.getrefcount(obj)时,形参obj又引用了a引用的对象,使得20489的引用计数变为2;当sys.getrefcount(b)时,即b引用a引用的对象时,20489的引用计数变为3;当一个对象的引用计数变为0时,Python才开始销毁这个对象,并回收这个对象占用的内容。
7.sys.getsizeof()
函数sys.getsizeof()返回一个对象占用的内存的字节数。例如:
import sys a=25 sys.getsizeof(a)
输出:
28
即整数a占用了28字节。
8.sys.stdin、sys.stdout、sys.stderr
sys.stdin、sys.stdout、sys.stderr分别映射到与解释器的标准输入、标准输出和错误流相对应的文件对象。除脚本外,sys.stdin用于提供给解释器的所有输入,而sys.stdout用于函数print()的输出。解释器自己的提示及其错误消息转到sys.stderr。例如:
import sys user_input=sys.stdin.readline()
程序等待用户输入,如果输入如下内容:
hello world
然后,执行下列语句:
print("Input : " + user_input)
则输出:
Input : hello world
9.sys.getdefaultencoding()
函数sys.getdefaultencoding()获取系统当前编码,系统默认编码是UTF-8。
sys.getdefaultencoding()
输出:
'utf-8'
10.sys.setdefaultencoding()
函数sys.setdefaultencoding()设置系统默认编码。例如,执行setdefaultencoding('utf8')时,则将系统默认编码设置为UTF-8。
3.6.3 伪随机数发生器模块
1.random模块
random模块为不同的分布提供伪随机数生成器。例如,为整数提供从一个范围里均匀地选取一个整数的函数;为序列提供均匀地选择一个元素、生成随机排列、用于随机抽样的函数;为实数提供计算均匀、正态(高斯)、对数正态、负指数、伽马和贝塔分布的函数。其中,为了生成角度分布,可以使用von Mises分布。
几乎所有模块函数都依赖于基本函数random(),它在半开放范围[0.0,1.0)内均匀生成随机浮点数。Python使用Mersenne Twister作为核心生成器,它产生53位精度浮点数,周期为219937-1,其底层的C实现线程既快又安全。Mersenne Twister是最广泛使用的随机数发生器之一,但因其具有完全确定性,所以它不适用于所有目的,且完全不适用于加密。
下面是random模块的一些常用随机数函数。
● random.random()生成[0.0, 1.0)之间的浮点数。
● random.uniform(a, b)如果a<b,则生成[a, b]之间的浮点数,否则,生成[b, a]之间的浮点数。
● random.choice(sequence)从序列sequence中获取一个随机元素。
● random.randrange([start], stop[, step])在range(start, stop, step)中随机选择一个元素,等价于choice(range(start, stop, step))。
例如,random.randrange(10, 100, 2),结果相当于从[10, 12, 14, 16, ..., 96, 98]序列中获取一个随机数。
● random.randint(a, b)生成整数a和b之间的整数,且必须满足a<=b,相当于randrange(a, b+1)。
● random.shuffle(x[, random])随机置换序列x(将序列x中元素的位置随机置换/打乱顺序),可选参数random是一个返回[0.0,1.0)之间浮点数的随机函数,默认就是random.random()函数。
● random.sample(x, k)从总体序列或集合x中随机选择k个元素,该函数可用于无替换随机抽样。函数random.sample()不会修改原有序列,而只是返回一个list对象。
下面是一个简单的使用示例。
import random a=random.random() #[0.0, 1.0)之间的随机浮点数(除1外) b=random.uniform(100, 1) #[1.0, 100.0)之间的浮点数,可能不包括100.0 c=random.randint(-10, 80) #随机整数 print(a, '\t', b, '\t', c) r=random.choice(r'dfs*d=! kh#^h@') #在字符串r'dfs*d=! kh#^h@’中随机选择一个 print(r) p=["Python", "C", "小白", "a.hwdong.com"] print(random.choice(p)) #从列表x中任意选择1个数 random.shuffle(p) #重排序:打乱列表x中元素的顺序 print(p) alist=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] a=random.sample(alist, 5) #从list中随机获取5个元素,返回1个list对象 print(a)
输出:
0.3492832859352474 77.17952854183415 66 s 小白 ['Python', 'C', ’小白’, 'a.hwdong.com'] [10, 8, 5, 3, 9]
2.种子(seeding)
每次调用random()等随机数生成函数时都会产生不同的值,这对于生成不断变化的随机数是有用的,但有时则希望以不同方式处理相同的数据集。例如,对于两个不同的算法,从数据集随机取出相同的一组数据,然后比较不同算法对这组数据处理的差别。这时可以用函数seed()初始化随机数发生器,以便让它产生预期的一组值。例如,两次运行下面的程序,都会产生一样的一组随机数:
import random def f(): random.seed(1) for i in range(5): print('{:04.3f}'.format(random.random()), end=' ') print() for i in range(3): print(random.randint(-5, 5), end=' ') print() f() f()
输出:
0.134 0.847 0.764 0.255 0.495 2 2 5 0.134 0.847 0.764 0.255 0.495 2 2 5
3.6.4 包
1.包(package)
一个大的软件项目会包含很多模块文件,如果将这些模块文件都放在一个文件夹中,则难以管理并跟踪它们。如同计算机中的资源管理器使用不同文件夹分类管理文件一样,这些模块文件也可以放在不同文件夹(目录)中。
Python以包的形式将相关模块文件组合在不同文件夹中。Python的包的概念很简单,可以理解为,包就是一个文件夹,其中包含了一个__init__.py文件。这个__init__.py文件可以是一个空文件,也可以包含一些Python语句。也就是说,包就是一个包含__init__.py文件的文件夹,包名就是文件夹名。在一个包(文件夹)中可能还包含其他的包(文件夹)或模块文件,这些包中又会包含其他的包和模块文件,从而形成一个层次性的目录结构。
创建包很简单,就是利用操作系统自带的层次性文件系统创建包对应的文件夹。例如,创建一个叫作myProj的文件夹,在该文件夹下创建一个空的__init__.py和两个模块文件hello.py、hi.py。这个叫作myProj的文件夹就是一个包,包名就是文件夹名myProj,文件夹和其中的文件形成如下的目录结构。
myProj |_ __init__.py |_ hello.py |_ hi.py
例如,hello.py的内容如下:
def hello(name): print('hello, ', name) def f(): print(’你好’)
例如,hi.py的内容如下:
def welcome(city): print('hi, welcome to ', city)
例如,这个文件夹myProj的路径已经在sys.path的某个路径中(如果该文件夹路径不在sys.path中,则可通过sys.path.a ppend()将其添加到这个对象中),在程序中就可以导入这个包中的模块文件,并调用其中的对象(如函数):
import myProj.hello, myProj.hi myProj.hello.hello('Li ping') myProj.hi.welcome('ShangHai')
输出:
hello, Li ping hi, welcome to ShangHai
当然,也可以采用from...import导入单个模块或名字。
例如,导入单个模块:
from myProj import hello hello.hello('Li ping')
输出:
hello, Li ping
可以导入单个模块并重命名。例如:
from myProj import hello as he he.hello('Li ping')
输出:
hello, Li ping
也可以导入模块中的具体对象(如函数)。例如:
from myProj.hello import hello as heo heo('Li ping')
输出:
hello, Li ping
当然,还可以导入整个包。例如:
import myProj myProj
尽管语法上没有问题,但是这种方式并没有将该包中的模块名导入当前的名字空间中,因此,不能访问该包中的模块:
myProj.hello.hello('Li ping')
这种导入包的方式没有用处。
2.包的初始化
如果程序包目录中存在名为__init__.py的文件,则在导入程序包或程序包中的模块时会执行该文件。该功能可以用于执行包初始化代码,如包级数据的初始化。
例如,__init__.py的代码如下:
print(’调用__init__.py for {__name__}') NAMES=['Li Ping', 'Wang Hao', 'Zhang Ping']
当导入包或包中的模块时,会首先执行__init__.py中的代码,如上面的输出语句和初始化一个全局变量NAMES:
import myProj
输出:
调用__init__.py for myProj
执行:
myProj.NAMES
输出:
['Li Ping', 'Wang Hao', 'Zhang Ping']
模块中的代码可以访问用import导入的包中的全局变量,如myProj.NAMES。例如,修改hello.py模块文件,导入myProj的NAMES全局变量:
def hello(name): print('hello, ', name) def f(): from myProj import NAMES print(NAMES[0])
执行:
from myProj import hello hello.f()
输出:
调用__init__.py for myProj Li Ping
__init__.py也可用于实现从包中自动导入模块。import myProj仅将名称myProj放在调用程序的本地符号表中,而不导入任何模块。但是,如果myProj目录中的__init__.py包含以下内容:
print(f’调用__init__.py for {__name__}') import myProj.hello, myProj.hi NAMES=['Li Ping', 'Wang Hao', 'Zhang Ping']
则当执行import myProj时,包中的模块hello和hi就被自动导入了。下面的语句就能正常工作了:
import myProj myProj.hello.hello('Li ping')
对于模块,可以通过import *将模块中的所有对象导入到本地符号表中。例如:
>>>dir()
显示本地符号表:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
继续执行:
>>>from myProj.hello import * >>>dir()
显示本地符号表:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'hello']
可以看到,myProj.hello模块中的对象,如函数hello()和f(),都已导入本地符号表中。
对于包也可使用import *。例如:
from myProj import *
显示结果:
['NAMES', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
可以看到,已导入包myProj中的全局变量NAMES,但并没有导入包及其包含的模块中的所有对象。
3.__all__变量
Python规定,如果在__init__.py中定义了一个__all__变量,且该变量中包含一些模块名,那么使用import *,如from myProj import *,就会导入__all__变量中的模块中的所有对象。
例如,修改,__init__.py文件:
print(’调用__init__.py for {__name__}') NAMES=['Li Ping', 'Wang Hao', 'Zhang Ping'] __all__=['hello', 'hi']
可以看到,此时本地符号中就包含了__all__变量中的模块名:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'hello', 'hi']
可以使用这些模块中的对象,如:
>>>hello.f()
输出:
Li Ping
同样,调用hi模块的函数welcome():
>>>hi.welcome('Bei Jing')
输出:
hi, welcome to Bei Jing
在__init__.py中定义的__all__变量是针对整个包的,也可以在单独的模块文件中定义__all__变量,以说明该模块文件中的哪些对象可以被其他程序import。例如,修改hello.py文件:
__all__=['hello'] def hello(name): print('hello, ', name) def f(): from myProj import NAMES print(NAMES[0])
说明当该模块被其他程序导入时,只有函数hello()是可见的。
>>>dir()
输出:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
执行:
from myProj.hello import * dir()
输出:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'hello']
因此,可以执行:
hello(’李平’)
输出:
hello,李平
但不能执行:
f()
会产生错误:
Traceback(most recent call last): File "<pyshell#5>", line 1, in <module> f() NameError: name 'f' is not defined
__all__总结
● 如果没有为一个包定义__all__,则当使用import *时不会导入任何对象。
● 如果没有为一个模块定义__all__,则当使用import *时将导入模块中的所有对象,否则仅导入__all__中定义的对象。
4.子包
包中可以嵌套子包(如同文件夹可以包含子文件夹一样),子包中还可以嵌套子包的子包。包之间可以一直这样嵌套下去(正如文件夹可以一直嵌套一样)。例如:
myProj |_ __init__.py |_ util |_ __init__.py |_ img_process.py |_ math_util.py |_ game |_ __init__.py |_ init_game.py |_ scene |_ __init__.py |_ init_scene.py |_ render.py |_ update.py |_ hello.py |_ hi.py
在myProj包中除三个模块文件__init__.py、hello.py和hi.py外,还有两个子包util和game,在每个子包下又有一些模块文件或包,如game包下有两个模块文件__init__.py、init_game.py和一个包scene,包scene下又有一些模块文件。
无论这种嵌套的包的层次结构多么复杂,都可以使用import语句,唯一需要注意的是,需要用额外的.表示完整的正确嵌套包含关系。例如:
导入单独的模块:
import myProj.util.math_util
导入单独的模块名:
from myProj.util import math_util
假设render模块有一个函数render(),则也可以直接导入单独的函数:
from myProj.game.scene.render import render
3.6.5 Matplotlib包
Python提供了一个专门用于绘图的工具包Matplotlib。Windows系统下以管理员权限打开命令行窗口,然后执行下列命令进行安装:
pip install matplotlib
Linux或Mac可以管理员权限执行下列命令进行安装:
sudo pip install matplotlib
matplotlib的pyplot模块提供了简单的绘图函数,可以用下面的代码导入matplotlib.pyplot模块并命名为plt,以避免在代码中写入一长串的matplotlib.pyplot。
import matplotlib.pyplot as plt
jupyter环境中可以用命令“%matplotlib inline”将图形嵌入在浏览器网页中。
%matplotlib inline
pyplot模块的函数plot()可以直接绘制2D数据。例如:
y=[i for i in range(10)] print(y) plt.plot(y) # 绘制y作为纵轴坐标点构成的图形 plt.show() # 调用plt.show()显示图形
输出并显示绘制图形:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
尽管只给了纵轴坐标的数组y,函数plot()默认自动生成从0开始的横轴坐标。当然,可以分别传递2个数组表示x和y坐标。例如:
x=[i*0.1 for i in range(10)] y=[xi**2 for xi in x] print(["{0:0.2f}".format(i)for i in x]) print(["{0:0.2f}".format(i)for i in y]) plt.plot(x, y) #绘制(x, y)坐标点构成的图形 plt.show() #调用plt.show()显示图形
输出:
['0.00', '0.10', '0.20', '0.30', '0.40', '0.50', '0.60', '0.70', '0.80', '0.90'] ['0.00', '0.01', '0.04', '0.09', '0.16', '0.25', '0.36', '0.49', '0.64', '0.81']
可以绘制几个曲线。例如:
x=[i*0.2 for i in range(10)] y=[xi**2 for xi in x] y2=[3*xi-1 for xi in x] plt.plot(x, y) #绘制(x, y)坐标点构成的图形 plt.plot(x, y2) plt.ylim(0,5) plt.xlabel('x axis label') plt.ylabel('y axis label') plt.title('y=$x^2$ and y=3x-1') plt.legend(['y=$x^2$', 'y=3x-1']) plt.show() #调用plt.show()显示图形
其中,pyplot模块的函数title()用于给图起一个标题,而函数legend()则给每个绘制的曲线起一个名字,函数xlim()和ylim()分别用于限定x和y坐标的范围,函数xlabel()和ylabel()分别用于给x轴和y轴分配一个标签。可以看到不同的图形将自动用不同的颜色显示。
函数plot()还可以接收一些参数,用于定制绘制的图形的样式。例如:
import math x=[i*0.2 for i in range(50)] y=[math.sin(xi)for xi in x] y2=[math.cos(xi)for xi in x] y3=[0.2*xi for xi in x] plt.plot(x, y, 'r-') plt.plot(x, y2, 'bo') plt.plot(x, y3, 'g:') plt.legend(['sin(x)', 'cos(x)', '0.2x']) plt.show()
其中,'r-’的r表示红色(red); -表示短线;'bo’的b表示蓝色(blue)、o表示以圆点;'g:’的g表示绿色(green); :表示虚线。
pyplot模块除函数plot()可以绘图外,还有其他的一些函数也可用于绘制其他类型的图,如函数scatter()用于绘制散乱点图。例如:
import math x=[i*0.2 for i in range(50)] y=[math.sin(xi)for xi in x] y2=[math.cos(xi)for xi in x] y3=[0.2*xi for xi in x] plt.plot(x, y, 'r-') plt.plot(x, y2, 'bo') plt.plot(x, y3, 'g:') plt.legend(['sin(x)', 'cos(x)', '0.2x']) plt.show()
总结
● 后缀是.py的Python程序文件称为模块。可以用关键字import导入一个模块(如xxx)到程序中,模块xxx中的名字如name,可以通过xxx.name访问。import xxx as yy用于在导入模块xxx时给模块xxx起一个别名yy,访问模块中的名字就要用这个别名作为前缀。
● from ... import可以导入模块中的一个名字(如from xxx import name导入模块xxx的单个名字name),也可以导入模块中的所有名字(如from xxx import *导入模块中的所有名字)。
● 包就是一个包含__init__.py文件的文件夹。这个__init__.py可以是空的文件,也可以包含一些Python命令(如执行一些初始化)。包将所有模块文件组织成一个层次结构。
● sys模块是Python解释器交互的接口,如向脚本程序传递命令行参数,添加工作路径等。
● random随机数模块可以用于生成各种随机数,如从一个序列对象里随机选择元素,或者得到一个序列的随机排列等。
● Python提供了一个专门用于绘图的工具包Matplotlib。