Python核心技术与实战——十一|程序的模块化
我们现在已经总结了Python的基本招式和套路,现在可以写一些不那么简单的系统性工程或代码量较大的应用程序。这时候,一个简单的.py文件就会显得过于臃肿,无法承担一个重量级软件开发的重任。这就需要这一章的内容——化繁为简,将功能模块化、文件化,从而可以像搭积木一样,将不同的功能,组建在大型工程中搭建起来。
简单模块化
最简单的模块化方式,就是把函数、类、常量拆分到不同的文件,把他们放在同一个文件夹,然后使用下面的语句导入
from filename import function_name
from filename import class_name
举个例子吧
#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(2, 3)) encoder = Encoder()
decoder = Decoder() print(encoder.encode('abc'))
print(decoder.decode('dcba'))
我们把函数get_sum()放在一个文件里(utils.py),而类放在另外一个文件中(class_utils.py),在main函数里就直接调用,from...import...就可以把所需要的类和函数都导入进来。
可是慢慢的,我们发现把所有的文件都放在一个文件夹里也是不好管理的,需要建立多级的目录,就像这样
.
├── utils
│ ├── utils.py
│ └── class_utils.py
├── src
│ └── sub_main.py
└── main.py
main.py调用子目录的模块时,就需要改变一下代码了
#sub_main.py import sys
sys.path.append('..') from utils.class_utils import *
from utils import utils as fun print(fun.get_sum(2, 3)) encoder = Encoder()
decoder = Decoder() print(encoder.encode('abc'))
print(decoder.decode('dcba'))
sub_main函数调用的是上层目录下的子目录,就要把这个目录('..')加载到环境变量里。导入的方法也有所不同,导入的方法和模块使用方法可以看看前面写的文章:python之模块的导入。
要注意的一点,import同一个模块指挥被执行一次,这样就可以防止重复导入模块出现问题。在很多公司的编程规范中,除了一些极其特殊的情况,import必须位于程序的最前端。
还有一点,在许多教程中,我们都要在模块所在的文件夹里建一个__init__.py的文件,内容也可以是空的,主要用来描述包(package)对外暴露的模块接口。不过这是Python2的规范。在Python3以后的版本中,__init__.py并不是必须的。
项目模块化
很多大的项目,一个项目组的workspace可能有上千个文件,几十万到几百万行代码,上面所说的调用方式已经完全不够用了,下面我们就了解一下模块化的科学组织方式
首先要回顾一下绝对路径和相对路径的概念。我们在下面的路径下有两个文件
a1/b1/c1/d1/e1/example.txt
a1/b1/c1/d2/e2/example2.txt
如果我们要从e1跳转到e2文件夹,有两种方法
#方法1
cd a1/b1/c1/d2/e2
#方法2
../../d2/e2
方法1用的就是绝对路径,而方法2就是相对路径。其中,用点点(../)代表上一级目录。
通常一个Python文件在运行的时候,都会有一个运行时的位置,最开始即为这个文件所在的文件夹。而通过下面的语句就可以改变当前Python解释器的位置。而这种方法是不被推荐的。因为一个确定的路径对大型工程来说是非常必要的。
import sys
sys.path.append('..')
首先,因为代码可能会迁移,相对为hi会使得重构既不雅观,也容易出错。因此,在大型的工程中尽可能使用绝对位置是第一要义。对于一个独立的项目,所有的模块的追寻方式,最好从项目的根目录开始追溯,这叫做相对的绝对路径。下面我们创建一个新的项目,项目的结构如下:
.
├── 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 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]]
这个案例和前面的例子很像,但是导入的方式是直接用proto.mat导入的因为使用pycharm构建的项目,把不同文件放在不同的子文件夹里,跨模块调用是直接从顶层搜索的。所以必须是新建的项目,不能在已建的项目里新建个文件夹。
但是当我们用命令行调用src文件夹下,执行
python main.py ##########输出##########
Traceback (most recent call last):
File "main.py", line 2, in <module>
from proto.mat import Matrix
ModuleNotFoundError: No module named 'proto'
错了吧!因为Python解释器在遇到import的时候,他会在一个特定的列表里寻找模块,这个列表我们可以看一下
import sys print(sys.path)
Pycharm在细腻教案项目是就是把根目录设置为列表的里的一项(列表太长了我就不展示了)。这样我们在运行main.py时,import都会从根目录里找相应的包。
那普通的Python运行环境怎么办呢?
第一种:大力出奇迹——强行修改sys.path列表。把根目录直接加进去
import sys
sys.path.append(r'根目录路径')
这样我们的import就畅通无阻了。但是把绝对路径写在代码里是一个非常不推荐的方式(写在配置文件中也会因为找配置文件也需要个路径,于是就进入了个死循环)
第二种方法:修改PYTHONHOME。这里可以提一下Python的Virtual Environment,Python可以通过Virtualenv工具非常方便的常见一个全新的Python运行环境。事实上,对于每一个Python项目来说,最好要有一个独立的运行环境来保持包和模块的纯净性。
在一个虚拟环境里,能找到一个文件叫active,在这个文件的末尾,可以加上下面的内容
export PYTHONPATH='项目路径‘
这样每次通过active激活这个运行环境的时候就会自动将项目的根目录添加到搜索路径中去。
神奇的if __name__ == '__main__'
最后我们来看一下一个非常常见的写法:
if __name__ == '__main__':
这个语法有什么用呢?Python是脚本预压,和C++、Ja最大的不同就是不需要显性的提供main()函数入口。
但是我们在导入文件的过程中,会把所有暴露在外面的代码统统执行一遍。但是大多数时候我们是不想让他们执行的,这时候如果我们想要把一个东西封装成模块,又想让他在需要的时候才执行的话,就把必须要执行的代码放在if __name__ == '__main__'里。因为在__name__是一个内置参数,在我们使用import导入的时候,__name__就会被赋值为该模块的名字,当然就不等于'__main__'了!
思考题
我们在导入的时候有这两种方式
#方式A
from module_name import *
#方式B
import module_name
正常使用的时候哪种比较适合呢?
第一种会把路径下所有的模块导入程序,这就存在一个问题:如果有其他的函数名或类名和导入的模块相同,很容易造成冲突。
而第二种在使用的时候需要用下面的方式调用。相当于加了一层layer,能有效的避免因为名字相同导致的冲突。
model_name.fun()
Python核心技术与实战——十一|程序的模块化的更多相关文章
- Python核心技术与实战——十九|一起看看Python全局解释器锁GIL
我们在前面的几节课里讲了Python的并发编程的特性,也了解了多线程编程.事实上,Python的多线程有一个非常重要的话题——GIL(Global Interpreter Lock).我们今天就来讲一 ...
- Python核心技术与实战——六|异常处理
和其他语言一样,Python中的异常处理是很重要的机制和代码规范. 一.错误与异常 通常来说程序中的错误分为两种,一种是语法错误,另一种是异常.首先要了解错误和异常的区别和联系. 语法错误比较容易理解 ...
- Python核心技术与实战——十二|Python的比较与拷贝
我们在前面已经接触到了很多Python对象比较的例子,例如这样的 a = b = a == b 或者是将一个对象进行拷贝 l1 = [,,,,] l2 = l1 l3 = list(l1) 那么现在试 ...
- Python核心技术与实战 笔记
基础篇 Jupyter Notebook 优点 整合所有的资源 交互性编程体验 零成本重现结果 实践站点 Jupyter 官方 Google Research 提供的 Colab 环境 安装 运行 列 ...
- Python核心技术与实战——十六|Python协程
我们在上一章将生成器的时候最后写了,在Python2中生成器还扮演了一个重要的角色——实现Python的协程.那什么是协程呢? 协程 协程是实现并发编程的一种方式.提到并发,肯很多人都会想到多线程/多 ...
- Python核心技术与实战——七|自定义函数
我们前面用的代码都是比较简单的脚本,而实际工作中是没有人把整个一个功能从头写到尾按顺序堆到一块的.一个规范的值得借鉴的Python程序,除非代码量很少(10行20行左右)应该由多个函数组成,这样的代码 ...
- Python核心技术与实战——二一|巧用上下文管理器和with语句精简代码
我们在Python中对于with的语句应该是不陌生的,特别是在文件的输入输出操作中,那在具体的使用过程中,是有什么引伸的含义呢?与之密切相关的上下文管理器(context manager)又是什么呢? ...
- Python核心技术与实战——二十|assert的合理利用
我们平时在看代码的时候,或多或少会看到过assert的存在,并且在有些code review也可以通过增加assert来使代码更加健壮.但是即便如此,assert还是很容易被人忽略,可是这个很不起眼的 ...
- Python核心技术与实战——二十|Python的垃圾回收机制
今天要讲的是Python的垃圾回收机制 众所周知,我们现在的计算机都是图灵架构.图灵架构的本质,就是一条无限长的纸带,对应着我们的存储器.随着寄存器.异失性存储器(内存)和永久性存储器(硬盘)的出现, ...
随机推荐
- 使用Choose构建分支动态语句
1.在接口写方法 public List<Employee> getEmpsByConditionChoose(Employee employee); 2 在映射文件中配置 <!-- ...
- 2、Shiro的认证
Shiro的认证流程大体可以参考下面这幅图: 但是没有接触过shiro的同学看到上面的图片也不明白,下面我们来在代码中尝试体验Shiro的认证过程: 1.新建一个SpringBoot项目项目结构如下: ...
- python 3 爬虫
import urllib.request url = "http://www.oschina.net/" data = urllib.request.urlopen(url).r ...
- SpringContextHolder使用报错
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/dobecoder/article/details/88401612今天在使用SpringContex ...
- http层负载均衡之 haproxy实践篇
方案 上篇文章讲到了负载均衡的相关理论知识,这篇文章我打算讲讲实践方法以及实践中遇到的问题 方案:haproxy http层负载均衡 安装一个haproxy服务,两个web服务 haproxy:192 ...
- dotnet core排序异常,本地测试和linux上结果不一致
根据汉字排序,本地测试结构正常,发到docker之后,发现汉字升序降序和本地相反,检查代码后,没找到任何可能出现问题的点. 然后去翻文档:字符串比较操作 看到了这一句,会区分区域性 然后猜测应该是do ...
- python+selenium模拟鼠标操作
from selenium.webdriver.common.action_chains import ActionChains #导入鼠标相关的包 ------------------------- ...
- Double类型的数值 在写入excel时 如何去掉 科学计算法的 后面数值+ E的 情况
Double start = 20190724100000.000; 写入excel时 是 201907E+13 但想要输出的是 20190724100000 这种格式 Java在java.math包 ...
- Apache 强制SSL访问
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R ...
- make the fence great again(dp 二维)
D. Make The Fence Great Again time limit per test 2 seconds memory limit per test 256 megabytes inpu ...