Python 模块

自定义一个简单的模块

例如,我们有一个文件utils.py,用来给字符串加密:

import hashlib

def encrypt(data):
    """ 数据加密 """
    hash_object = hashlib.md5()
    hash_object.update(data.encode('utf-8'))
    return hash_object.hexdigest()

我们在另一个文件run.py中可以引入这个utils.py文件中的方法。

from utils import encrypt

user = input("请输入用户名:")
pwd = input("请输入密码:")
md5_password = encrypt(pwd)

message = "用户名:{},密码:{}".format(user, md5_password)
print(message)

目录结构:

└─  utils.py
└── run.py

包和模块

在开发简单的程序时,使用一个py文件就可以搞定,如果程序比较庞大,需要些10w行代码,此时为了,代码结构清晰,将功能按照某种规则拆分到不同的py文件中,使用时再去导入即可。另外,当其他项目也需要此项目的某些模块时,也可以直接把模块拿过去使用,增加重用性。

├── commons
│   ├── convert.py
│   ├── page.py
│   └── utils.py
└── run.py

如果run.py想使用文件夹下面的模块的时候,可以进行下面的方式:

from common.utils import encrypt
from common.page import pagination
from common.convert import int_to_string

v1 = encrypt('tom')
v2 = pagination()
v3 = int_to_string()

向这样把一些功能放到一个文件夹中进行调用,这个文件夹就成为。包中的py文件就是模块

中一般都需要一个__init__.py文件,在这个__init__.py文件中,往往会写一些关于这个的注释。表名这个包的作用是什么。

当导入这个中的模块时,这个__init__.py文件会自动加载并执行。

例如在__init__.py文件中,添加内容如下:

"""
    包的作用
"""
VERSION = 0.1
print(VERSION)

目录结构如下:

├── commons
│   ├── __init__.py
│   ├── convert.py
│   ├── page.py
│   └── utils.py
└── run.py

在运行run.py时,会自动运行__init__.py
输出如下:

0.1

那么我们加载了三个模块,为什么只运行了一次__init__呢?

原因是,对于python中所有的模块和包,如果加载过一次之后,下一次再次使用的时候,就不会再加载了,因为第一次加载之后就已经放入到内存中了。

导入包

文件结构:

├── commons
│   ├── __init__.py
│   ├── convert.py
│   ├── page.py
│   └── utils.py
└── run.py

其中utils.py中的文件内容如下:

import hashlib

def encrypt(data):
    """ 数据加密 """
    hash_object = hashlib.md5()
    hash_object.update(data.encode('utf-8'))
    return hash_object.hexdigest()

def f1(data):
    return 1

run.py中的内容如下:

from common.utils import encrypt
from common.utils import f1

当我们执行from common.utils import encrypt的时候,会将utils文件中所有的内容加载到内存,然后执行from common.utils import f1时,就不会再次导入utils文件的内容了。

导入电脑中另一个位置的包

当前的项目所在的位置是C盘,如果我要导入一个D盘的模块,应该如何做?

例如我的run.py文件内容如下:

# D:/xx/x1.py
from xx.x1 import x0

这种情况下,即使D盘下存在/xx/x1.py文件,也无法导入成功。提示xx模块找不到。

因为Python在找模块时,只会去找指定的路径中找相应的模块,而D盘不在指定的路径下,所以提示找不到模块。

查看Python内部默认的模块寻找路径,都模块或包时,都会按照指定顺序逐一去特定的路径下查找:

import sys

print(sys.path)

输出结果为:

