python import 文件夹_python模块详解

67bbc6d8bc84dbb807ecca81c917881e.png

使用python时,常常会涉及到库的调用,这就需要掌握模块的基本知识。

本文分为如下几个部分

  • 概念说明
  • 模块的简单调用
  • 包的导入
  • 特殊的__init__.py文件
  • 导入模块的搜索路径
  • __all__
  • 绝对引用与相对引用
  • import运行本质
  • if __name__ == '__main__'

概念说明

这里厘清python中模块、库、包之间的概念差异

  • 模块(module)其实就是py文件,里面定义了一些函数、类、变量等
  • 包(package)是多个模块的聚合体形成的文件夹,里面可以是多个py文件,也可以嵌套文件夹
  • 库是参考其他编程语言的说法,是指完成一定功能的代码集合,在python中的形式就是模块和包

模块的简单调用

比如我们有一个trymodule的文件夹,里面有一个first.py文件,文件中的内容如下

a = 1

def myfun(s):
    print(s + 1)

trymodule的文件夹下打开命令行窗口(按住shift单击鼠标右键,选择“在此处打开命令窗口”),输入python进入命令行交互模式

>>> import first
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> first.a
1
>>> first.myfun(2)
3

所以说first.py文件就是一个模块,可以用import导入,里面变量都要用first.前缀来引用,如果想不使用这个前缀可以这样

>>> from first import a
>>> a
1

其他用法如下

# 重命名
>>> from first import myfun as addone
>>> addone(4)
5

# 导入模块中全部变量
>>> from first import *
>>> myfun(2)
3

# 一次导入多个变量
>>> from first import a, myfun
>>> a
1

包的导入

在trymodule文件夹中新建folder1文件夹,我们想让folder1文件夹成为一个包。文件夹里新建abcd.py文件,文件中内容如下

b = 2

class Myclass:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_info(self):
        print('my name is {name} and age is {age}'.format(name = self.name, age = self.age))

此时在folder1文件夹中新建一个__init__.py文件,否则程序会将这个文件夹当成普通文件夹来处理而不是一个包。这个__init__.py文件中可以什么都不填。

注:python3.3+已经可以省略掉空白的__init__.py 文件。

此时文件结构如下

trymodule
│   first.py
├───folder1
│   │   abcd.py
│   │   __init__.py

我们还是在trymodule文件夹下打开命令行,进入python交互模式

我们来看一下下面几种导入方式

>>> import folder1
>>> folder1.abcd.b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'folder1' has no attribute 'abcd'

>>> from folder1 import abcd
>>> bob = abcd.Myclass(name = 'Bob', age = 20)
>>> bob.name
'Bob'
>>> bob.get_info()
my name is Bob and age is 20

>>> from folder1.abcd import b
>>> b
2

>>> import folder1.abcd
>>> abcd.b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'abcd' is not defined
>>> folder1.abcd.b
2

>>> import folder1.abcd as aa
>>> aa.b
2

注意:

  • 只是导入包不能随便使用其中的模块,要导入到具体模块或者变量的层次
  • 文件夹与文件之间可以用.也可以用from import格式,而文件与里面的变量之间只能用from import格式,即不能import folder1.abcd.b

特殊的__init__.py文件

__init__.py文件其实是一个特殊的文件,它相当于名为folder1模块,即如果使用import folder1则可以调用在__init__.py文件文件中定义的变量。

__init__.py文件编写如下

from folder1.abcd import b

c = 3

在trymodule文件夹下打开命令行,进入python交互模式

>>> import folder1
>>> folder1.c
3
>>> folder1.b
2
>>> from folder1 import b
>>> b
2

对比之前的from folder1.abcd import b,使用__init__.py文件可以将常用的一些变量、函数、甚至模块导入以方便调用。

导入模块的搜索路径

import hello时,python会搜寻hello.py文件,搜索顺序如下

  • 首先搜寻内置模块是否有hello(所以我们定义的模块名不要和内置模块相同)
  • 如果内置模块没有,则看下面这些目录里有没有
>>> import sys
>>> sys.path
['', 'C:Program FilesAnaconda3python35.zip', 'C:Program FilesAnaconda3DLLs', 'C:Program FilesAnaconda3lib', 'C:Program FilesAnaconda3', 'C:Program FilesAnaconda3libsite-packages', 'C:Program FilesAnaconda3libsite-packagesSphinx-1.4.6-py3.5.egg', 'C:Program FilesAnaconda3libsite-packagessnownlp-0.12.3-py3.5.egg', 'C:Program FilesAnaconda3libsite-packageswin32', 'C:Program FilesAnaconda3libsite-packageswin32lib', 'C:Program FilesAnaconda3libsite-packagesPythonwin', 'C:Program FilesAnaconda3libsite-packagessetuptools-27.2.0-py3.5.egg']

其中第一个''表示当前的工作路径,我们可以看出安装的第三方包所在路径('C:Program FilesAnaconda3libsite-packages')也在这个列表之中,所以无论工作路径在哪里,都能搜寻到这些包。

如果想添加搜索路径,可以参考这篇文章

__all__

首先要明确,import *的方式无法导入以下划线开头的变量名

__init__.py文件内容更改如下

from folder1.abcd import b

c = 3
_e = 4

python交互模式下

>>> from folder1 import *
>>> c
3
>>> _e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_e' is not defined

而如果指定导入是可以的

>>> from folder1 import c
>>> c
3
>>> from folder1 import _e
>>> _e
4

如果定义了__all__,则import *就可以导入下划线开头的变量

__init__.py文件内容更改如下

from folder1.abcd import b

__all__ = ['c', '_e']

c = b + 1
_e = 4

python交互模式下

>>> from folder1 import *
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> c
3
>>> _e
4

可见import *只会导入__all__中指定的变量,无论是否以下划线开头。这样限制可以防止import *命令导入太多变量污染命名空间,过滤掉一些中间变量如b

绝对引用与相对引用

python中的import分为绝对引用和相对引用两种。它们之间的差异在于,引用模块时 定位被引用模块位置 的方式不同

  • 绝对引用是通过.的连接,指定出最高级文件(夹),到目标文件的绝对路径。我们上面的所有用法都属于绝对引用。
  • 而相对引用是 指定待引用模块与当前文件的相对位置,.表示上一级文件

绝对引用类似这样from folder.abcd import myclass

相对引用类似这样from .abcd import myclass

在实际使用中,无论是绝对导入还是相对导入都要注意,如何导入与被调用位置有关。

以下面的文件结构为例

folder1
    │  first1.py
    │  first2.py
    └─folder2
         |  second1.py
         |  second2.py
    └─folder3
         |  third1.py
         |  third2.py

下面分为两种情况
1.folder1是一个项目,运行这个项目需要在folder1文件夹中打开cmd,运行python first1.py

# first1.py
from first2 import sth          # OK
from folder1.first2 import sth  # ERROR
from .first2 import sth         # ERROR

from folder2.second1 import sth # OK

# second1.py
from folder2.second2 import sth # OK
from .second2 import sth        # OK
from second2 import sth         # ERROR

(注意first1引用first2 与 second1引用second2 的区别)

2. folder1是一个包,一般使用时,是在外面的文件夹中import这个包

# first1.py
from folder1.first2 import sth   # OK
from .first2 import sth          # OK
from first2 import sth           # ERROR
from . import first2             # OK
import .first2                   # ERROR

from folder1.folder2 import second1  # OK
from .folder2 import second1         # OK


# second1.py
from folder1.folder2 import second2  # OK
from . import second2                # OK
from .. import first1                # OK
from ..folder3 import third1         # OK

规律

  • 绝对引用就是从包的位置一直往下,写到目标文件位置
  • 相对引用,用.代替当前文件所在目录,用..代替再上一级目录,用点来回溯,之后往下写到目标文件位置。
  • .只能放在from后,不能放import后。

相对引用的好处在于,代码中不需要出现folder1这个名称,因此当包的名称改变时,里面代码不需要做任何改变;或者有的用户要装两个版本的包,把其中一个版本的包重命名了,此时使用相对引用也不会出问题。

另外,以second1.py为例,如果其中有一些相对引用的代码,在folder1的上一层文件夹调用

python folder1/folder2/second1.py

这时相当于在folder2文件夹中调用second1.py文件,会报错。但如果像下面这样就可以正常运行

python -m folder1.folder2.second1

参考python cookbook

import运行本质

使用import语句,要明确两件事

(1)执行导入模块命令时,会首先检查待导入的模块是否在当前已有模块之中,如果有则跳过import。因此模块之间相互引用不会导致无限循环。

查看当前已导入模块使用下面方法

import sys
sys.modules

得到结果是一个字典,键是模块名,值是文件所在路径

(2)import语句与文件执行

在这样的文件结构下

trymodule
│   first.py
├───folder1
│   │   abcd.py
│   │   __init__.py

folder1是一个package,abcd是一个module

  • import folder1 只是导入package,相当于执行__init__.py文件
  • from folder import abcd则执行了__init__.py文件文件与abcd.py文件
  • from folder1.abcd import b其实也执行了__init__.py文件文件与abcd.py文件

(要知道执行了什么,可以在这些文件之中添加print语句,看是否打印出结果)

知道这个执行原理,可以更好理解前面得到的一些结论

  • 首先是在__init__.py文件中什么都没有的情况下,import folder1无法调用abcd模块中的变量,是因为相当与运行了一个空文件,没有将整个包导入工作空间
  • abcd模块中定义了print语句后,import两次,只有第一次会print出值,说明第二次检查出模块已在导入之列,忽略了这条import命令

