文章目录
模块
模块的概念
模块是 Python 程序架构的一个核心概念
- 每一个以扩展名
py
结尾的Python
源代码文件都是一个 模块 - 模块名 同样也是一个 标识符,需要符合标识符的命名规则
- 在模块中定义的 全局变量 、函数、类 都是提供给外界直接使用的工具
- 模块 就好比是 工具包,要想使用这个工具包中的工具,就需要先 导入 这个模块
新建一个模块
%%writefile produce_code.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__date__ = '2018/11/29'
__author__ = 'sunchengquan'
__mail__ = 'sunchq14@lzu.edu.cn'
__doc__ = 'a produce code module'
import random
title = 'public'
_title = 'private'
def test():
checkcode=''
for i in range(5):
current=random.randrange(0,5)
if current == i:
tmp=chr(random.randint(65,90))
else:
tmp=random.randint(0,9)
checkcode+=str(tmp)
return checkcode
class Test:
pass
def _main():
print("-------main-------")
if __name__=='__main__':
_main()
Overwriting produce_code.py
- 第1行和第2行是标准注释,第1行注释可以让这个produce_code.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
__date__
变量,编写模块的日期__author__
变量,把作者写进去__mail__
变量,邮件信息__doc__
,文档注释- import , 导入模块,后面详细介绍
- title ,定义一个全局变量,值为‘public’
_title
,定义一个私有变量,值为‘private’- 定义一个名为test的函数
- 定义一个名为Test的类
- 定义一个名为
_main
的私有函数 __name__
,被其他文件导入的,__name__ 就是 模块名,当前执行的程序 __name__ 是 __main__
执行produce_code.py
!python produce_code.py
-------main-------
作用域
上面提到的类似__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途, 我们自己的变量一般不要用这种变量名
正常的函数和变量名是公开的(public),可以被直接引用
类似_xxx
和__xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc
,__abc
等;
执行如下代码:
%%writefile test.py
import produce_code
print(produce_code.__date__)
print(produce_code.__author__)
print(produce_code.__mail__)
print(produce_code.__doc__)
print(produce_code.test())
print(produce_code.title)
print(produce_code._title)
print(produce_code._main())
Overwriting test.py
!python test.py
2018/11/29
sunchengquan
sunchq14@lzu.edu.cn
a produce code module
26838
public
private
-------main-------
None
我去,不是私有变量,私有函数不能访问吗?
换一种导入方式
%%writefile test.py
from produce_code import *
# print(__date__)
# print(__author__)
# print(__mail__)
# print(__doc__)
print(test())
print(title)
print(_title)
print(_main())
Overwriting test.py
!python test.py
646S6
public
Traceback (most recent call last):
File "test.py", line 9, in <module>
print(_title)
NameError: name '_title' is not defined
private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量不会导入私有变量和私有函数,还有特殊变量
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public
模块的导入方式
import 导入
%%writefile 测试模块1.py
# 全局变量
title = "模块1"
# 函数
def say_hello():
print("我是 %s" % title)
# 类
class Dog(object):
pass
Overwriting 测试模块1.py
%%writefile 测试模块2.py
# 全局变量
title = "模块2"
# 函数
def say_hello():
print("我是 %s" % title)
# 类
class Cat(object):
pass
Overwriting 测试模块2.py
import 测试模块1
import 测试模块2
测试模块1.say_hello()
测试模块2.say_hello()
dog = 测试模块1.Dog()
print(dog)
cat = 测试模块2.Cat()
print(cat)
我是 模块1
我是 模块2
<测试模块1.Dog object at 0x7effc7b372b0>
<测试模块2.Cat object at 0x7effb4fcfc50>
import同时指定别名
import 测试模块1 as DogModule
import 测试模块2 as CatModule
DogModule.say_hello()
CatModule.say_hello()
dog = DogModule.Dog()
print(dog)
cat = CatModule.Cat()
print(cat)
我是 模块1
我是 模块2
<测试模块1.Dog object at 0x7effb4fcfeb8>
<测试模块2.Cat object at 0x7effc7b372b0>
from…import 导入
from 测试模块1 import Dog
from 测试模块2 import say_hello
say_hello()
wangcai = Dog()
print(wangcai)
我是 模块2
<测试模块1.Dog object at 0x7effb4fc0748>
如果两个模块中的函数名相同怎么办?
## 从 模块 导入 所有工具
from 测试模块1 import *
from 测试模块2 import *
print(title)
say_hello()
wangcai = Dog()
print(wangcai)
模块2
我是 模块2
<测试模块1.Dog object at 0x7effb4f925c0>
如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数
注意
这种方式不推荐使用,因为函数重名并没有任何的提示,出现问题不好排查
from 测试模块1 import say_hello
from 测试模块2 import say_hello as module2_say_hello
say_hello()
module2_say_hello()
我是 模块1
我是 模块2
动态导入模块
- 主要用于反射或者延迟加载模块。
建一个包,名为lib
__init__.py
- test.py
!mkdir -p lib
%%writefile ./lib/__init__.py
Overwriting ./lib/__init__.py
%%writefile ./lib/test.py
class Person(object):
def __init__(self):
self.name = 'sunchengquan'
# if __name__ == "__main__":
obj = Person()
print(obj.name)
Overwriting ./lib/test.py
__import__
说明:
__import__(module)
相当于import module
使用 __import__
动态以字符串形式导入lib下的test模块
lib = __import__("lib.test")
obj = lib.test.Person()
print(obj.name)
sunchengquan
sunchengquan
%%writefile reflection.py
lib = __import__("lib.test")
print(hasattr(lib.test,'Person'))
instance = getattr(lib.test,'Person')
print(instance().name)
Writing reflection.py
!python reflection.py
sunchengquan
True
sunchengquan
import importlib
实例还是上面的lib.test模块,这里使用importlib进行动态导入(这个方法好理解,也是官方建议使用的)
import importlib
test = importlib.import_module("lib.test")
print(test.Person().name)
sunchengquan
模块的搜索顺序
Python
的解释器在 导入模块 时,会:
- 搜索 当前目录 指定模块名的文件,如果有就直接导入
- 如果没有,再搜索 系统目录
在开发时,给文件起名,不要和 系统的模块文件 重名
%%writefile 模块的搜索顺序.py
import random
#Python中每一个模块都有一个内置属性 `__file__` 可以 查看模块的完整路径
print(random.__file__)
rand = random.randint(0, 10)
print(rand)
Overwriting 模块的搜索顺序.py
!python 模块的搜索顺序.py
/root/miniconda3/lib/python3.7/random.py
0
在当前目录下新建一个名为random的模块
%%writefile random.py
def randint(num1,num2):
return num1+10+num2
Writing random.py
!python 模块的搜索顺序.py
!rm -rf random.py
/root/python_code_scq/python_basic/random.py
20
注意:如果当前目录下,存在一个
random.py
的文件,程序就无法正常执行了!
- 这个时候,
Python
的解释器会 加载当前目录 下的random.py
而不会加载 系统的random
模块
__name__
属性
实际开发场景
- 在实际开发中,每一个模块都是独立开发的,大多都有专人负责
- 开发人员 通常会在 模块下方 增加一些测试代码
- 仅在模块内使用,而被导入到其他文件中不需要执行
__name__
属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行!
__name__
是Python
的一个内置属性,记录着一个 字符串- 如果 是被其他文件导入的,
__name__
就是 模块名 - 如果 是当前执行的程序
__name__
是__main__
在很多 Python
文件中都会看到以下格式的代码:
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
# 在代码的最下方
def main():
# ...
pass
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
main()
%%writefile name_test.py
if __name__ == '__main__':
print('程序自身在运行')
else:
print('我来自另一模块')
Writing name_test.py
!python name_test.py
程序自身在运行
import name_test
我来自另一模块
不同级目录下的模块调用
实例演示
- 新建目录sun
- 在sun目录下新建一个模块
module_sun.py
- 在sun目录下新建目录module
- 在./sun/module目录下新建一个模块
main.py
!mkdir -p sun
%%writefile ./sun/module_sun.py
name='sun'
def say_hello():
print('hello sun')
def logger():
print('in the module sun')
def running():
pass
Writing ./sun/module_sun.py
!mkdir -p sun/module
%%writefile ./sun/module/main.py
import module_sun
from module_sun import logger as f
def logger():
print('in the main')
module_sun.logger()
logger()
f()
print(module_sun.name)
module_sun.say_hello()
Overwriting ./sun/module/main.py
目录结构如下:
!tree ./sun
./sun
├── module
│ └── main.py
├── module_sun.py
└── __pycache__
└── module_sun.cpython-37.pyc
2 directories, 3 files
运行mian.py
!python ./sun/module/main.py
Traceback (most recent call last):
File "./sun/module/main.py", line 1, in <module>
import module_sun
ModuleNotFoundError: No module named 'module_sun'
如何解决呢?
%%writefile ./sun/module/main.py
import sys,os
print("之前的环境路径",sys.path)
x=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(x)
print("之后的环境路径",sys.path)
import module_sun
from module_sun import logger as f
def logger():
print('in the main')
module_sun.logger()
logger()
f()
print(module_sun.name)
module_sun.say_hello()
Overwriting ./sun/module/main.py
!python ./sun/module/main.py
之前的环境路径 ['/root/python_code_scq/python_basic/sun/module', '/root/miniconda3/lib/python37.zip', '/root/miniconda3/lib/python3.7', '/root/miniconda3/lib/python3.7/lib-dynload', '/root/miniconda3/lib/python3.7/site-packages']
之后的环境路径 ['/root/python_code_scq/python_basic/sun/module', '/root/miniconda3/lib/python37.zip', '/root/miniconda3/lib/python3.7', '/root/miniconda3/lib/python3.7/lib-dynload', '/root/miniconda3/lib/python3.7/site-packages', '/root/python_code_scq/python_basic/sun']
in the module sun
in the main
in the module sun
sun
hello sun
包
概念
- 包 是一个 包含多个模块 的 特殊目录
- 目录下有一个 特殊的文件
__init__.py
- 包名的 命名方式 和变量名一致,小写字母 +
_
好处
- 使用
import 包名
可以一次性导入 包 中 所有的模块
案例演练
- 新建一个
message
的 包 - 在目录下,新建两个文件
send_message
和receive_message
- 在
send_message
文件中定义一个send
函数 - 在
receive_message
文件中定义一个receive
函数 - 在外部直接导入
message
的包
!mkdir -p message
%%writefile ./message/send_message.py
def send(text):
print("正在发送 %s..." % text)
Writing ./message/send_message.py
%%writefile ./message/receive_message.py
def receive():
return "这是来自 100xx 的短信"
Writing ./message/receive_message.py
%%writefile message_test.py
import message
message.send_message.send("hello")
txt = message.receive_message.receive()
print(txt)
Writing message_test.py
!python message_test.py
Traceback (most recent call last):
File "message_test.py", line 4, in <module>
message.send_message.send("hello")
AttributeError: module 'message' has no attribute 'send_message'
__init__.py
- 要在外界使用 包 中的模块,需要在
__init__.py
中指定 对外界提供的模块列表
%%writefile ./message/__init__.py
print('from the package message')
from . import send_message
from . import receive_message
Overwriting ./message/__init__.py
!python message_test.py
from the package message
正在发送 hello...
这是来自 100xx 的短信
%%writefile ./message/__init__.py
#from hm_messahe import *导入模块时,只有send_message
__all__ = ['send_message']
from . import send_message
from . import receive_message
Overwriting ./message/__init__.py
%%writefile message_test1.py
from message import *
send_message.send("hello")
txt = receive_message.receive()
print(txt)
Overwriting message_test1.py
!python message_test1.py
正在发送 hello...
Traceback (most recent call last):
File "message_test1.py", line 5, in <module>
txt = receive_message.receive()
NameError: name 'receive_message' is not defined
安装第三方模块
一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称
pip
pip install pandas
pip install django==1.8.2
conda
conda install pandas
建议下载miniconda/anaconda,使用conda安装