18. Python中的模块与包

更新於 發佈於 閱讀時間約 18 分鐘
raw-image


Hi, 大家好。我是茶桁。

这一段Python之旅怎么样?还算顺利吧?

之前我们都学习了些什么?有基本常识,流程,函数,不同类型的数据以及一些模块对吧?并且还做了一些练习来巩固所学过的内容。

那么今天,我们接着来学习模块。不过今天要学的模块和以往不太一样了,以前我们学习的都是Python内置的一些模块,而今天呢,我们自己来打包模块。

模块

简单点说,当我们定义一个Python文件,其后缀名为.py的时候,那么这个文件就被称为模块。

模块中通常呢会定义一些相似的类、函数等代码内容,提供给别的程序引入使用。那对于应用,之前我们已经用过很多次了对吧?我们曾多次应用系统模块来使用,那这次,我们还是从系统模块开始吧。

系统模块

系统模块实际上就是一个Python的程序脚本,专门提供给我们自己的程序使用。它们是在安装好Python环境时,就已经存在的,需要的时候可以使用 import 导入到程序中使用。比如:

 import os, re, time, json, calendar

自定义模块

那知道了系统模块是什么东西,在理解自定义模块就轻松多了对吧?其实就是我们自己创建一个Python脚本,定义一些类或方法,供别的脚本导入后使用。

由于本节课比较特殊,所以课程源码除了18.ipynb这个笔记本文件之外,还有有一个文件夹,路径为./Python/packages/file,然后内部会有多个.py文件。

比如我们定义一个self.py文件如下:

 # self.py
 ​
 # 定义类
 class MyException():
     pass
 ​
 # 定义函数
 def func():
     print('我是一个模块中的func函数')
 ​
 # 定义变量
 myStr = 'iloveyou'

然后让我们在笔记本中引用这个文件(模块)以及其他模块,让我们来看看,还记得我们是怎么引入模块的嘛?来,回忆一下:

 # 先引入一个系统模块:time
 import time
 print(f'time:{time.time()}')
 ​
 ---
 time:1692005247.144672

我们引入了一个系统模块time,然后执行了一下模块里的time()方法,并把最终结果打印了出来。

既然都已经有例子了,那我们有样学样来试试引入我们自己创建的文件:

 import self
 ​
 ---
 ModuleNotFoundError: No module named 'self'

报错了,告诉我们并没有self这个模块。这个...

还记得我们刚才说过的文件路径嘛?./Python/packages/file,而我们当前文件18.ipynb是放在Python目录下的,层级关系如下:

 - .
 - Python/
 - ...
 - 18.ipynb
 - packages/
  | - self.py

也就是说,我们要应用self.py, 需要找对路径才行。那我们将路径加上去:

 # 引入自定义模块
 import packages.self

这回执行之后是没报错了,应该没问题了。

那下面呢,让我们来操作一下文件内的类、函数之类的试试:

 # 使用模块中定义的类
 obj = packages.self.MyException()
 print(obj)
 ​
 ---
 <packages.self.MyException object at 0x10468cb80>

没毛病,确实获取到了相关类病打印了出来。

可是我们这也太麻烦了,每次使用这个模块不是抖要输入这么长一段吧?packages.self.xxx, 不知道之前的学习中大家有没有注意到一个关键字as,这个我之前课程中都没有特意讲解过,但是在我们引入模块的时候会经常的用到。所以这里顺带讲一下吧,比如,我们在操作文件的时候有如下代码:

 with open('./file', 'w') as fp:
   fp.read()

那这个as我们能猜到是什么作用吗?其实,就是讲as前的内容放入as后面的这个变量里,然后将as身后的这个变量改为一个对象而已,在这段代码里,我们打开了文件,并且将其放入了fp这个变量里,变成了一个fp对象。也可以理解为,我们将as之前的内容起了一个别名。

那么我们导入文件的时候可以这么操作吗?我们试试看:

 import packages.self as self
 obj = self.MyException()
 print(obj)
 ​
 ---
 <packages.self.MyException object at 0x1046ce2c0>

嗯,看来我们没搞错,确实可以这么用。

