这是机器未来的第19篇文章
写在前面:
- 博客简介:专注AIoT领域,追逐未来时代的脉搏,记录路途中的技术成长!
- 专栏简介:本专栏的核心就是:快!快!快!2周快速拿下Python,具备项目开发能力,为机器学习和深度学习做准备。
- 面向人群:零基础编程爱好者
- 专栏计划:接下来会逐步发布跨入人工智能的系列博文,敬请期待
- Python零基础快速入门系列
- 快速入门Python数据科学系列
- 人工智能开发环境搭建系列
- 机器学习系列
- 物体检测快速入门系列
- 自动驾驶模拟器AirSim入门系列
- 自动驾驶物体检测系列
- …
- 原文首发地址:https://blog.csdn.net/RobotFutures/article/details/125353783
前面已经学习过函数和类了,今天我们继续学习模块化的另外2种封装方式:模块和包。函数、类、模块和包构成了模块化四级封装体系。
模块其实就是python源代码文件,以.py后缀结尾,而包就是文件夹,其内包含.py源代码和__init__.py文件。他们的层级结构如下:
├─package
│ ├─module1.py
│ │ ├─class1
│ │ │ ├─function1
│ │ │ └─function2
│ │ ├─class2
│ │ │ ├─function11
│ │ │ └─function12
│ ├─module2.py
│ └─__init__.py
└─package2
├─module21.py
│ └─class21
│ └─function21
└─__init__.py
以上就构成了模块化的四级封装体系。
以python三剑客matplotlib绘图库为例来看看是不是这样在多层封装体系,我们先来写一段代码:
# 引入pylot模块
from matplotlib import pyplot as plt
# 引入numpy
import numpy as np
X = np.linspace(start=-5, stop=5, num=50)
y = X**2 + 6
plt.plot(X, y)
plt.show()
要实现绘制曲线的功能,需要使用到matplotlib包pyplot模块下的plot和show函数,我们先来看一下他在文件中的组织形式:
plt.plot(X, y)
中的plot函数是pyplot模块中在一个函数,看它在不在?
小技巧:在vscode环境下,将鼠标放在函数名上,按住CTRL键可以直接跳转到函数定义在位置。
跳转后,发现了plot函数在身影,的确在pyplot模块文件中。
我们实现一个功能的模块化封装:
以上篇文章中在吃饭例子为例
【Python零基础入门笔记 | 10】类的设计哲学:自然法则的具现, 将它划分如下:
Project
|-- restaurant_industry # 餐馆
|-- __init__.py # 包初始化文件,更改后缀
|-- restaurant.py
|-- customer.py # 食客
|-- main.py # 主程序
创建文件夹restaurant_industry和__init__.py, 那么__init__.py到底要写什么,以及有什么用呢?
功能:
_init_.py用来标识当前文件夹为Python包(Python3.2以后版本无需__init__.py也可以)
用法:
_init_.py可以为空,仅告诉解释器当前文件夹为Python包即可
做一些预加载工作
执行import package
时,package文件夹下的__init__.py会自动执行,基于加载包时自动加载特性,init.py还可以用来做一些预加载工作,例如模块的导入等
下面举例进行说明,创建如下的文件结构,内容及代码如下:
Project
|-- restaurant_industry # 餐馆
|-- __init__.py # 包初始化文件,更改后缀
|-- restaurant.py
|-- customer.py # 食客
|--package2
|--__init__.py
|-- main.py # 主程序
# restaurant_industry/__init__.py
print("restaurant_industry __init__.py load!")
# package2/__init__.py
print("package2 __init__.py load!")
# main.py
import restaurant_industry
import package2
# 打印文件中加载了哪些内容,dir()函数输入为空时,表明是当前文件
print(dir())
restaurant_industry __init__.py load!
package2 __init__.py load!
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'package2', 'restaurant_industry']
可以看到在dir()的输出中restaurant_industry和package2这两个文件夹已经被识别为包了,可以通过import引入,在引用时就自动执行了包下的_init_.py文件。
Python3.2以后版本无需_init_.py也可以识别为包,测试一下:将_init_.py后缀修改为.pyi,再次执行
Project
|-- restaurant_industry # 餐馆
|-- __init__.pyi # 包初始化文件,更改后缀
|-- restaurant.py
|-- customer.py # 食客
|--package2
|--__init__.pyi
|-- main.py # 主程序
# main.py
import restaurant_industry
import package2
# 打印文件中加载了哪些内容,dir()函数输入为空时,表明是当前文件
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'package2', 'restaurant_industry']
发现没有报错,可以执行,dir()的输出中同样包含了package2和restaurant_industry,但是不再打印_init_.py中的打印信息了。说明Python3.2以后版本无需_init_.py也可以识别为包了。
在_init_.py中可以使用一个特殊变量__all__来配合from module import *
预加载模糊引入的模块。
# restaurant_industry/__init__.py
#模糊引入时,指定加载的模块,如果不指定则不加载任何模块或模块中的内容,测试Python版本3.7.0
__all__ = ['restaurant']
print("restaurant_industry __init__.py load!")
# main.py
from restaurant_industry import *
import package2
print(dir())
运行python main.py的输出结果为
restaurant_industry __init__.py load!
package2 __init__.py load!
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'package2', 'restaurant']
可以看到restaurant模块被引用成功了,customer未引用,在代码中可以直接调用restaurant模块中的函数、类、变量了,而不需要import restaurant。
注意:__all__变量配合from module import *
代码使用时才生效
引用模块有以下几种形式
# 方式一:直接引用模块名
import module
# 方式二:通过包名.模块名引用
import package.module
# 方式三:通过from [package] import [module]引用,等价方式二
from package import module
不可以引用包,会提示模块无相关属性,类似这样
AttributeError: module 'restaurant_industry' has no attribute 'Restaurant'
语法:
from [module] import [class1, class2, func1, varable1,...]
详解:
现在,根据上一篇博文中的吃饭例子,我们将相关代码分别封装到customer和restaurant模块中,代码如下:
# 文件位置:Project\restaurant_industry\customer.py
class Customer:
"""客人
"""
def __init__(self, order_id):
self.order_id = order_id
self.amount = 0
def order(self, restaurant, dish_id):
restaurant.order(self.order_id, dish_id)
# 用于演示函数引用
def func1():
print("客人对餐品很满意,五星点赞!")
# 用于演示变量引用
customer_list = ['c', 'u', 's', 't', 'o', 'm', 'e', 'r']
# 文件位置:Project\restaurant_industry\restaurant.py
class Dish:
"""
菜品
"""
def __init__(self, id, name, price):
self.id = id
self.name = name
self.price = price
class Restaurant:
"""
菜单
"""
def __init__(self):
self.menu = []
self.ordered_menu = {}
def add(self, id, name, price):
"""添加新菜品
"""
self.menu.append(Dish(id, name, price))
def display_menu(self):
"""展示菜谱
"""
for item in self.menu:
print(f"{item.id}\t{item.name}\t\t{item.price}")
def order(self, order_id, dish_id):
# ord = {item for item in self.ordered_menu.keys if item == order_id}
ord = self.ordered_menu.get(order_id, [])
if ord: # 不为空,说明订单已经产生
ord.append(dish_id)
else: # 为空,说明是新订单
self.ordered_menu[order_id] = list([dish_id])
def check(self, order_id):
amount = 0
# self.ordered_menu[order_id]直接获得客户的已选菜单,然后用列表推导式获得结算价格
checklist = [dish.price for dish in self.menu if dish.id in self.ordered_menu[order_id]]
for x in checklist:
amount += x
return amount
# main.py
# 从模块customer.py中引用Customer类
from restaurant_industry.customer import Customer
# 直接引用restaurant,并使用as将restaurant_industry.restaurant定义别名为restaurant,避免每次调用时都要加上restaurant_industry.restaurant.前缀,用as定义别名后,可以直接使用rest.前缀即可。
import restaurant_industry.restaurant as rest
# 模块customer.py中引用func1函数,customer_list变量
from restaurant_industry.customer import func1, customer_list
print(dir())
# 因为仅引用了restaurant模块,因此需要使用【模块.类】的访问方式
restaurant = rest.Restaurant()
# 添加新菜品
restaurant.add(1, "青椒肉丝", 22)
restaurant.add(2, "皮蛋豆腐", 16)
restaurant.add(3, "新疆大盘鸡", 89)
restaurant.add(4, "虎皮青椒", 22)
# 展示菜单
restaurant.display_menu()
# 客人c1点餐
# 因为已经从模块customer.py中引用了Customer类,因此无需使用【模块.类】的访问方式
c1 = Customer(order_id = 1)
c1.order(restaurant=restaurant, dish_id = 1) # c1点了青椒肉丝
c1.order(restaurant=restaurant, dish_id = 2) # c1点了皮蛋豆腐
# 客人c2点餐
c2 = Customer(order_id = 2)
c2.order(restaurant=restaurant, dish_id = 2) # c2点了皮蛋豆腐
c2.order(restaurant=restaurant, dish_id = 3) # c2点了新疆大盘鸡
# 客人c1结账
c1.amount = restaurant.check(c1.order_id)
# 客人c2结账
c2.amount = restaurant.check(c2.order_id)
print(f"客人c1消费了{c1.amount}元")
print(f"客人c2消费了{c2.amount}元")
# 因为已经从模块customer.py中引用了func1函数,因此无需使用【模块.函数】的访问方式
func1()
restaurant_industry __init__.py load!
# dir()的输出
['Customer', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'customer_list', 'func1', 'rest']
1 青椒肉丝 22
2 皮蛋豆腐 16
3 新疆大盘鸡 89
4 虎皮青椒 22
客人c1消费了38元
客人c2消费了105元
客人对餐品很满意,五星点赞!
从代码可以了解到以下知识点:
到这里,自定义一个功能模块库的例子就讲解完毕了,通过定义包、模块、类、方法四级体系,搭建了完整的模块化封装体系。模块化也讲了好几期了,今天为模块化画上了一个圆满的句号。模块化的优势就不多说了,使用过程中自然就有体会了。
《Python零基础快速入门系列》快速导航: