• Python 模块


    自定义一个简单的模块

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

    import hashlib
    
    def encrypt(data):
        """ 数据加密 """
        hash_object = hashlib.md5()
        hash_object.update(data.encode('utf-8'))
        return hash_object.hexdigest()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    from utils import encrypt
    
    user = input("请输入用户名:")
    pwd = input("请输入密码:")
    md5_password = encrypt(pwd)
    
    message = "用户名:{},密码:{}".format(user, md5_password)
    print(message)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    目录结构:

    └─  utils.py
    └── run.py
    
    • 1
    • 2

    包和模块

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

    ├── commons
    │   ├── convert.py
    │   ├── page.py
    │   └── utils.py
    └── run.py
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

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

    """
        包的作用
    """
    VERSION = 0.1
    print(VERSION)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    目录结构如下:

    ├── commons
    │   ├── __init__.py
    │   ├── convert.py
    │   ├── page.py
    │   └── utils.py
    └── run.py
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    0.1
    
    • 1

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

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

    导入包

    文件结构:

    ├── commons
    │   ├── __init__.py
    │   ├── convert.py
    │   ├── page.py
    │   └── utils.py
    └── run.py
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    run.py中的内容如下:

    from common.utils import encrypt
    from common.utils import f1
    
    • 1
    • 2

    当我们执行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
    
    • 1
    • 2

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

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

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

    import sys
    
    print(sys.path)
    
    • 1
    • 2
    • 3

    输出结果为:

    ['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

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

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

    import sys
    sys.path.append(r'D:\xx')
    from xx.x1 import x0
    print(sys.path)
    
    • 1
    • 2
    • 3
    • 4

    导入模块名冲突

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

    项目结构:

    ├── random.py
    └── run.py
    
    • 1
    • 2

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

    import random
    
    v = random.randint(1, 10)
    print(v)
    
    • 1
    • 2
    • 3
    • 4

    那么执行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

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

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

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

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

    ├── commons
    │   ├── __init__.py
    │   ├── convert.py
    │   ├── page.py
    │   └── utils.py
    ├── bin
    │   ├── run.py
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

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

    解决办法就是,使用相对路径。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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    import test_module
    
    data = test_module.func1()
    
    • 1
    • 2
    • 3

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

    import common.utils
    
    data = common.utils.encrypt()
    
    • 1
    • 2
    • 3

    为了方便,可以给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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    后端——获取提交的数据(GET、 POST)、获取上传的文件
    混沌策略和单纯形法改进的鲸鱼优化算法-附代码
    《Clean Code》
    电子行业MES管理系统的主要功能与用途
    华为od计试
    (附源码)springboot万花筒 毕业设计 345600
    一文搞懂SpringSecurity---[Day03]自定义登录逻辑+自定义登录页面
    网络安全还有就业前景吗?转行网络安全可行吗?
    PCL交互选择ROI区域
    在pycharm中导入sklearn库失败到成功
  • 原文地址:https://blog.csdn.net/vincent_duan/article/details/127572338