关于模块
在Python中,模块分为三种:
- 内置模块:打开Python解释器目录,内置木块就在Lib目录下
- 第三方(扩展)模块:第三方模块被统一地存放在笨的Python解释器的Lib/site-packages文件内
- 自定义模块:就是我们自己写的模块了
标准库
Python将常用的实现某类功能的代码组织在一起并起名为模块,随着Python解释器安装到本地,成为内置模块。为了有别于其的模块,内置模块又称为Python 标准库模块。但Python能干的事情实在是太多了,不可能把所有的模块都预先安装在本地的解释器内。
标准库模块被统一放在一个文件夹(目录)内,这个文件夹(目录)又称为Python标准库。
第三方模块
Python的开发者们根据特定的应用场景开发出了特定用途的模块,这些模块经过Python官方审核通过,就可以被广大Python开发者使用,这种线程的并未随着解释器内置的模块被统称为第三方模块。
所有发布的模块(包括第三方模块)均维护在PyP(the Python Package )网站上。
自定义模块
我们自己在项目写的模块,其实一个Python文件就是一个模块,所以模块并没那么神秘!
曲径通幽处,让我们一探究竟。
必要的准备
在做模块之前,要做一些准备。
首先创建一个Module文件夹,文件夹中有m1.py和m2.py两个统计目录文件。我们接下来的示例代码都会围绕module文件内的两个文件展开:
M:\Module\
├─ m1.py
├─ m2.py
└─ a.py
在Python中,一个py文件就是一个模块。但模块名的命名也是要遵循变量的命名规范的,避开关键字和其他的模块名一致的名字,比如自己定义的模块名不要携程def.py 或者 time.py 等这种方式。
# m1.py
x = 1
l = [2, 3]
def func(x):
print("m1模块输出", x)
# m2.py
print("这个是模块2")
x = 2
y = [3, 4]
def bar(x):
print("m2模块输出", x)
上例m1.py 为一个模块。当这个模块被别的模块调用时,变量x、y和func都会便成为m1的属性,也就是说位于m1模块的全局作用域内的变量、函数名、类名都将成为m1的属性,在别的模块通过 module.attribute 方式调用。
模块的导入
模块的导入使用 import 语句,其语法如下:
import module_name # 推荐
import module_name1, module_name2...module_name n # 不推荐
# 多个模块导入推荐如下方法
import module_name
import module_name2
import module_name3
import语句将模块整体导入到当前模块中,如果一次导入多个模块可以用逗号隔开,但并不推荐这种方式
# a.py
import m1
print(m1.x, m1,l)
'''
1 [2, 3]
'''
上例中,通过import导入包中的所有属性
from m1 import func
func(10)
print(x, l)
'''
NameError: name 'x' is not defined
m1模块输出 10
'''
上例中用from导入包中的一个函数,导入哪个属性,哪个属性就可以使用,其他的属性或者变量是不可以被调用。上例,导入m1包中的func函数,就能用func函数,没有导入变量x 和 列表l 所以在用print函数输出x 和 l 是报错。
from语句的弊端
from语句会让变量名变得模糊。如果导入多个模块,那么使用from语句很难分辨某个变量来自哪个模块:
# a.py
from m1 import *
from m2 import *
print(x)
print(y)
上例中,能看出x 和 y 归属于哪个模块吗? 相较于import语句的1m.x, m2.y,单独的x 对我们来说并不能提供太多的有限信息。而且from 语句也潜在破坏名称空间。
来看示例:
# a.py
from b import x
x = 2
print(x)
"""
结果输出2
"""
上例中,通过from语句导入过来得变量x被本地的作用域中的变量x悄悄的覆盖掉了。而使用import则有效的避免了此类问题,并且Python提供了as语句来解决这个问题:
import module_name as alvin
from module_name import attribute as yuan # 推荐
通过as语句为模块的某个属性起个别名。
# a.py
from b import x as d
x = 2
print(x)
# 输出 2
print(d)
# 输出 1
上例中,as语句有效地避免重了名问题。通过为模块b中的属性x
起个别名d
,d
指向真实的x。
object.attribute(对象.属性)
当导入模块后,通过模块名点属性,调用其内对应的方法。
在Python中,任何对象都可以通过,来获取该对象的attribute(属性),如果该attribute存在的话。
点号运算是一个表达式,返回该对象匹配的属性名的值。比如s.replace会返回s的replace方法对象。需要注意的时,s再通过点号运算找replace方法时,和作用域法则没有关系。
单个变量s,从当前作用域找到变量s,遵从LEGB法则。
s.f,从当前作用域内找到变量s,然后从s中找属性f,跟作用域无关。
s.f.e,从当前作用域内找到变量s,然后从s中找到属性f,再从属性f中找到属性e。
让模块如脚本一样运行
前面我们说,每个文件都是一个模块,那么每个模块不能仅仅被调用,也要负责本身的逻辑。如在模块a中定义了一个登陆函数,在本模块内实现登陆逻辑:
# a.py a模块
def login(user,password):
print(user,password)
login('xiaomi','6666')
# xiaomi 6666
# b.py
import a
# xiaomi 6666
上例模块a实现了一个登录功能。那么当这个模块被模块b调用时,也会触发该函数的执行。但这并不是我们想要的结果,我们只是想调用这个login函数,实现自己的功能,而不是触发原函数的执行。这该怎么办呢?Python采用__name__
帮助我们解决这个问题:
# a.py
print(__name__, type(__name__))
# __main__ <class 'str'>
# b.py
print(__name__, type(__name__))
# __main__ <class 'str'>
import a
# a <class 'str'>
由上例的执行结果可以总结 __name__的特性:
- 当模块自己被当成脚本执行时,__name__ 返回 __main__ 。
- 当模块被导入而引发内部代码的执行时, __name__ 返回该模块的名字。
根据 __name__ 的特性,来解决我们的问题:
# a.py
def login(user, pwd):
print(user, pwd)
if __name__ == '__main__':
login('xiaomi', '666') # xiaomi 666
# c.py
import a
上例中,通过在模块 a 内判断__name__ 的值的不同,来决定login函数是否执行。也就是说该文件是被当成脚本还是当成模块被导入,而会分别返回不同的值。当 __name__ 等于 __main__ 的时候,表示模块自己在执行代码,就执行 login函数,而当 __name__ 不等于 __main__ 的时候,表示要被别的模块导入,就不通过 if 判断,从而解决了我们的需求。
模块导入规范
在使用这些模块时,也要遵循导入规范:
- 内置模块在模块最上部,第三方模块在中间,自定义模块放在最下面
- import语句、from语句和def语句一样,时可执行的复制语句,那么二者可以嵌套在def或者if 语句中,只有当程序在执行到该语句时,Python才会解析。我们在使用模块前,就像def 一样,要先导入在能使用。。
- import语句时将模块赋值给一个变量,模块内的所有全局作用域的变量都成为该变量的属性。
- from语句时将模块内位于全局作用域下的一个或者多个变量名赋值给一个变量。