Python核心技术与实战学习笔记(八):Python模块化

8. Python模块化

  • 8.1 简单模块化
    • 同一文件夹下
    • 不同文件夹下
  • 8.2 项目模块化
    • 项目中如何设置模块的路径
  • 8.3 if \__name\__ == '\__main\__'

8.1 简单模块化

同一文件夹下

简单模块化的方式,如将函数,类,常量拆分到不同的文件中,再放到同一个文件夹下,然后再import调用,就可以在文件内调用这些函数和类了。

  1. 将get_sum函数放到一个文件中
# utils.py

def get_sum(a, b):
    return a + b
  1. 将类单独放到一个文件里
# class_utils.py

class Encoder(object):
    def encode(self, s):
        return s[::-1]

class Decoder(object):
    def decode(self, s):
        return ''.join(reversed(list(s)))
  1. 同文件夹下创建文件进行函数和类的调用
# main.py

from utils import get_sum
from class_utils import *

print(get_sum(1, 2))

encoder = Encoder()
decoder = Decoder()

print(encoder.encode('abcde'))
print(decoder.decode('edcba'))

########## 输出 ##########

3
edcba
abcde

不同文件夹下

文件结构如下:

.
├── utils
│   ├── utils.py
│   └── class_utils.py
├── src
│   └── sub_main.py
└── main.py
# utils/utils.py

def get_sum(a, b):
    return a + b
# utils/class_utils.py

class Encoder(object):
    def encode(self, s):
        return s[::-1]

class Decoder(object):
    def decode(self, s):
        return ''.join(reversed(list(s)))
# src/sub_main.py

import sys
sys.path.append("..")   # 将程序位置向上提了一级

from utils.class_utils import *

encoder = Encoder()
decoder = Decoder()

print(encoder.encode('abcde'))
print(decoder.decode('edcba'))

########## 输出 ##########

edcba
abcde

在sub_main.py中,调用上层目录:sys.path.append( " . . " ".." "..")表示将当前程序所在位置向上提了一级,之后就能调用utils的模块

8.2 项目模块化

这里有两个概念:

  • 绝对路径:在 Linux 系统中,每个文件都有一个绝对路径,以 / 开头,来表示从根目录到叶子节点的路径,例如/home/ubuntu/Desktop/my_project/test.py
  • 相对路径:对于任意两个文件,我们都有一条通路可以从一个文件走到另一个文件,例如 /home/ubuntu/Downloads/example.json。再如,我们从 test.py 访问到 example.json,需要写成 ‘…/…/Downloads/example.json’,其中… 表示上一层目录。这种表示方法,叫作相对路径

一个python文件在运行时,都会有一个运行时的位置,最开始即为这个文件所在的文件夹。这个位置也可以被改变,如8.1中调用上层目录使用了sys.path.append( " . . " ".." ".."),就是将当前python解释器的位置向上提了一级。但是这种用法并不推荐,固定一个确定路径对大型工程来说是非常必要的

项目中如何设置模块的路径

相对路径是一种很不好的选择,因为代码可能会迁移,这样就会出错。因此,在大型工程中尽可能使用绝对位置是第一要义

对于一个项目,所有模块的追寻方式,最好从项目的根目录开始追溯,这叫相对的绝对路径。也就是说,以项目的根目录作为最基本的目录,所有的模块调用,都要通过根目录一层层向下索引的方式来import

如,在pycharm中创建一个项目,项目结构如下:

.
├── proto
│   ├── mat.py
├── utils
│   └── mat_mul.py
└── src
    └── main.py
# proto/mat.py

class Matrix(object):
    def __init__(self, data):
        self.data = data
        self.n = len(data)
        self.m = len(data[0])
# utils/mat_mul.py

# 直接从proto里导入,这是由于pycharm自动添加项目根目录的绝对路径
from proto.mat import Matrix

def mat_mul(matrix_1: Matrix, matrix_2: Matrix):
    assert matrix_1.m == matrix_2.n
    n, m, s = matrix_1.n, matrix_1.m, matrix_2.m
    result = [[0 for _ in range(n)] for _ in range(s)]
    for i in range(n):
        for j in range(s):
            for k in range(m):
                result[i][k] += matrix_1.data[i][j] * matrix_2.data[j][k]

    return Matrix(result)
# src/main.py

from proto.mat import Matrix
from utils.mat_mul import mat_mul

a = Matrix([[1, 2], [3, 4]])
b = Matrix([[5, 6], [7, 8]])

print(mat_mul(a, b).data)

########## 输出 ##########

[[19, 22], [43, 50]]

在上面的代码中,import Matrix的方式是from proto.mat。这种做法直接从项目根目录中导入,并依次向下导入模块mat.py中的Matrix,而不是用sys.path.append(" . . .. ..")的方式导入上一级文件夹。

也就是说,对于pycharm中构建的项目,把不同的模块放在不同的子文件夹里,跨模块调用则是从顶层直接索引,十分方便。

之所以能够这么做,是因为当python解释器在遇到import语句时,会去一个列表中搜寻模块,该列表可这样查看到:

import sys  

print(sys.path)

########## 输出 ##########

['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']

pycharm会自动将该列表的第一个参数(原本为空‘’)设置为项目根目录的绝对路径。这样,无论什么位置的程序,只要在执行到import时都会从项目的根目录中开始去寻找相应的包

8.3 if _name_ == ‘_main_

Python是脚本语言,和C++、Java最大的不同在于不需要显示提供main()函数入口

那么,if _name_ == '_main_'这样的写法的好处在于什么呢?

对于一个项目,其结构如下:

.
├── utils.py
├── utils_with_main.py
├── main.py
└── main_2.py
# utils.py

def get_sum(a, b):
    return a + b

print('testing')
print('{} + {} = {}'.format(1, 2, get_sum(1, 2)))
# utils_with_main.py

def get_sum(a, b):
    return a + b

if __name__ == '__main__':
    print('testing')
    print('{} + {} = {}'.format(1, 2, get_sum(1, 2)))
# main.py

from utils import get_sum

print('get_sum: ', get_sum(1, 2))

########## 输出 ##########

testing
1 + 2 = 3
get_sum: 3
# main_2.py

from utils_with_main import get_sum

print('get_sum: ', get_sum(1, 2))

########## 输出 ##########

get_sum_2: 3

import在导入文件时,会自动把所有暴露在外面的代码重新执行一遍(如# main.py中所示)。因此,若要把一个东西封装成模块,又想让它可执行的话,必须将要执行的代码放在if _name_ == '_main_'里面。

这是因为,__name__作为Python的魔术内置参数,本质上是一个模块对象的属性。当使用import导入一个模块时,此时__name__就会被赋值为该导入模块的名字,自然就不等于__main__了,故不会执行if _name_ == '_main_'里的操作。

也就是说,在运用import导入时,为了避免自动执行该模块中的一些语句,可以将这些语句放入if _name_ == '_main_'里面。

你可能感兴趣的:(python,模块,python项目模块)