Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

Python中import的细节

2020/12/08 防忘系列
Word count: 2,194 | Reading time: 8min

防忘系列…

Pycharm并没有IDEA一样可以设置对import进行排序以及自动消除没用到的库(要用快捷键Ctrl + Alt + O), 并且如果导入方式不正确会 1.找不到自定义包 2.Pycharm对指定内容报错(下划线或红线)

Python中模块,包,库概念

模块:就是.py文件,里面定义了一些函数和变量,需要的时候就可以导入这些模块。

:在模块之上的概念,为了方便管理而将文件进行打包。包目录下第一个文件便是__init__.py,然后是一些模块文件和子目录,假如子目录中也有__init__.py,那么它就是这个包的子包了。

:具有相关功能模块、包的集合。这也是Python的一大特色之一,即具有强大的标准库、第三方库以及自定义模块。

标准库:就是下载安装的python里那些自带的模块,要注意的是,里面有一些模块是看不到的比如像sys模块,这与linux下的cd命令看不到是一样的情况。

第三方库:就是由其他的第三方机构,发布的具有特定功能的模块。

自定义模块:用户自己可以自行编写模块,然后使用。

总结: 我们pip install <package>安装的是库,库下有很多包,我们在自己的项目文件中import有以下几种用法:

  • import numpy as np
  • import urllib.parse import urlencode
  • from lxml import HTML

之前大家应该碰到过,命名自己pip install xx安装的是叫xx的库,但是在用的时候写的却是from yy import zz,明显不对应嘛。因此,其实库和包的区别还可以通过setup.py打包成库来看。——使用setup.py打包python文件Python实现打包成库供别的模块调用

其中比较重要的就是5.编写:setup.py

注意:setup.py与要打包的test_package包同在bricewulib根目录下。

其中比较重要的几项是:name,version,packages

  • name:描述的是你打包的文件夹名。
  • version描述的是文件的版本号。
  • packages是所有要打包的包(package),这里需要打包的是test_package包以及test_package包下的test_package。输入python setup.py sdist然后就能创建bricewulib库,使用test_package包
1
2
3
4
5
setup(
name = "bricewulib",
version = "1.0",
packages=["test_package","test_package.test_package2"]
)

import推荐顺序

我们推荐所有的模块在 Python 模块的开头部分导入。 而且最好按照这样的顺序:

  1. Python 标准库模块
  2. Python 第三方模块
  3. 应用程序自定义模块

通过空行来分割——(Python’s Style Guide), PEP8 。

import的搜索顺序:

  1. 首先判断这个module是不是built-in即内建模块,如果是则引入内建模块,如果不是则在一个称为sys.path的list中寻找
  2. sys.path在python脚本执行时动态生成,包括以下3个部分:
  • 脚本执行的位置,即当前路径
  • 环境变量中的PYTHONPATH, 即.bash_profilec.
  • 安装python时的依赖位置

Python的import机制

运行、编写Python代码时,一定要注意的是脚本路径和脚本执行路径,以及相对路径和绝对路径

如果在项目中运用了相对路径,则一定要注意脚本执行路径

假设A是B的父目录, 如我们想在A目录调用B中模块, 而B中模板是又import了A下模板, 如login: from login import ftSession命令行在A路径下输入python B/main.py时, 会报错。因为import其实就是在搜索目录下按相对路径找的。

Pycharm的坑:

借鉴: Python的import机制坑在哪?

pycharm里打出来的路径是更多的,它把项目根目录加进去了。(也可以设置: 进入设置,找到Console下的Python Console,勾选选项**“Add source roots to PYTHONPAT”** )

查看搜索路径:print(sys.path)

settings

What’s resource root?

img

以上导致的问题就是:

pycharm中,project folder是默认的source root,当你使用import语句导入模块时,Pycharm默认在project folder中寻找;所以当你在project folder下的某个subfolder导入某个同样定义在这个subfolder中的模块时,会出现导入错误。

pycharm中的第一个文件夹,即代表项目, 当在其中再次建立文件夹时,是按package处理的

如果要在这些文件夹内import其他文件,需要将完整路径导入进来

所以最终在Pycharm中导入的方式有两种: burning回答

  • 从project folder开始相对导入
  • 将module文件夹直接定义为source root

