简单模块化的方式,如将函数,类,常量拆分到不同的文件中,再放到同一个文件夹下,然后再import调用,就可以在文件内调用这些函数和类了。
# utils.py
def get_sum(a, b):
return a + b
# 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)))
# 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的模块
这里有两个概念:
一个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时都会从项目的根目录中开始去寻找相应的包
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_'里面。