python学习[第十九篇] 模块
python学习[第十九篇] 模块
模块和文件
python中文件时物理上组织模块的方法,模块是按照逻辑层组织代码的方法。一个python文件就是 一个独立模块一个模块也可以看作是一个文件。
模块名称空间
一个名称空间就是一个从名称到对象的关系映射的集合。模块名称是他们属性名称中的一个重要部分。每个模块都定义了自己的唯一的名称空间。,不会出现名称交叉现象。。用句点符号标识来解决属性名称交叉的问题。即mymodel1.test 和mymodule2.test是两个互相独立的属性。
路径搜索和搜索路径
模块的导入需要一个叫路径搜索的过程,搜索路径是一组目录,python通过依次查找搜索路径来进行路径搜索然后导入相应的模块。
默认的搜索路径是在python的shell或命令行pythonpath环境变量定义的。是一组用冒号:分割的目录路径。
解释器启动之后,可以访问这个搜索路径。他会保持在sys.path变量里。不过这不是冒号分隔的字符串,而是每个独立路径的列表。我们可以在python代码中通过修改sys.ptah来改变搜索路径。可以通过sys.module查看已经导入了哪个模块来自哪个搜索路径。
名称空间
名称空间
名称空间是名称到对象的映射。python在运行期间有两个或三个名称空间。分别是局部名称空间,全局名称空间,和内建名称空间,局部名称空间是在运行期间不断变化的。
加载顺序为首先加载内建名称空间由__builtins__模块中的名字构成,然后假装执行模块的全局空间,然后在模块开始执行后变为活动命名空间。如果执行期间调用了一个函数,,那么将创建局部名称空间。
__builtins__和__builtin__的区别:__builtins__包好内建名称空间中内建名字的集合,其中大多数来自__builtin__模块,__builtin__模块包含内建函数,异常以及其他属性。
名称空间与作用域
名称空间是纯粹意义上的名字和对象间的映射关系,而作用能够以指出了用户代码的那些物理位置上可以访问到这些名字。
所有局部名称空间都在局部作用范围内,局部作用范围外的所有名称都在全局作用范围内。局部名称空间和做哟你哦个与会随函数调用的变化而变化。全局名称空间是不变的。
名称查找,确定作用域和覆盖
python在访问一个属性时,必须要在三个名称空间的一个找到他,首先从局部名称空间开始,如果没有找到,解释器会查找全局名称空间,如果全局空间也没找到,会在内建名称空间里查找,如果内建名称空间也没有找到,会报NameErro的错误。
局部名称空间中找到的名字会隐藏全局空间或内建名称空间的对应对象。这就相当于覆盖了那个全局变量。
>>> def foo(): ... print 'this is foogai' ... i=50 #覆盖了全局变量i ... print 'i is 50' ... >>> i=100 >>> i 100 >>> foo() this is foogai i is 50
无限制的名称空间
python的一个有用特性就是可以在任何需要放置数据的地方获得一个名称空间。
例如我们可以在任何时候给函数添加属性:
>>> def foo(): ... pass ... >>> foo.version=0.1 >>> foo.__doc__='this is empty foo' >>> print foo.version 0.1 >>> print foo.__doc__ this is empty foo
例如为一个类添加属性:
>>> class Emptyclass(): ... pass ... >>> emp=Emptyclass() >>> emp.x=1 >>> emp.y=2 >>> emp.version=01. >>> print emp <__main__.Emptyclass instance at 0x0284D580>
导入模块
导入模块方式
直接导入 import module
导入模块的某个属性 from module import method
多行导入 利用括号 from module1 import (method1,method2)
扩展import as 语句 from module1 import method as md
模块导入的特性
载入时执行模块
python加载某个模块的时候会执行这个模块。也就是说被载入模块的顶层代码将直接被执行。包括设定的全局变量,类的声明和函数的生命。如果有检查__name__的操作也会被执行。所以良好的编程习惯是尽可能多的把代码封装到函数。明确的说只把函数和模块定义放入模块的顶层是良好的习惯。
导入(import)和加载(load)
一个模块只被加载一次,无论被导入多少次,这可以阻止多重导入时代码被多次执行。例如导入了sys模块,而要导入的其他的3个模块也导入了sys模块。那么加载只在第一次导入时发生。
导入到当前名称空间的名称
调用from import语句可以把名字导入到当前的名称空间中来,这意味着不需要使用属性句点标识符的方式来访问模块的标识符。
>>> from sys import path >>> print path ['', 'C:\\Windows\\system32\\python27.zip', 'C:\\Python27\\DLLs', 'C:\\Python27\ \lib', 'C:\\Python27\\lib\\plat-win', 'C:\\Python27\\lib\\lib-tk', 'C:\\Python27 ', 'C:\\Python27\\lib\\site-packages']
关于__future__
由于改进,新特性和当前特性增强,某些变化会影响到当前功能,所以为了python程序员为新事物做好准备,python实现了__future__指令。
只能用from future import xxx语法来导入某个特定的属性或方法。
从zip文件中导入模块
如果搜索路径中存在一个包含模块(.py,.pyc 或.pyo)的.zip文件,导入时会把自拍文件当作目录处理,在文件中搜索模块。
模块内建函数
__import__()
import语句会调用__import__()函数。
__import__() [modules[,globals][,locals[,fromlist]]]) gloabls,locals,fromlist 默认分别为globals() locals() 和[]
sys=__import__('sys')
globals() locals()
globals()和locals()内建函数分别返回调用全局和局部名称空间的字典。在一个函数内部们局部名称空间代表在函数执行时候定义的所有名字,locals()函数返回的就是 这些名字的字典,globals函数会返回函数可以返回的全局名字。
>>> def foo(): ... print 'calling foo()...' ... astring='bar' ... anInt=42 ... print "foo()'s globals:" ,globals().keys() ... print "foo()'s locals :" ,locals().keys() ... def bar(): ... print 'calling bar()...' ... bstring='22' ... print astring ... print 'bars() globals :',globals().keys() ... print 'bars() locals :',locals().keys() ... bar() ... >>> foo() calling foo()... foo()'s globals: ['__builtins__', '__package__', 'uuu', '__name__', 'foo', '__doc__'] foo()'s locals : ['anInt', 'astring'] calling bar()... bar bars() globals : ['__builtins__', '__package__', 'uuu', '__name__', 'foo', '__doc__'] bars() locals : ['bstring', 'astring'] >>> print "__main__'s globals :",globals().keys() __main__'s globals : ['__builtins__', '__package__', 'uuu', '__name__', 'foo', '__doc__'] >>> print "__main__'s locals :",locals().keys() __main__'s locals : ['__builtins__', '__package__', 'uuu', '__name__', 'foo', '__doc__']
reload()函数
reload函数即为重新加载某个模块
import sys reload(sys) sys.setdefaultencoding('utf8')
包
包示例层级关系如下图。__init__.py是初始化模块,from-import语句导入子包时需要他。
Phone/ |----_init_.py |----common_util.py Mobile/ |----__init.py |----Analog.py |----Digital.py Pager/ |----__init__.py |----Numeric.py
使用from-import 导入包
可以使用from-import 实现不同需求的导入。
绝对导入
绝对导入都是通过sys.path的路径来进行访问的。
from Phone.Page import Numeric
相对导入
假设要在Digital.py中调用Analog.py
from Phone.Mobile.Analy import dial
或者
from .Analog import dial
from ..Pager import Numeric
模块的其他属性
自动载入的模块
当python解释器在标准模式启动时,一些模块会被解释器自动导入,用于系统相关的操作。唯一一个影响我们的是__builtin__模块,他会被正常地载入,这和__builtins__模块相同。
sys.module变量包含一个有当前载入到解释器的模块组成的字典。模块名作为键值,模块位置作为路径。
导入循环
即两个模块互相导入对方,会陷入导入循环,然后报错ImportError
报错情况如下
#imptee.py from pro.test import impter foo = 'abc' def show(): print('foo from impee',foo) print(impter.foo) #impter.py from pro.test.imptee import foo,show import sys show() foo = 123 print('foo from impter:',foo) show()
执行报错如下:
D:/programSoft/PyCharm_pro/projects/pro/test/impter.py Traceback (most recent call last): File "D:/programSoft/PyCharm_pro/projects/pro/test/impter.py", line 1, in <module> from pro.test.imptee import foo,show File "D:\programSoft\PyCharm_pro\projects\pro\test\imptee.py", line 1, in <module> from pro.test import impter File "D:\programSoft\PyCharm_pro\projects\pro\test\impter.py", line 1, in <module> from pro.test.imptee import foo,show ImportError: cannot import name 'foo' from 'pro.test.imptee' (D:\programSoft\PyCharm_pro\projects\pro\test\imptee.py)
这是我们可以将imptee中的 print(impter.foo)放入到一个函数中,然后到impter模块这样就不会报错了。
#修改后的imptee.py foo = 'abc' def show(): print('foo from impee',foo) def show_impter(): from pro.test import impter print(impter.foo) show_impter()
##模块end