python模块和包
文章目录
在python中,一个程序由简单的程序可以由变量,函数,类组成,也可以说函数也是一个变量(对象), 类也是一个变量(对象),其次有了模块的概念,以
.py
结束的一个文本文件就可以称为一个模块,模块是我们保存代码的最小的一个单位,
一个模块可以称之为一个细胞,而由很多细胞组成的器官,可以称之为包。一个包包含了很多的模块。
python之所以强大,就是因为第三方别人编写好的包我们可以直接导入使用,life is short, we need python
,那么怎么导入别人编写好的包呢?python在安装的过程中,只会提供一些基础的包,比如math包,random包等等……如果我们需要更多的别人写好的模块,我们需要使用pip模块管理工具下载别人写好的模块。pip就约等于一个管理插件的工具。
有过C++基础的人应该知道C++中的命名空间的概念(namespace),在python中,一个模块不允许出现重名的情况,如果出现了重名的情况,那么后面的定义的变量会覆盖前面的,但是如果是不同的模块中,python就允许出现重名的情况,所以一个模块也能够称为一个命名空间
以下的测试代码在python的一个轻量IDE-pycharm下编写测试,pycharm下载安装教程,当使用pycharm创建好一个项目文件之后,会出现两个文件夹,不能够删除
.idea文件夹和venv文件夹,为python的运行环境文件夹,不能够删除。
接下来,我们创建两个文件夹,test01
文件夹和test02
文件夹,和idea和venv文件夹属于同级目录下,并且在test01目录下创建一个t1.py文件,在test02目录下创建一个c1.py文件,整个文件体系如下:
为了使用的方便,都使用以下图片表示
1. 模块的导入以及使用
1.1 import的使用
我们在一个模块中使用import导入别的模块写好的函数,类等…
- 我们在test01包中的t1.py模块编写一个变量a,一个add()函数,和一个People类
- 通过import把t1中编写的函数,变量,类导入到test02中的c1模块中
- 在c1中使用导入的模块
test01.t1:
a = 10 def add(c: int, b: int) -> int: return c + b class People(object): def __init__(self, age: float, name: str): self.age = age self.name = name def __str__(self): return f'我叫{self.name}, 我今年{self.age}岁了'
我们在test01
文件夹下的t1.py
模块中定义了一个变量,一个函数和一个类,我们在test02
文件夹下的c1.py
模块下导入test01.t1
这个模块
test02.c1:
import test01.t1 # 把test01.t1模块导入到我们的c1模块中 print(test01.t1.a) print(test01.t1.add(10, 20)) tom = test01.t1.People(20, 'tom') print(tom)
程序输出为:
10
30
我叫tom, 我今年20岁了
通过以上步骤,我们成功实现了自己编写的模块的导入和使用
import的导入一定是具体到模块的,比如下面的代码,我仅仅导入test01
,而不是test01.t1
import test01
# 导入的并非一个模块,而是一个包
print(test01.t1.a)
print(test01.t1.add(10, 20))
tom = test01.t1.People(20, 'tom')
print(tom)
那么整个代码就会报错
1.2 as(取别名的使用)
现在我们在test01
文件夹下面再新建一个文件夹,test03
,在test03
包中再编写一个模块
现在我们需要编写这个m1.py
文件,并且在c1.py
中调用这个模块
test03.m1.py
a = 10 def add(c: int, b: int) -> int: return c + b class People(object): def __init__(self, age: float, name: str): self.age = age self.name = name def __str__(self): return f'我叫{self.name}, 我今年{self.age}岁了'
直接复制test01中的
t1.py
模块的代码到test03中的m1.py
模块中
现在在c1.py
模块中调用上面模块中的代码
c1.py
import test01.test03.m1 print(test01.test03.m1.a) print(test01.test03.m1.add(10, 20)) tom = test01.test03.m1.People(20, 'tom') print(tom)
程序输出为:
10
30
我叫tom, 我今年20岁了可以看出来,这里嵌套了四层,每次我们需要调用这个嵌套中的模块,都需要写很多层嵌套,对于我们来说十分麻烦,于是就有了取别名
import test01.test03.m1 as m #这里使用as进行取名,把原来的很多层嵌套进行替换 print(m.a) print(m.add(10, 20)) tom = m.People(20, 'tom') print(tom)
程序输出为:
10
30
我叫tom, 我今年20岁了也能够得到正确的结果
1.3 from…import…
现在又有了一个新的问题,为了更加的方便,连点运算符都不想使用,有没有更加简单的方法
还真有! 我们的口号是:life is short, we need python!
依然从上面的test03.m1.py
模块中导入
test02.c1.py
from test01.test03.m1 import a, add print(a) print(add(10, 20))
在这里,我们直接把test01.test03.m1模块中的a, 和 add()导入,并且不需要加任何前缀就能使用
程序输出为:
10
30是不是十分方便,那如果想导入的东西很多,比如想要再导入一个People类,那么应该怎么办
可以这样
from test01.test03.m1 import a, add, People
可是我还是觉得麻烦,这边使用 * 号一键全部导入
from test01.test03.m1 import * print(a) print(add(10, 20)) tom = People(20, 'tom') print(tom)
程序输出为:
10
30
我叫tom, 我今年20岁了是不是十分方便,哈哈,但是不推荐使用 * 号,因为如果你导入的包是你不太熟悉的,那么你可能会翻车车什么的🚗
还是推荐你需要什么,就导入什么,别一股脑全部导入
1.4 来自面向对象的思考
【小故事】想象一下,你是一个模块,你自己有三种属性,车,房子和老婆。你有一个好基友,也就是另外一个模块,他需要导入你的车子使用(这个可以有),你能够把车子借出给朋友,这是你朋友能导入你这个模块的一些内容,那你的朋友能不能导入你的房子呢?额,十分要好的好基友,问题也不大,那么问题来了,你朋友能导入你老婆嘛?【💚要坚强💚]
在我们看来,某个模块能不能导入我们编写的模块,我们想让他导入,他才能导入,我们想要隐藏的一些东西,外界不能够随便导入
__all__
:填写一个列表,这个列表中的元素是外界能访问的元素,列表之外的不能够被其他模块访问,下面对test01.t1.py
进行限制
test01.t1.py
__all__ = ['a', 'add'] #People类不在列表中 a = 10 def add(c: int, b: int) -> int: return c + b class People(object): def __init__(self, age: float, name: str): self.age = age self.name = name def __str__(self): return f'我叫{self.name}, 我今年{self.age}岁了'
同时,在test02.c1.py中,我们这样写
from test01.t1.py import People #8好意思,会报错,这是我老婆你不能访问
这也是面向对象的很重要的思想,我想让外界访问什么,外界只能访问什么
2. python中的包
python中的包其实可以当作一个文件夹,一个文件夹下面有许多的模块,但是python中的包和文件夹有一些不一样的属性,
__init__.py
:“到我了?”
我:对,到你了!
2.1 __init__.py
在python中,我们新建一个包的时候
上面的例子中,我们的所说的包都是Directory
,也就是test01
, test02
, test03
,他们都是普通的包,严格来说,python中的包(python Package)在新建了之后,子目录下会自动产生一个__init__.py文件,当我们在其他模块导入这个新建的python包,__init__.py模块中的代码会自动执行(本来是没有代码的,但是我们可以在里面写代码)
新建的python包我们取名叫做test04
,在自动生成的目录下的__init__.py文件中写几句代码
好了,我们现在在test02.c1.py模块导入test04包,会发现导入之后啥也没干,控制台会自动输出life is short,we need python
在这里我的思考,python中万物皆是对象,Python中的包也是一个对象,这个__init__.py估计就是这个包对象的def __init__(self), 当我们生成这个python的对象,这个对象的构造函数会自动运行
2.2 __init__.py的用法
OK,现在我们在test04文件夹下新建三个python的模块
- k1
- k2
- k3
当这四个模块都需要使用random(随机数模块), math(数学模块),如下图:
按照我们的想法,是不是每个模块都导入一次random包和math包呢?那这样每个包导入一次,重复的代码也太多了8,没错,我们的__init__.py就可以用来解决重复代码这个问题,我们只需要在__init__.py导入一次这些random和math包,k1, k2, k3就能够使用这些模块了
测试代码如下:
__init__.py
import random import math
k1.py
from test04 import * print(math.sqrt(9)) #计算出9的算数平方根 print(random.randint(10, 50)) #生成一个10到50的随机数
k2.py
from test04 import * print(math.sqrt(9)) #计算出9的算数平方根 print(random.randint(10, 50)) #生成一个10到50的随机数
k3.py
from test04 import * print(math.sqrt(9)) #计算出9的算数平方根 print(random.randint(10, 50)) #生成一个10到50的随机数
我们只需要导入在__init__.py中导入一次这些需要在k1, k2, k3都需要使用的公共的包,就可以避免k1, k2, k3重复多次的导入公共的包了
2.3 导入嵌套bug
现在我们在test04包中有了k1, k2, k3三个模块,如下图:
因为python是一门动态的语言,所以在程序执行的过程中,我们运行任何一个模块,比如我们运行k1,
- 编译器发现自己需要导入k2这个模块,于是编译器去就导入k2
- 编译器导入k2的时候,发现k2需要去导入k3这个模块,然后编译器就去导入k3
- 编译器导入k3的时候,发现居然要导入k1,然后k1不得不去导入k1
- 编译器去导入k1的时候,发现k1要导入k2,然后编译器去导入k2
- ……
- ……
- 编译器:“我fffffffff”
这个时候,我们运行任何模块都会陷入死循环
如下:
3. pip – 模块管理工具
以下是bilibili献给新一代的演讲《后浪》名场面,用来形容python再合适不过了
python之所以强大,很主要的一个原因是python本身具有很强大的python库,并且因为python本身是开源的,所以第三方库特别的多,使得管理文档,执行单元测试、数据库、web浏览器、电子邮件、密码学、图形用户界面和更多的东西被更容易调用。我们只需要去调用别人已经写好的库并且使用,不用关心具体的实现,如果要是什么都要自己实现,那……
pip就是这样一个工具,用来下载安装别人提供的第三方库,通过pip我们就可以尽情的享用现代文明的成果。
在pycharm中,点击下图中的终端,就可以使用pip命令
以下是pip相关的一些命令
命令
- pip install / uninstall 第三方库名
查看我们都白嫖了那些库
- pip list
看看我们还有那些库能够白嫖
- help(‘modules’) ,单引号别忘记了
至于安装速度慢的问题,因为是国外的网站,所以访问比较慢,换成国内的镜像网站,比如阿里源,或者清华源就可以解决下载速度慢的问题;以下是python换源详细教程,用以解决第三方库安装速度慢的问题;
如果pip版本较低,需要更新,复制以下代码:
python -m pip install --upgrade pip
在终端运行就可以把pip升级到最新版本;