那让我们再来试试文件中的那个函数吧,函数内应该是执行了一段打印方法:

 self.func()
 ​
 ---
 我是一个模块中的func函数

确实正确执行了。这也太顺利了,趁热打铁,让我们再来获取其中的变量:

 print(self.myStr)
 ​
 ---
 iloveyou

导入模块其实不是仅可导入模块,还能从一个模块中导入类,方法甚至是变量:

 from packages.self import func
 from packages.self import myStr as str
 ​
 func()
 print(str)

应该能看出这一段代码的含义吧?就是from(从)一个模块中import导入一个对象。

模块中的测试代码

在自定义模块中,通常我们只是去定义类或函数,变量等,并不调用。如果在指定模块中,想要写一些测试代码,在当前模块作为主程序使用时执行,而作为模块被别的程序导入时不执行,那么可以把测试代码写到下面的代码块中:

 if __name__ == '__main__':
   print('这个位置的代码只有当前脚本被直接运行时才会运行。')

那么这个模块再被别的程序调用之后,这段代码中的程序是不会被执行的。因为只有这个模块作为主程序运行时才会运行这段代码。我们来看下面这些操作就明白了:

 import packages.self as self
 self
 ​
 ---
 <module 'packages.self' from '/Users/du/git/AI_Cheats/Python/packages/self.py'>

按道理,我们引入模块之后应该会拿到该模块内的所有方法,可是刚才我们写的打印并没有被执行。现在我们在命令行内直接大概这个.py文件来试试:

raw-image


能看到,if里面的print被直接执行了,打印出了里面的字符串。

在这整段代码中,__name__是一个特殊的变量,这个变量在当前脚本作为模块被别的程序导入时__name__的值是当前这个模块的名称,也就是说,我在笔记本中导入的时候__name__就是self, 而我们在if条件中的设定,是只有当前脚本被作为主程序直接由Python解析时才会进入判断,也就是__name__这个变量的值为__main__时。

我们来看看是不是如此,我们在self.py中加上一段代码:

 name = __name__
 print(f'__name__: {name}')

然后我们直接让self.py在Python解释器中运行:

raw-image

现在让我们在笔记本中重新引入一下模块中的变量name,再打印出来看看:

raw-image


打印的第一段内容为引入模块的时候,模块内的print(f'__name__: {name}')执行了一次,第二段内容则是在笔记本中输入的方法print(name)。 这样,我们就很直观的看到了__name__在不同位置时存储了不同的值。

我们在写程序的时候要记得,不要想着把所有的方法定义在一个脚本文件内。

那什么是包呢?包并不是模块。你可以将包理解为一个文件夹,这个文件夹里面包含了多个Python文件。

包的结构

 '''
 package/   # (文件夹)
 ├── __init__.py # 包中的初始化文件
 ├── a.py         # 包中的模块
 ├── b.py
 └── ps/   # 子包
  ├── __init__.py
  ├── c.py
  └── d.py
 '''

包的使用方法

其实,我们在刚才所讲的内容中,已经给大家演示过了包的使用方法,不知道小伙伴们能不能反应过来到底是哪里?不知道也没有关系,让我们从头来好好的盘一下这件事。

我们之前在当前目录下创建了一个文件夹packages, 里面有我们self.py文件。实际上,这就是一个包了。

让我们将这个包搞的复杂一点,按照上面我们写的结构来增加一些文件,然后我们看看现在的目录结构:

raw-image


我们可以看到,除了我们之前设定的文件之外,还有多出来一个文件夹__pycache__以及文件self.cpython-310.pyc, 这个文件夹和文件是当这个包内的文件存在引入关系的时候,自动生成的缓存文件。大家可以不用管。

下面我们来看具体的包使用方法,我们预先在a, b, c, d这四个文件内都写入了一模一样的代码:

 def funca():
  print('a.py')

当然,方法名和打印的内容都和文件同名的。

然后我们回到18.ipynb这个笔记本文件内,开始操作使用:

 import packages as pa
 pa.a.funca
 ​
 ---
 AttributeError: module 'packages' has no attribute 'a'

似乎并不行,我们好像并不能引用包来直接使用。那我们怎么办呢?前面我们介绍过一个引用的方法from ... import,我们在使用包内的模块时,需要这样去引用。

 from packages import a, b
 a.funca()
 b. funcb()
 ​
 ---
 a.py
 b.py