['c:\\Users\\vincent\\Desktop\\code\\neimeng-python\\test',  #'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\python310.zip', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\DLLs', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\lib', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\lib\\site-packages']

而我们的自定义的模块所在的路径不在上面的路径下,所以提示导入不成功。

如果我们将自定义的模块路径添加到sys.path中,就可以调用我们的模块了。

import sys
sys.path.append(r'D:\xx')
from xx.x1 import x0
print(sys.path)

导入模块名冲突

我们知道Python内部有一个模块random,如果我们在项目中自己定义了一个模块也叫random,那么会出现什么情况。

项目结构:

├── random.py
└── run.py

其中random.py的内容是个空文件。run.py内容如下:

import random

v = random.randint(1, 10)
print(v)

那么执行run.py时,就会提示报错:AttributeError: module 'random' has no attribute 'randint'

说明他从我们自己定义的random.py中去加载了。

原因是,我们的模块加载顺序是根据sys.path列表中的顺序来确定的,而列表的路径顺序如下:

['c:\\Users\\vincent\\Desktop\\code\\neimeng-python\\test',  #'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\python310.zip', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\DLLs', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\lib', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng', 'C:\\Users\\vincent\\anaconda3\\envs\\neimeng\\lib\\site-packages']

可以看到,我们当前的项目所在文件夹在列表中的开头,所以会加载我们定义的模块。

  1. 所以我们以后在写模块名称时,千万不能与内置和第三方的模块同名。
  2. 项目的执行文件一般都在项目的根目录,结构如下:
├── commons
│   ├── __init__.py
│   ├── convert.py
│   ├── page.py
│   └── utils.py
└── run.py

也就是说,run.py要放到项目的根目录。

假设run.py放到与commons同一级的文件夹中:

├── commons
│   ├── __init__.py
│   ├── convert.py
│   ├── page.py
│   └── utils.py
├── bin
│   ├── run.py

这样运行run.py时,当前的run.py所在的目录加入到sys.path中,而我们所导入的模块就不在sys.path中,所以就无法导入commons中的模块了。

如果还想使用commons下的模块时,需要将这个路径加入到sys.path列表中。 修改run.py,在代码的最上面加入:

import sys
sys.path.append('commons的绝对路径')

但是这种方式,不利于移植,如果部署到别的地方,那么路径一旦发生变化,将不能运行了。

解决办法就是,使用相对路径。os.path.abspath(__file__)可以获取当前文件的绝对路径,因此如果想获取common的路径,可以使用sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

需要注意的是,如果使用pycharm,那么pycharm会自动将当前项目目录加入到sys.path列表中。

模块导入的方式

上面我们介绍到,我们项目的执行入口一般都会放入到项目的根目录下。

导入模块分为下面两种方式:

  • import xxx
  • from xx import xxx

import方式导入
这种方式可以导入一个模块,并将这个模块中的所有内容加载到内存。

├── commons
│   ├── __init__.py
│   ├── convert.py
│   ├── page.py
│   └── utils.py
├── test_module.py
└── run.py

当导入与run.py同一级的test_module.py模块时:

import test_module

data = test_module.func1()

当导入与run.py不同一级的utils.py模块时:

import common.utils

data = common.utils.encrypt()

为了方便,可以给import的模块起一个别名:import common.utils as f1

import除了导入一个python模块(Python文件)之外,还可以导入一个包(文件夹)。这仅仅能导入包中的__init__.py中的内容,而不能把包中的所有模块都导入进来。

from xx import xxx 方式导入
使用这种方式,比import导入的方式不管是从粒度上,还是从级别上都更细。

from test_module import func1:表示导入的是test_module模块的func1函数。
from test_module import *:表示导入的是test_module中所有的函数。
from common.utils import encrypt:表示导入common.utils 模块下的encrypt函数。

也可以直接导入一个模块:
from common import utils:表示导入的是common下的utils所有函数。通过utils.encrypy()来执行模块的函数。

通过from方式导入一个包,假如common下面还有一个文件夹models,其中又包含其他一些Python文件,那么执行from common import models会自动执行models里面的__init__.py文件。

from也支持相对导入。例如convert.py模块中也需要导入utils.py模块中的函数,就可以使用相对导入:在convert.py中导入from . import utils。 (一般不推荐,而且只能用在包里面,不能用在项目根目录下导入同级的模块。也就是说run.py不能导入test_module。)

两种导入方式的应用场景

import方式:适合导入项目根目录下的模块。
from方式:适合导入某个函数,或者嵌套的包和模块。

需要注意的是,使用from方式导入一个模块中的函数,并不能节省内存,因为from导入一个模块,就会把当前模块全部加载到内存中

导入模块时一般遵循的规范

  1. 一般将模块的注释信息,写到文件顶部。
  2. 一般先导入内置模块,再导入第三方模块,最后导入自定义模块。他们之间用空行进行分割。
    例如:
import os
import sys

import requests
import elasticsearch

from common.utils import encrypt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值