pycharm在同目录下import划红线但实际可以运行

解决方法: 右键将model所在的文件夹设置为source root

总结:python该如何import

需要明确的一点是:在运行Python脚本的时候,Python解释器会把脚本执行的位置,即当前路径加入到搜索路径中,而绝大多数情况main函数就在工程根目录,因此对于一个工程而言,最好的是从从project folder(工程根目录,即运行main函数的那个入口文件)开始相对导入

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
G:.
AI-for-Othello
│ .gitignore
│ board.py
│ config.py
│ game.py
│ main.ipynb
│ main.py
│ README.md
│ __init__.py

├─ai
│ │ mcts.py
│ │ randm.py
│ │ __init__.py
│ │
│ ├─utils
│ │ │ mct.py
│ │ │ __init__.py

在这里ai/utils中mct.py需要import board.py中的Board对象等,mct.py中import具体写法为

1
2
3
4
5
6
7
8
9
10
11
from enum import Enum
from random import choice
from time import time
from copy import deepcopy

import numpy as np


from board import Board
from config import C
# 解释一下这边为什么是直接from board, 因为我们的main函数入口是main.py文件(在工程根目录),所以他会把AI-for-Othello这个路径给加入到包的搜索路径中,所以此时我们import就是以这个路径(工程根目录)开始相对导入

▲需要注意的是,对于main函数如果不在根目录,而是在工程目录的子目录中的情况,需要在main.py中自己手动添加sys.path.append(".."),即想办法把工程根目录加到搜索目录中,这样以后就可以又开始从工程根目录开始import了。

还有一个对于初学者可能会产生自我怀疑的点,我也顺便提一下好了

1
2
3
from config import *
// 等价于
from .config import *

__init__文件作用

在Python工程里,当python检测到一个目录下存在__init__.py文件时,python就会把这个目录当成一个包(package)。Module跟C++的命名空间和Java的Package的概念很像,都是为了科学地组织化工程,管理命名空间。

__init__.py的设计原则

__init__.py的原始使命是声明一个模块,所以它可以是一个空文件。

A、不要污染现有的命名空间。模块一个目的,是为了避免命名冲突,如果你在种用__init__.py时违背这个原则,是反其道而为之,就没有必要使用模块了。

B、利用__init__.py外提供类型、变量和接口,对用户隐藏各个子模块的实现。一个模块的实现可能非常复杂,你需要用很多个文件,甚至很多子模块来实现,但用户可能只需要知道一个类型和接口。就像我们的arithmetic例子中,用户只需要知道四则运算有add、sub、mul、dev四个接口,却并不需要知道它们是怎么实现的,也不想去了解arithmetic中是如何组织各个子模块的。由于各个子模块的实现有可能非常复杂,而对外提供的类型和接口有可能非常的简单,我们就可以通过这个方式来对用户隐藏实现,同时提供非常方便的使用。

C、只在__init__.py中导入有必要的内容,不要做没必要的运算。像我们的例子,import arithmetic语句会执行__ini__.py中的所有代码。如果我们在__init__.py中做太多事情,每次import都会有额外的运算,会造成没有必要的开销。一句话,init.py只是为了达到B中所表述的目的,其它事情就不要做啦。

上述来自: https://zhuanlan.zhihu.com/p/115350758

__all__变量

__all__是一个字符串list;

约束作用:用来定义模块中对于from XXX import *时要对外导出的符号,即要暴露的借口,但它只对import *起作用(即如果在使用脚本中对当前包使用import *,那么可以管理当前包下模块的导入情况),对from XXX import XXX不起作用。

Author: Mrli

Link: https://nymrli.top/2020/10/23/Python中import的细节/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
范数的概念_MATLAB使用
NextPost >
emplace_back与push_back
CATALOG
  1. 1. Python中模块,包,库概念
  2. 2. import推荐顺序
  3. 3. import的搜索顺序:
  4. 4. Python的import机制
    1. 4.1. Pycharm的坑:
    2. 4.2. 总结:python该如何import
  5. 5. __init__文件作用
    1. 5.1. __init__.py的设计原则
    2. 5.2. __all__变量