一个例子

  • import numpy as np,之后可以这样调用np.random.normal()
  • import skimage,不可以调用skimage.io.imread(),要import skimage.io才可以
  • 这是因为numpy文件夹中的_init_.py文件中有一句from . import random;而skimage没有

更多运行细节可以参考这篇文章

if __name__ == '__main__'

我们经常会在别人的代码中发现if __name__ == '__main__',为了理解它的作用,我们来看下面的例子

在folder1文件夹下新建new.py文件,里面内容为

print(__name__)

在folder1文件夹下打开命令行,输入

python new.py

返回结果是__main__

在trymodule文件夹下打开命令行,进入python交互模式

>>> from folder1 import new
folder1.new

>>> print(__name__)
__main__
>>> print(new.__name__)
folder1.new

上面测试结果说明直接运行文件和import文件是有差异的,差异在于二者的__name__变量不同。__name__变量是一个特殊的变量,每个py文件运行时都会对应一个__name__变量,即使在交互模式下也可以查看这个变量值。

所以if __name__ == '__main__'的作用就很好理解了,即import时不执行下面的代码,只有在直接执行这个文件时才运行之后的代码。这算是一种约定俗成的写法,如果不怕文件被import,可以不用这个。

专栏信息

专栏主页:python编程

专栏目录:目录

版本说明:软件及包版本说明


http://www.niftyadmin.cn/n/637250.html

相关文章

python tqdm_有没有一些让你相见恨晚的Python库?

作者 | Roc来源 | 数据札记倌(ID:Data_Groom)欢迎客官光临&#xff0c;这里主要给大家介绍一下pandas-profiling库&#xff0c;一行代码&#xff0c;生成超详细的数据分析报告&#xff0c;数据分析师的福音。主菜之前先送两个小菜给大家&#xff5e;1、爬虫的吸星大法&#xff…

spring中如何实现参数隐式传递_Python 中 staticmethod 和 classmethod 原理探究

起步文章 《Python中 property 的实现原理及实现纯 Python 版》 中探究了 property 的实现原理。如果能理解那边描述符的使用方式&#xff0c;那也能很快理解本篇中的 staticmethod 和 classmethod 。函数与方法对于类中定义的方法来说&#xff0c;通过类来调用与实例调用是不一…

用c语言写电话薄管理系统,求助 哈稀表编电话薄程序(c语言) 算法

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼还是俺自己来回答吧&#xff0c;希望对看帖子的你有用(俺自己琢磨一个月研究出来地o(∩_∩)o...)&#xff1a;#include #include "string.h"#include "fstream"#define NULL 0unsigned int key;int *p;struct …

字典序最小是什么意思_时光序app - 时光序app手机软件下载 - 官方版

时光序app是一款非常简单方便记录时间的管理软件。看到时光序就觉得这意思就非常文艺范&#xff0c;让用户在使用这款软件的时候会有一种文艺青年的感觉&#xff0c;管理自己每一天的工作任务和生活流程&#xff0c;让自己通过软件获得一点闲余之时&#xff0c;来从生活中获得一…

vue-cli 打包出来的文件缺少_PyQt5打包运行: unable to find Qt5Core.dll on PATH 解决方案...

一、背景最近一个项目采用PyQt5 5.13.0 进行开发&#xff0c;经过打包成exe可以执行。 环境&#xff1a;Windows 10 python 3.7 PyQt5 5.13.0 tensorflow 1.14.0等。但是在此之前运行时会有这么一个错误&#xff1a;ImportError: unable to find Qt5Core.dll on PATH5.12.3 …

c语言A 100 开头地址,2009C语言A.doc

华南农业大学期末考试试卷(A卷)2008学年第 2 学期考试科目&#xff1a; 程序设计考试类型&#xff1a;(闭卷)考试考试时间&#xff1a; 120  分钟学号 姓名 年级专业注意&#xff1a;1&#xff0e;本次考试由三部分组成&#xff1a;试卷、机读卡、答卷纸2&#xff0e;本试卷…

Oracle集合的基本用法

2019独角兽企业重金招聘Python工程师标准>>> --关联数组 declare type list_of_ctrys is table of t_mt_ctry.cd%type index by pls_integer; ctrys list_of_ctrys; l_row pls_integer; beginctrys(10) : CN; ctrys(1000) : US; ctrys(-10) : JP; ctrys(50) : MY;l_…

sqlyong导出sql没有数据_我用起来顺手的数据库设计工具,这次推荐给大家

摘要好的数据库设计工具&#xff0c;可以帮助我们进行思考并提高我们的设计效率。以前一直使用的是PowerDesigner&#xff0c;最近发现Navicat的数据库设计功能也很不错&#xff0c;界面简洁且容易使用&#xff0c;特此推荐给大家。NavicatNavicat是一套快速、可靠的数据库管理…