Python模块和包的简单记录及使用,新增体会__file__,__module__,__package__,__all__,__path__,sys.modules,sys.path。
对于包于模块的定义:http://www.imooc.com/article/247114这里写的还是不错的。
这个参考链接也很不错,讲的很清楚了为什么相对路径不能直接用脚本的形式执行。
https://www.jianshu.com/p/04bac02ae3f0
以前用Python的标准库import XXX
或者from xxx import xxx
其实具体里面的使用方法于技巧没有系统性的学习过,一直认为能用就ok了
刚到了Python cookbook里面有相对来说比较详细的介绍,我挑一些简单的给自己一些记录。
首先Python有Python的环境变量,我们能够从外部from或者import的都在Python的环境变量里面,下面是我的IPython环境变量。
In [5]: sys.path Out[5]: ['/usr/local/bin', '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '', '/usr/local/lib/python3.7/site-packages', '/usr/local/lib/python3.7/site-packages/IPython/extensions', '/Users/shijianzhong/.ipython']
Python有一个很有意思的tip,就是当你运行某个文件,那个文件的文件夹就会加入PythonPATH里面。
shijianzhongdeMacBook-Pro:formats shijianzhong$ python3 jpg.py ['/Users/shijianzhong/study/PythonCookbook/charter_10/t_10_1_2/graphics/formats',
'/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/site-packages'] shijianzhongdeMacBook-Pro:formats shijianzhong$ pwd /Users/shijianzhong/study/PythonCookbook/charter_10/t_10_1_2/graphics/formats
这是我的一个比较里面的文件,他还是把自己的文件夹(这是一个顶层的文件夹)当做包,放入了Python环境变量中。
这个文件夹下面所有的包以及模块都能够被导入。
我用Pycharm新建了一个工作文件,Pycharm默认的设置会把这个目录添加到Python工作环境中,我的是
/Users/shijianzhong/study/PythonCookbook/charter_10/t_10_1_2
下面的文件内容为:
└── graphics ├── __init__.py ├── __pycache__ │ └── __init__.cpython-37.pyc ├── formats │ ├── __init__.py │ ├── jpg.py │ └── png.py └── primitive ├── __init__.py ├── __pycache__ │ └── __init__.cpython-37.pyc ├── fill.py ├── line.py └── text.py
首先我准备通过这个事例来简单的说明一下。
In [1]: import sys In [2]: sys.path Out[2]: ['/usr/local/bin', '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '', '/usr/local/lib/python3.7/site-packages', '/usr/local/lib/python3.7/site-packages/IPython/extensions', '/Users/shijianzhong/.ipython']
很明显,ipython的python环境里面没有我刚才新建的工作路径目录。
In [7]: cd ~ /Users/shijianzhong In [8]: from graphics import primitive --------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) <ipython-input-8-e5e5ddc40003> in <module> ----> 1 from graphics import primitive ModuleNotFoundError: No module named 'graphics'
很明显,因为没有这个环境的路径,导包失败。那就添加路径:
In [9]: sys.path.append('/Users/shijianzhong/study/PythonCookbook/charter_10/t_10_1_2') In [10]: from graphics import primitive In [11]: primitive Out[11]: <module 'graphics.primitive' from '/Users/shijianzhong/study/PythonCookbook/charter_10/t_10_1_2/graphics/primitive/__init__.py'>
添加了环境变量的路径,这个工作目录下面的所有的模块于包都能只能导入。
为了测试是否能导入模块,我新建了一个文件在顶层目录下(t.py):
. ├── __pycache__ │ └── t.cpython-37.pyc ├── graphics │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-37.pyc │ ├── formats │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ └── __init__.cpython-37.pyc │ │ ├── jpg.py │ │ └── png.py │ └── primitive │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-37.pyc │ ├── fill.py │ ├── line.py │ └── text.py └── t.py
In [4]: import t I am t
导入模块后,直接输出里面的内容,说明模块已经运行了。
从这里可以说明,一般情况下,我的主运行文件应该建立在顶层,这样的话,在运行这个文件的时候,能够正确的把这个脚本的工作环境添加到Python环境中。
避免后期的一些导包错误(可能是绝对路径的导包错误)。
在这样的情况下,我可以通过顶层包,非常轻松的导入模块
有了这个,后面能够更好的理解绝对路径导入模块。
在我们对一个包的文件夹进行导入的时候,其实导入的是__init__文件,每次都会运行里面的文件内容。
from graphics.formats import jpg
I am graphics package I am formats package
当我导入graphics包下面的formats包下面的jpg模块,这两个包里面的__init__全部执行了。
这个__init__文件可以方便的让你通过包,管理包里面的模块,只需要通过相对路径from . import xxx
同级别的木块就会导入这个包内,
后期导入这个包可以通过包名字.xxx调用这个模块。
包于模块的使用还是非常丰富的,Pythoncookbook书中介绍比较详细,自己也前面简单的看了一些,难的等以后用到再补充。
2020年10月25日
最近在看一个框架的源码,遇到了很多关于包,模块,以及模块的属性的一些方法。按照自己的理解写一下。
什么是包,Python的一个.py文件就是一个模块,包其实也是一个模块,只不过模块是一个py文件,包是在顶层目录下的一个带有__init__的文件夹
import MyPackage from directory1 import file1 print(MyPackage.__class__) # print(vars(MyPackage)) # 当存在__init__文件的时候的输出__init__文件的路径 # 当包只不过是一个文件夹的时候,没有输出 print(MyPackage.__file__) print(file1.__class__) print(file1.__file__)
这是两次分别导入包与模块:
输出
<class 'module'> /Users/shijianzhong/study/PythonCookbook/chapter_10/completion_study/MyPackage/__init__.py <class 'module'> /Users/shijianzhong/study/PythonCookbook/chapter_10/completion_study/directory1/file1.py
从输出来看,类型都输出module类型,也就是模块,按照我现在来看,也只能看文件名称,如果是最后是__init__,所以这是一个包文件。
__file__,首先来说明这个。每一个模块与包,都带有这个属性。
在模块中__file__的输出,在倒包模式下,导入的包或者模块,在__file__的属性输出都是该包或者模块的绝对路径。
在模块中直接输出__file__,当你在编辑器中运行的时候【我使用的是PyCharm】,输出的是模块的绝对路径,也就是模块路径。但这个没用。
当你通过命令行模式运行的时候,输出是相对路径,是相对与命令行运行处的位置的相对位置。所以当你需要定位绝对路径的时候,需要使用os.path.abspath
第二个属性是__module__,每个函数与类都有这个属性,有些类的实例也有,有些没有,但我查不到资料是为什么。
一般我们自定义的类的实例,还是能够输出__module__属性的信息。
# 导入包 import MyPackage # 导入包内部的模块 from directory1 import file1 print(MyPackage.My_Dir.__module__) my_package = MyPackage.My_Dir() print(my_package.__module__) print('_'*100) # 通过模块内部的函数输出他的模块的性质 # 通过.的形式代表文件的路径层层关系 print(file1.func.__module__) import time print(time.time.__module__)
输出
MyPackage MyPackage ____________________________________________________________________________________________________ directory1.file1 time
忘了一个本模块下函数在模块下执行执行的输出
def mm(): ... print(mm.__module__)
输出
__main__
如果当外部调用的时候,输出还是相对与项目目录下的路径。
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- class Demo: ... def func(): ... name = 'a gou' print(func.__module__,'This is file1 func module')
该模块是项目directory1/file1,上面的代码在这个模块中,当别人导入这个模块输出
directory1.file1 This is file1 func module
__module__在框架设计的时候还是很有用的,通过一些另外模块传进来的函数,通过调用该属性可以输出自己所属模块的名称。
再通过sys.modules的字典中,通过这个key找出具体的module。
__package__是我自己测试来看,是返回一个模块【包】的包目录。
这个属性只有模块【包】才有,类与函数是没有的。
from directory1.init1 import init_file import directory1 from directory1 import file1 from directory1.file1 import Demo print(directory1.__package__) print(file1.__package__) print(init_file.__package__) print(Demo.__package__)
输出
Traceback (most recent call last): File "/Users/shijianzhong/study/PythonCookbook/chapter_10/completion_study/test_package.py", line 17, in <module> print(Demo.__package__) AttributeError: type object 'Demo' has no attribute '__package__' directory1 directory1 directory1.init1
通过输出可以看到,输出的包的信息是根据运行脚本的位置的相对路径绝对的,路径的层级通过.来处理。顶层的模块__package__输出是None
__all__相对比较简单
在导入一个模块或者包的时候,通过*导入的时候,__all__能够进行筛选。
具体写法__all__=[能被导入的名称,]
但通过具体模块的内属性的名称进行导入__all__就无能能力了。
__path__后续写,
sys.modules是一个字典,一个所有已经被导入的模块的信息的字典。
keys模块的名称,values就是具体的模块对象。
import sys print(sys.modules) print(sys.modules['directory1.file1'].name) sys.modules['time'].sleep(5)
还是前面那些代码,完全可以通过sys.modules的key取出具体对象。
最后一个是sys.path
这也是我在项目进行中,碰到的一个实际操作,当你需要将项目文件中具体的某个文件夹直接可以通过倒包的方式导入,你需要将该路径添加进sys.path之中。
sys.path.append(xxx)
这样的话xxx目录之下的所有py文件,你可以通过
import importlib xoab = importlib.import_module('xoab')
这样的方式读取指定模块【包】
简单的先写到这里__path__真的不知道咋用,就大概了解,一个包去除了__init__会有一个__path__的属性。后续使用再完善吧