可以看到,这回我们引用成功了。那我们之前也学到了,在引入模块的时候,也可以直接就引用模块内的方法和变量,模块在包内也可以如此使用:

 from packages.a import funca
 funca()
 ​
 ---
 a.py

那既然我们得到了这种方式来导入模块内的内容,同样的,包内层如果还存在一个包,而我们要使用子包里的模块,也是这样的导入方法:

 from packages.ps import c
 c.funcc()
 ​
 ---
 c.py

看到了,同样能够正常使用。

那如果再过分点,我们要想导入c.py里的函数可以吗?试试就知道了, 再使用.多链接一层:

 from packages.ps.d import funcd
 funcd()
 ​
 ---
 d.pys

呐,完全没问题。

然后我们再反过来看最开始,其实呢,我们的第一种方法直接引用包不是不可以,这需要用到我们这个包内的__init__.py文件。

__init__.py是一个包内的初始化文件,可以说,没有这个文件,这只是一个文件夹,只有有了这个文件,这才是一个包。在初始化的时候,就把包内的模块导入一次,在__init__.py中写下以下代码:

 import a

然后我们再回到笔记本文件中直接导入包来使用试试:

 import packages
 a.funca()
 ​
 ---
 a.py

这样就可以了。

好了,那如果这个时候我packages这个包里一大堆的模块,我不想一个个的来导入,有什么办法吗?也是有的,我们需要用到__all__这个参数,在__init__.py中将包内所有的模块名做成一个列表,然后赋值给__all__这个变量,那么我们在引入包内的模块的时候,就可以使用`*来代表所有文件:

__init__.py文件:

 __all__ = ['a', 'b']

然后进行引入:

 from packages import *
 b.funcb()
 ​
 ---
 b.py

这样,我们就一次性导入了packages这个包里的所有文件。

导入方式的分类

之前我们讲的内容中,把导入的方式都过了一遍。到现在这个位置,我们应该总结一下了。

具体的导入方式,我们可以将其分为两个类别,分别是绝对导入相对导入。那两者有什么区别呢?

绝对导入

绝对导入的方式会使用「搜索路径」去查找和导入指定的包或模块,包括以下几种方式:

  • import module 导入模块
  • import package 导入包
  • import package.module导入包.模块
  • from module import func 从模块中导入函数
  • from package import module从包中导入模块
  • from package.module import func 从包.模块中导入函数

关于「搜索路径」,我们先简单的理解一下就是,从当前文件夹中去找,如果找不到,就会去Python的安装环境中去寻找。

相对导入

⚠️ 相对导入智能在非主程序的模块中使用,不需要直接运行的模块文件。比如:

  • from .包名/模块名 import 模块/内容
  • from ..包名/模块名 import 模块/内容

...我们之前已经了解过了,.代表的就是当前这一级,..代表的就是上一级。

举个栗子好理解:假设我们现在去修改一下ps/c.py这个文件,在这个模块中如果需要当前包中的d模块:

 from .d import funcd

注意啊,我们这个时候不要在c.py中直接运行funcd()方法,这样会导致报错:

 ImportError: attempted relative import with no known parent package

那我们需要怎么运行呢?我们需要讲c.py导入到其他文件中再执行。比如我们进入到笔记本18.ipynb中导入执行。

 import packages.ps.c as c
 c.funcd()
 ​
 ---
 d.py

然后让我们再在c.py中加上一段内容:

 from ..a import funca

小伙伴们应该都看出来了,我是在引用c.py的上一级的a.py

让我们再在笔记本中执行一下试试:

 import packages.ps.c as c
 c.funcd()
 c.funca()
 ​
 ---
 d.py
 a.py

这样,在我们引入了模块c之后,我们同时也拥有了c.py引入的同级和上一级中的d.pya.py

搜索路径

刚才我们简单提到了一下「搜索路径」, 这里我们详细的来展开说一下。

「搜索路径」就是在导入模块或者包的时候,程序查找的路径。主要的搜索路径包含以下三部分:

  • 当前导入模块的程序所在的文件
  1. python的扩展目录中
  2. python解释器指定的其它 第三方模块位置 /lib/sitepackages

当然,如果你像我一样,系统中安装了多个Python版本,并且使用虚拟环境。那么你的「搜索路径」就不一定是在哪里了。那么我们到底该如何查找呢?我们来看一下以下查找方法:

 # 在当前脚本中查看包或者模块的搜索路径
 import sys
 print(sys.path)
 ​
 ---
 ['/Users/du/git/AI_Cheats/Python', '/Users/du/miniforge3/envs/glm/lib/python310.zip', '/Users/du/miniforge3/envs/glm/lib/python3.10', '/Users/du/miniforge3/envs/glm/lib/python3.10/lib-dynload', '', '/Users/du/miniforge3/envs/glm/lib/python3.10/site-packages']

我们看到找到的搜索路径被以列表的形式呈现出来。当然,我们找到搜索路径后,其实是可以向其中添加一个的。

 sys.path.append('/Users/du/AI/GPT')

单入口程序

那什么是单入口程序呢?顾名思义,这种程序就只有一个入口。那单入口程序就是指整个程序都是经过一个主程序文件在运行,其它程序都封装成了包或模块。

单入口文件是作为程序直接被运行的唯一文件,其他都是作为模块或者包,被导入单入口中去执行。打个比方说,我们要去做一个ATM机的程序,我们来实现一个单入口程序。那么可能的情况如下:

 ATM/
 |-- main.py # 当前程序的主入口文件,单入口文件,唯一直接运行的文件
 |-- package/ # 主要程序模块包
 |-- |--- __init__.py # 包的初始化文件
 |-- |--- View.py     # 视图函数模块
 |-- |--- Controller.py# 控制器模块
 |-- |--- Card.py     # 银行卡模块
 |-- |--- User.py     # 用户模块
 |-- databases/ # 数据存储文件夹
 |-- |-- user.txt
 |-- |-- user_id_card.txt

那么这个程序中,main就是程序的主入口文件,会被直接作为主程序运行。所以main.py文件必须使用「绝对导入」的方式。

好,那讲到这里,我们今天的内容也就结束了。不知道小伙伴们理解了多少?

本节课也不太好放练习,那我们这节课就免了。下去之后,大家去拉取我的源码好好的研究一下引入关系,然后讲包、模块的概念好好的理解透。

那小伙伴们,咱们下节课再见。

留言
avatar-img
留言分享你的想法!
avatar-img
茶桁的沙龍
9會員
62內容數
从基础开始,再到Python,然后是CV、BI、NLP等相关技术。从头到尾详细的教授一边人工智能。
茶桁的沙龍的其他內容
2023/08/22
虽然是最后一节课了,但是本节课的任务却是一点也不轻松。相比较而言,如果你以后从事的是数据治理和分析工作,那么本节课的内容可能会是你在今后工作中用到的最多的内容。我们需要学习行列索引的操作,数据的处理,数据的合并,多层索引,时间序列,数据的分组聚合(重点)。最后,我们会有一个案例的展示。
Thumbnail
2023/08/22
虽然是最后一节课了,但是本节课的任务却是一点也不轻松。相比较而言,如果你以后从事的是数据治理和分析工作,那么本节课的内容可能会是你在今后工作中用到的最多的内容。我们需要学习行列索引的操作,数据的处理,数据的合并,多层索引,时间序列,数据的分组聚合(重点)。最后,我们会有一个案例的展示。
Thumbnail
2023/08/21
Hi,大家好。我是茶桁。 上一节课中,我们学习了matplotlib. 实际上,我们已经进入了数据可视化阶段。 可是在上一节课中,所有的数据都是我们固定写好的,包括两个电影的数据展示的案例(柱状图和直方图),都是我们将数据手动写成了数据列表,然后直接使用。 在我们平时的工作中,不太有那么多的机
Thumbnail
2023/08/21
Hi,大家好。我是茶桁。 上一节课中,我们学习了matplotlib. 实际上,我们已经进入了数据可视化阶段。 可是在上一节课中,所有的数据都是我们固定写好的,包括两个电影的数据展示的案例(柱状图和直方图),都是我们将数据手动写成了数据列表,然后直接使用。 在我们平时的工作中,不太有那么多的机
Thumbnail
2023/08/21
Hi, 大家好。我是茶桁。 在上一节课中,我们结束了Python正式的所有内容,但是咱们的Python课程还未结束。从这节课开始,我们要来学习一下Python的第三方库。 Python的生态非常完善也非常活跃,我们不太可能讲目前所有的第三方库全部都介绍一遍,只介绍几个有影响力并且和处理数据相关的
Thumbnail
2023/08/21
Hi, 大家好。我是茶桁。 在上一节课中,我们结束了Python正式的所有内容,但是咱们的Python课程还未结束。从这节课开始,我们要来学习一下Python的第三方库。 Python的生态非常完善也非常活跃,我们不太可能讲目前所有的第三方库全部都介绍一遍,只介绍几个有影响力并且和处理数据相关的
Thumbnail
看更多
你可能也想看
Thumbnail
2025 vocus 推出最受矚目的活動之一——《開箱你的美好生活》,我們跟著創作者一起「開箱」各種故事、景點、餐廳、超值好物⋯⋯甚至那些讓人會心一笑的生活小廢物;這次活動不僅送出了許多獎勵,也反映了「內容有價」——創作不只是分享、紀錄,也能用各種不同形式變現、帶來實際收入。
Thumbnail
2025 vocus 推出最受矚目的活動之一——《開箱你的美好生活》,我們跟著創作者一起「開箱」各種故事、景點、餐廳、超值好物⋯⋯甚至那些讓人會心一笑的生活小廢物;這次活動不僅送出了許多獎勵,也反映了「內容有價」——創作不只是分享、紀錄,也能用各種不同形式變現、帶來實際收入。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
⏳ 優質好文,歡迎支持與學習,讓阿Han給你最歡樂的學習課程、最貼近業界的實戰技巧…,just for fun fun fun…😄 相信對於具有開發基礎的學員們來說,基本的開發工程相信已經得心應手了, 那麼您們可能會苦於這些問題: ❓ 哇! pip install別人的程式好方便
Thumbnail
⏳ 優質好文,歡迎支持與學習,讓阿Han給你最歡樂的學習課程、最貼近業界的實戰技巧…,just for fun fun fun…😄 相信對於具有開發基礎的學員們來說,基本的開發工程相信已經得心應手了, 那麼您們可能會苦於這些問題: ❓ 哇! pip install別人的程式好方便
Thumbnail
在第十二課中,我們將學習 Python 中的模塊和包的概念。 模塊和包是 Python 中組織程式碼的主要方式,它們讓你能夠以邏輯和易於管理的方式組織你的程式碼。
Thumbnail
在第十二課中,我們將學習 Python 中的模塊和包的概念。 模塊和包是 Python 中組織程式碼的主要方式,它們讓你能夠以邏輯和易於管理的方式組織你的程式碼。
Thumbnail
在第七課,我們將談論 Python 中的模組 (Modules) 和套件 (Packages)。 模組和套件允許我們將 Python 程式碼組織成可重用的單元,有助於程式碼的模組化和重用。
Thumbnail
在第七課,我們將談論 Python 中的模組 (Modules) 和套件 (Packages)。 模組和套件允許我們將 Python 程式碼組織成可重用的單元,有助於程式碼的模組化和重用。
Thumbnail
Hi, 大家好。我是茶桁。 在我们之前的课程中,讲解了数据,函数,类,模块以及包。这些基本上已经构成了Python的全部了。 那么,我们在学习Python的包之后,有没有思考过,既然Python有内置模块,我们也可以自己写一些模块来使用,那一定有很多第三方写过相应的模块来供我们使用。那么,这
Thumbnail
Hi, 大家好。我是茶桁。 在我们之前的课程中,讲解了数据,函数,类,模块以及包。这些基本上已经构成了Python的全部了。 那么,我们在学习Python的包之后,有没有思考过,既然Python有内置模块,我们也可以自己写一些模块来使用,那一定有很多第三方写过相应的模块来供我们使用。那么,这
Thumbnail
Hi, 大家好。我是茶桁。 这一段Python之旅怎么样?还算顺利吧? 之前我们都学习了些什么?有基本常识,流程,函数,不同类型的数据以及一些模块对吧?并且还做了一些练习来巩固所学过的内容。 那么今天,我们接着来学习模块。不过今天要学的模块和以往不太一样了,以前我们学习的都是Python内
Thumbnail
Hi, 大家好。我是茶桁。 这一段Python之旅怎么样?还算顺利吧? 之前我们都学习了些什么?有基本常识,流程,函数,不同类型的数据以及一些模块对吧?并且还做了一些练习来巩固所学过的内容。 那么今天,我们接着来学习模块。不过今天要学的模块和以往不太一样了,以前我们学习的都是Python内
Thumbnail
Hi,大家好。我是茶桁。 系统内置模块就是安装完Python解释器之后,系统本身所提供的模块。我知道,咱们之前的课程里有学习系统的内置函数,这个模块和函数不是一个东西。模块这种东西,是需要导入后才可以使用的,比如:json, re, os等等。
Thumbnail
Hi,大家好。我是茶桁。 系统内置模块就是安装完Python解释器之后,系统本身所提供的模块。我知道,咱们之前的课程里有学习系统的内置函数,这个模块和函数不是一个东西。模块这种东西,是需要导入后才可以使用的,比如:json, re, os等等。
Thumbnail
HI, 大家好。我是茶桁。 上一节中我们学习了Python基本的流程控制,并且预告了这一节的内容,就是将要学习「模块化编程」。那什么是模块化编程呢?按照维基百科的说法: 模块化编程(英语:modular programming),是强调将计算机程序的功能分离成独立的、可相互改变的“模块)”(mo
Thumbnail
HI, 大家好。我是茶桁。 上一节中我们学习了Python基本的流程控制,并且预告了这一节的内容,就是将要学习「模块化编程」。那什么是模块化编程呢?按照维基百科的说法: 模块化编程(英语:modular programming),是强调将计算机程序的功能分离成独立的、可相互改变的“模块)”(mo
Thumbnail
Python的系列课程是写给零基础的小白看的,如果基础比较好的小伙伴可以暂时先不关注,等待后续课程。 Hi, 大家好,我是茶桁。 之前的课程已经给大家讲解了Python相关特性和基本语法。那么这节课呢,我们尝试着从最简单的脚本来开始认识Python。 在开始这节课之前呢,我是默认大家已经安装好
Thumbnail
Python的系列课程是写给零基础的小白看的,如果基础比较好的小伙伴可以暂时先不关注,等待后续课程。 Hi, 大家好,我是茶桁。 之前的课程已经给大家讲解了Python相关特性和基本语法。那么这节课呢,我们尝试着从最简单的脚本来开始认识Python。 在开始这节课之前呢,我是默认大家已经安装好
Thumbnail
大家好,歡迎來到我的學習筆記系列,今天我要跟大家分享一下 Python 的 datetime 模組,這是一個非常實用的模組,可以幫助我們處理日期和時間相關的問題。 datetime 模組提供了四個主要的類別,分別是 date、time、datetime 和 timedelta。
Thumbnail
大家好,歡迎來到我的學習筆記系列,今天我要跟大家分享一下 Python 的 datetime 模組,這是一個非常實用的模組,可以幫助我們處理日期和時間相關的問題。 datetime 模組提供了四個主要的類別,分別是 date、time、datetime 和 timedelta。
Thumbnail
大型程式的開發通常不會由一個人獨力完成,而是將程式依功能拆分成不同的函數或是類別,由不同人協力開發成各個模組,最後再將這些模組導入主程式,或是直接套用現有的模組。模組依來源可以分成三大類: Python內建的模組 外部模組,需從第三方的軟體儲存庫(如PyPI)並安裝使用 程式開發者自行開發的模組
Thumbnail
大型程式的開發通常不會由一個人獨力完成,而是將程式依功能拆分成不同的函數或是類別,由不同人協力開發成各個模組,最後再將這些模組導入主程式,或是直接套用現有的模組。模組依來源可以分成三大類: Python內建的模組 外部模組,需從第三方的軟體儲存庫(如PyPI)並安裝使用 程式開發者自行開發的模組
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News