目录
1 模块
一个模块是包含了Python定义和声明的文件,文件名,就是模块名字加上py 后缀
把定义的函数、变量保存到文件中,通过Python test.py的方式执行,test.py就是脚本文件。程序功能越来越多,这些监本文件还可以当做模块导入其他的模块中,实现了重利用。
import
使用import的时候,想要使用spam下面的方法,必须使用spam.的方式
import spam
print(spam.money)
spam.read1()
spam.read2()
spam.change()
from… import …
对比import spam,会将源文件的名称空间’spam’带到当前名称空间中,使用时必须是spam.名字的方式
而from 语句相当于import,也会创建新的名称空间,但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了、
1.1 使用模块
#spam.py
print('from the spam.py')
money=1000
def read1():
print('spam->read1->money',1000)
def read2():
print('spam->read2 calling read')
read1()
def change():
global money # 经过测试这个在执行这个脚本自身的时候,global是把money的值引过来了,然后再下面进行了修改,之后再打印就是0
money=0
print(money) # 这里是有打印了下 主要 是在模块中的时候进行测试
if __name__ == '__main__':
# main()
print("测试") # 初始化执行的
print(money)
change()
print(money)
结果是:
1000
0
0
1.2 Python模块的导入
python模块在导入的时候,防止重复导入,在第一次导入后,会加载到内存,在之后的调用都是指向内存
#test.py
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam
'''
执行结果:
from the spam.py
'''
从sys.module中找到当前已经加载的模块
1.3 模块的名称空间
每一个模块都是一个独立的名称空间,定义在这个模块总的函数会把模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突
from spam import money, read1, read2,change # 可以导入多个
print(money) # 引用的是change 中的money
change() # 这个是修改了的 只是在
print(money)
'''
结果是:
1000
0
1000
'''
1.4 导入模块的做的事情
- 为源文件(spam)创建新的名称空间,在spam中定义的函数和方法使用了global时,访问的就是这个名称空间。
- 在新创建的名称空间中执行模块中包含的代码
- 创建名字spam来引用该命名空间
寻找的优先级:
'''
先从内存中寻找,sys.modules
然后从内置的寻找(内建)
从自己的路径中寻找 从sys.path中寻找
'''
2 from import
2.1
python中的变量赋值不是一种存储操作,而只是一种绑定关系
from spam import money, read1, read2, change
money = 100000000 # money现在是绑定到新的上
print(money) # 现在就有冲突了
read1()
read1 = 1111111
read2() # 这个不影响,从哪里调用,用哪里的
2.2 from spam import *
from spam import 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
通常使用all=[‘money’,’read1’],这样引用spam的就只能用money和read1两种
3 把模块当做脚本执行
文件有两种应用场景,一种是当做脚本执行,一种是当做模块
3.1 脚本执行
spam文件执行的的时候
print(__name__)
结果是:
'''
__main__
'''
3.2 模块执行
在test文件中导入import spam,打印的结果是spam
为了能够控制在不同场景下面的转换,使用了if name == ‘main‘:,当做脚本执行的时候,逻辑写到if name == ‘main‘:下面。
当做模块导入的时候,不会执行if name == ‘main‘:下面的内容。
4 模块搜索路径
总结模块查找的顺序:
内存—>内建—>sys.path
sys.path 的路径是以执行文件为基准的
在不同的路径中的调用,在dir1中调用dir2中的内容
import sys # 先导如sys模块
sys.path.append(r"D:Python_fullstack_s4day35模块dir1") # 在sys.path的路径中添加dir1的路径 r是在win平台的转义
import spam # 添加路径后再调用spam
"""
结果:
from dir1 # 这是spam中打印的内容
"""
5 编译Python文件
pyc文件是在导入模块的时候进行编译的,提高模块的导入速度,只有import/from import才能产生
提前编译
python -m compileall /module_directory 递归着编译
6 包
package是有init.py 文件的包
包的本质就是一个包含init.py文件的目录。
6.1
创建一个包的目录结构,可以把包想象成一个大的模块
glance/ #Top-level package
├── __init__.py #Initialize the glance package
├── api #Subpackage for api
│ ├── __init__.py
│ ├── policy.py
│ └── versions.py
├── cmd #Subpackage for cmd
│ ├── __init__.py
│ └── manage.py
└── db #Subpackage for db
├── __init__.py
└── models.py
文件的内容:
#文件内容
#policy.py
def get():
print('from policy.py')
#versions.py
def create_resource(conf):
print('from version.py: ',conf)
#manage.py
def main():
print('from manage.py')
#models.py
def register_models(engine):
print('from models.py: ',engine)
想要在外部是用glance-api-policy中的文件
import方法
import glance.api.plicy # 用点的方式
glance.api.plicy.get() # 通过import导入的还是要用名字的方式进行使用
'''
结果:
from policy.py # policy中的get的内容
'''
from import方法
from glance.api.plicy import get # 下面使用的时候就直接使用了
get()
6.2 小结
在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,
对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法
6.3 init.py文件
包都有init.py文件,这个文件是包初始化就会执行的文件,可以为空,也可以是初始化的代码
导入包的时候,仅仅做的就是执行init.py
from glance.api import plicy # 导入的是glance api
'''
结果:
glance 的包
init 的包
'''
下面用import *来测试,也是一样的,但是api下面的内容找不到
from glance.api import * # 这里则是导入包,执行inti,*对应的是__all__中的
'''
结果:
glance 的包
init 的包
'''
from glance.api import *
print(x)
print(y)
'''
结果:
glance 的包
init 的包
1
2
'''
'''
这是api中__init__,所以*是对应的__all__中的内容
__all__ = ["x", "y"]
x = 1
y = 2
'''
7 绝对导入和相对导入
7.1 绝对导入是从包的最开始的位置开始
从test的sys.path的列表中寻找
绝对导入的缺点是包的名字改变的话有问题
在api的init.py下面写
from glance.api import plicy
from glance.api import versions
test的使用
import glance.api # 仅仅是导入了glance.api 实际是找不到plicy的,因为导入模块仅仅是执行了__init__
print(glance.api.plicy)
'''
结果:
glance 的包
init 的包
<module 'glance.api.plicy' from 'D:\Python_fullstack_s4\day35\包\glance\api\plicy.py'>
'''
7.2 相对导入
.—当前目录
..—上一级的目录
from . import plicy,versions # 通过当前目录的方式导入,当前的目录是api
注意这种方式的init自己执行的时候会报错
导入包的执行效果
import glance.api
print(glance.api.plicy.get())
print(glance.api.versions.create_resource('aaa'))
'''
结果:
from . import plicy,versionsglance 的包
init 的包
from policy.py
None
from version.py: aaa
None
'''
8 通过包调用内部的所有的
这是glance的包中的init
from .api.plicy import get # 都是用的相对导入
from .api.versions import create_resource
from .cmd.manage import main
from .db.modules import register_models
包的定义者容易管理,对于使用者来说,使用的不知道是包还是模块
test文件调用的方式
import glance
glance.get() # 直接就能够使用
'''
glance 的包
init 的包
cmd 的包
from policy.py
'''
8 包遵循的原则
特别需要注意的是:可以用import导入内置或者第三方模块,但是要绝对避免使用import来导入自定义包的子模块,应该使用from… import …的绝对或者相对导入,且包的相对导入只能用from的形式。
包是给别人用的,是不能自己运行的。自己运行的时候会出错
9 在任意位置都能调用包
关键是在运行的的文件查找到的是当前目录的sys.path,如果在别的目录中使用的话就需要找到包的父目录
首先找到文件的绝对路径
import sys
import os
p=os.path.abspath(__file__) # 打印的是文件的绝对路径
print(p)
'''
结果:
D:Python_fullstack_s4day35包 est.py
'''
p = os.path.dirname(os.path.abspath(__file__)) # 返回的是文件的上一级目录
print(p)
'''
结果:
D:Python_fullstack_s4day35包
'''
下面的目录结构是
D:.day35
├─包
│ └─glance
│ ├─api
│ │ └─__pycache__
│ ├─cmd
│ │ └─__pycache__
│ ├─db
│ │ └─__pycache__
│ └─__pycache__
├─模块
│ ├─dir1
│ │ └─__pycache__
│ ├─dir2
│ └─__pycache__
└─练习
import sys
import os
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# sys.path.append(r"%s"%(base_path)) # 把父目录添加到sys.path中
sys.path.append(r'%s包' %(base_path)) # 拼接路径,这里拼接的是有glance的包
print(base_path)
import glance
glance.get()
'''
结果:
D:Python_fullstack_s4day35
glance 的包
init 的包
cmd 的包
'''