markdown Python模块(模块化)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown Python模块(模块化)相关的知识,希望对你有一定的参考价值。

参考资料: <<Python Cookbook第三版>> 第10章

代码库: 详见python-snippets仓库

## 关于module和package的理解

> .py是一个script, 当用-m 和包名的方式运行的时候, 是一个module, 若在某个文件夹层级有\_\_init\_\_.py文件, 那么这个文件夹就是一个package. 可以通过package name进行导入. 实际上一个package就是一个大的module, 一个package相当于将一个module拆分成了多个代码文件.

module是和命名空间\(namespace\)关联的, 一个module对应一个命名空间. 如果要查看命名空间, 直接`print(module_name)`或者`print(package_name)`.


```bash
graphics/
  __init__.py 
  primitive/ 
    __init__.py 
    line.py
  formats/
    __init__.py 
    png.py 
    jpg.py
```

以上述层级组织后, 就可以进行如下导入了

```python
import graphics.primitive.line 
from graphics.primitive import line 
import graphics.formats.jpg as jpg
```

导入的时候, 上级的init.py会顺次先于最终导入的文件进行初始化操作.

## 关于\_\_all\_\_变量

\_\_all\_\_变量是限制了`from <somemodule or package> import *`这种导入方式的导入成员, 实际上是为了最大限度减少命名空间的名称冲突, 但是`from somemodule import concrete_name`这种不会受到影响, 而且`import somemodule`这种的也不会受到影响.

[参考链接1](https://blog.csdn.net/orangleliu/article/details/49848413)

[参考链接2](https://blog.csdn.net/nivana999/article/details/39620673)

### 使用相对引入

注意只有from语句生效, 只有package组织形式的生效. 使用相对引入的好处是避免了在代码中对包名的硬编码.

当使用了相对引入的时候, 必须把.py当成模块执行才会生效, 当成脚本来执行会fail, 当成模块来执行就是加上\-m参数. 一个例子:

```python
% python3 mypackage/A/spam.py # Relative imports fail
% python3 -m mypackage.A.spam # Relative imports work
```

## 将module拆分成多个文件, 作为package存在

> 当一个module膨胀的时候, 需要拆分代码, 然后作为一个package的形式存在.

思考: 实际上package(文件夹)都是先以module(py文件)的形式存在, 当代码逐渐增多的时候, module就会升级为package, 实际上从用户层面来看, 是无法看出package和module的区别的.

例子:

拆分前源代码:

```python
# mymodule.py

class A:
  def spam(self):
    print('A.spam')

class B(A):
  def bar(self):
    print('B.bar')
```

拆分后代码:

```python
# 目录结构
mymodule/
  __init__.py
  a.py
  b.py
  
# a.py

class A:
  def spam(self):
    print('A.spam')
    
# b.py

from .a import A

class B(A):
  def bar(self):
    print('B.bar')
    
# __init__.py

from .a import A 
from .b import B
```

## namespace package

> 与普通的package不同的是, 在top level package不要加\_\_init\_\_.py文件即可. 通常在大框架或者需要多人开发plug\-in或者add\-on的时候需要这种package

```bash
# 层级结构, foo和bar是不同人维护的
foo-package/
  spam/ 
    blah.py

bar-package/
  spam/ 
    grok.py
```

使用:

```python
import sys

# 注意这样执行的时候要保证pwd为脚本所在路径, 才能使用相对路径
sys.path.extend(['foo-package', 'bar-package'])

import spam.grok
import spam.blah
import pyspark

# print(spam.__file__)    # 输出:AttributeError: 'module' object has no attribute '__file__'
print(spam)  # 输出:<module 'spam' (namespace)>
print(sys) # 输出:<module 'sys' (built-in)>
print(pyspark) # 输出:<module 'pyspark' from '/usr/local/lib/python3.6/site-packages/pyspark/__init__.py'>
```

## Python 打包为可执行文件

> 如果只是简单的想把几个脚本组合在一起交付给某人使用, 并不是为了作为一个standard library module来使用, 那么无需创建一个package, 直接放到文件夹打包并放置一个\-\_main\_\_.py文件即可.

```bash
# 层级结构
myapplication/
  spam.py 
  bar.py 
  grok.py 
  __main__.py
  
# 运行
bash % python3 myapplication

# 打包运行
bash % ls spam.py bar.py grok.py 
bash % zip -r myapp.zip *.py 
bash % python3 myapp.zip 
... output from __main__.py ...
```

以上是关于markdown Python模块(模块化)的主要内容,如果未能解决你的问题,请参考以下文章

markdown 与Python相关的模块和指南。

markdown Python模块安装

markdown ImportError:没有名为'_tkinter'的模块,请安装python3-tk包

markdown JavaScript的模块化编程

Python3基础 import...as 给导入的模块起别名

Python3基础 __file__ 查询模块的完整路径