Python实现创建模块的方法详解

目录
  • 楔子
  • __import__
  • importlib.machinery
  • 通过 module 类创建模块
  • 将一个类的实例变成一个模块
  • 小结

楔子

导入一个模块,我们一般都会使用 import 关键字,但有些场景下 import 难以满足我们的需要。所以除了 import 之外还有很多其它导入模块的方式,下面就来介绍一下。

__import__

这是一个内置函数,解释器在 import 的时候,实际上就执行了这个函数。

# import os 等价于如下方式
os = __import__("os")
print(os)  # <module 'os' from 'C:\\python38\\lib\\os.py'>

# 但是这种方式不能多级导入
path = __import__("os.path")
print(path)  # <module 'os' from 'C:\\python38\\lib\\os.py'>
# 可以看到,导入的仍是 os,而不是 os.path

# 如果想导入子模块,需要一个参数 fromlist
# 我们给它传一个非空列表即可
path = __import__("os.path", fromlist=[""])
print(path)  # <module 'ntpath' from 'C:\\python38\\lib\\ntpath.py'>

但是官方不建议使用这个函数,因为它是专门给解释器用的,我们可以使用一个模块。

import importlib

os = importlib.import_module("os")
print(os)  # <module 'os' from 'C:\\python38\\lib\\os.py'>

# 可以多级导入
path = importlib.import_module("os.path")
print(path)  # <module 'ntpath' from 'C:\\python38\\lib\\ntpath.py'>

所以当导入的模块名以字符串的形式存在时,或者模块名不符合规范时,就可以使用这种方式。

importlib.machinery

importlib.machinery 里面提供了三种 Loader,可以让我们以打开文件的方式导入一个模块。

from importlib.machinery import (
    SourceFileLoader,  # 导入源文件
    SourcelessFileLoader,  # 导入 pyc 文件
    ExtensionFileLoader  # 导入扩展文件
)

# 参数一:给模块起个名字
# 参数二:文件路径
os = SourceFileLoader(
    "我是 os 模块",
    r"C:\python38\lib\os.py"
).load_module()
print(os)
"""
<module '我是 os 模块' from 'C:\\python38\\lib\\os.py'>
"""
print(os.path.join("video", "overwatch", "hanzo.mp4"))
"""
video\overwatch\hanzo.mp4
"""

# 我们看到结果一切正常,但有一点需要注意
# 如果是导入包的话,那么要导入包里面的 __init__.py 文件
pd = SourceFileLoader(
    "我是 pandas 模块",
    r"C:\python38\lib\site-packages\pandas\__init__.py"
).load_module()
print(pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}))
"""
   a  b
0  1  4
1  2  5
2  3  6
"""

# 如果只写到 pandas,那么会抛出 PermissionError
# 因为我们不能把目录当成文件来读取
# 至于 import 一个包,本质上也是加载包内部的 __init__.py 
# 但这里需要我们显式地加上 __init__.py

同理加载 pyc 和 pyd 也是类似的,但需要注意的是,加载普通文件和 pyc 文件时,我们可以随便起名字,也就是第一个参数任意。但对于 pyd 文件,第一个参数必须和 pyd 文件的名字保持一致。

通过 module 类创建模块

Python 一切皆对象,模块自然也不例外。既然是对象,那么必然就会有相应的类来实例化它。

import os
import hashlib
import numpy

# os.__class__ 等价于 type(os)
print(os.__class__)  # <class 'module'>
print(hashlib.__class__)  # <class 'module'>
print(numpy.__class__)  # <class 'module'>

在 Python 里面,我们一般会把单独的可导入文件称之为模块,把包含多个模块的目录称之为。通过模块和包,我们可以对项目进行功能上的划分,分门别类地进行组织。

但不管是模块、还是包,它们都是 module 这个类的实例对象,打印结果也能说明这一点。所以从解释器的角度来看的话,模块和包区分的并没有那么明显,直接把包看做是包内部的 __init__.py 即可。

既然模块的类型是 class module,那么我们是不是也可以通过调用类型对象的方式创建呢?显然是可以的,但是 module 这个类解释器没有暴露给我们,直接用的话会提示变量 module 未定义。所以只能先随便导入一个模块,然后通过 type 函数或者 __class__ 属性获取。

# 不过 types 模块内部已经帮我们做好了
# ModuleType = type(sys)
from types import ModuleType

print(ModuleType)  # <class 'module'>

# 类对象有了,下面就可以创建了
# module 类接收两个参数
# 参数一:模块的名字,必须传递
# 参数二:模块的 doc,不传默认为 None
satori = ModuleType("古明地觉", "模块的名字是一个女孩,她来自地灵殿")
print(satori)  # <module '古明地觉'>
print(satori.__doc__)  # 模块的名字是一个女孩,她来自地灵殿

# 但此时模块里面是没啥东西的,我们加一些属性吧
# 操作模块本质上是在操作它的属性字典
code = """
age = 16

def foo():
    return "^_^"
"""
# 执行 code,结果会体现在 satori 的属性字典中
exec(code, satori.__dict__)
print(satori.age)  # 16
print(satori.foo())  # ^_^

需要注意的是里面 exec 函数,它会把字符串当成代码来执行,所以这就要求字符串的来源必须是可靠的,我们能够确保不会出现恶意内容。而如果是用户传递的字符串,那么绝不能用 exec 来执行,当然 eval 也是同理。

然后是 exec 的第二个参数,表示执行时的名字空间,默认是全局名字空间。所以当不指定第二个参数时,exec(code) 相当于创建了两个全局变量:age 和 foo。

code = """
age = 16

def foo():
    return "^_^"
"""

exec(code)
print(age)  # 16
print(foo())  # ^_^

但是我们在执行的时候,将它换成了 satori.__dict__,所以结果相当于给模块添加了两个变量,或者说属性。

将一个类的实例变成一个模块

如果想将一个类的实例变成模块,那么这个类应该继承 ModuleType。

import sys
from types import ModuleType

class A(ModuleType):

    def __init__(self, module_name):
        super().__init__(module_name)

    def __getattr__(self, item):
        return f"不存在的属性: {item}"

    def __setattr__(self, key, value):
        self.__dict__[key] = value

    def __str__(self):
        return f"<module '{self.__name__}' from '我来自于虚无'>"

a = A("我是 A")
print(a)  # <module '我是 A' from '我来自于虚无'>
print(a.__name__)  # 我是 A
print(a.xx)  # 不存在的属性: xx
a.xx = "xx"
print(a.xx)  # xx

# 加入到 sys.modules 中
sys.modules["嘿嘿"] = a
import 嘿嘿
print(嘿嘿.xx)  # xx
print(嘿嘿.yy)  # 不存在的属性: yy

是不是很好玩呢?

小结

以上就是加载模块的几种方式,主要用途如下:

  • 导入一个在 sys.path 中的模块,并且模块名已知,那么直接使用 import 关键字即可;
  • 导入一个在 sys.path 中的模块,但模块名是运行时的一个字符串,那么使用 importlib 模块的 import_module 函数;
  • 导入一个不在 sys.path 中的模块,使用 importlib.machinery 的各种 Loader,只要把模块的路径传进去即可。当然啦,位于 sys.path 中的模块也可以使用该方法,但显然此时使用前两种更为方便;
  • 直接创建一个模块,通过继承 module 类来实现,并且还可以加入到 sys.modules 中。Python 有一个第三方模块叫 sh,顾名思义是用来执行 Linux Shell 命令的,它内部就使用了继承 module 类来创建模块的这种方式。但是要知道 module 这个类解释器没有暴露给我们,我们需要通过 type(模块) 或者 模块.__class__ 的方式获取;

到此这篇关于Python实现创建模块的方法详解的文章就介绍到这了,更多相关Python创建模块内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python自定义模块的创建与使用

    目录 一.前言 二.模块的概述 三.自定义模块 1.创建模块 2.使用import语句导入模块 3.使用from…import语句导入模块 附:python打包用户自定义模块 总结 一.前言 在Python提供了强大的模块支持,主要体现为不仅在Python标准库中包含了大量的模块(称为标准模块),而且还有很多第三方模块,另外开发者自己也可以开发自定义模块.通过这些强大的模块支持,将极大地提高我们的开发效率. 二.模块的概述 模块的英文是Module,可以认为是一盒(箱)主题积木,通过它可以拼出某

  • Python创建模块及模块导入的方法

    本文实例讲述了Python创建模块及模块导入的方法.分享给大家供大家参考.具体分析如下: python学习手册中写道: 定义模块,只要使用文本编辑器,把一些python代码输入到文本中,然后以.py为后缀名进行保存,任何此类文件都会被认为是python模块. 比如说,下面的代码输入到一个文件中,就可以看作是一个模块: def printme(var): print var if __name__ == '__main__': printme(1) 假设说输入到a.py中,那么import a就可

  • Python实现创建模块的方法详解

    目录 楔子 __import__ importlib.machinery 通过 module 类创建模块 将一个类的实例变成一个模块 小结 楔子 导入一个模块,我们一般都会使用 import 关键字,但有些场景下 import 难以满足我们的需要.所以除了 import 之外还有很多其它导入模块的方式,下面就来介绍一下. __import__ 这是一个内置函数,解释器在 import 的时候,实际上就执行了这个函数. # import os 等价于如下方式 os = __import__("os

  • python安装sklearn模块的方法详解

    可直接用这行命令!: pip install -U scikit-learn 其他命令: (1)更新pip python -m pip install --upgrade pip (2)安装 scipy 在网址http://www.lfd.uci.edu/~gohlke/pythonlibs/ 中找到你需要的版本scipy 例如windows 64 位 Python2.7 对应下载:scipy-0.18.0-cp27-cp27m-win_amd64.whl cd 下载scipy 目录下,安装 p

  • python的re模块使用方法详解

    一.正则表达式的特殊字符介绍 正则表达式 ^ 匹配行首 $ 匹配行尾 . 任意单个字符 [] 匹配包含在中括号中的任意字符 [^] 匹配包含在中括号中的字符之外的字符 [-] 匹配指定范围的任意单个字符 ? 匹配之前项的1次或者0次 + 匹配之前项的1次或者多次 * 匹配之前项的0次或者多次 {n} 匹配之前项的n次 {m,n} 匹配之前项最大n次,最小m次 {n,} 配置之前项至少n次 二.re模块的方法介绍 1.匹配类方法 a.findall方法 # findall方法,该方法在字符串中查找

  • 通过字符串导入 Python 模块的方法详解

    我们平时导入第三方模块的时候,一般使用的是 import 关键字,例如: import scrapy from scrapy.spider import Spider 但是如果各位同学看过 Scrapy 的 settings.py 文件,就会发现里面会通过字符串的方式来指定pipeline 和 middleware,例如: DOWNLOADER_MIDDLEWARES = { 'Test.middlewares.ExceptionRetryMiddleware': 545, 'Test.midd

  • python 环境变量和import模块导入方法(详解)

    1.定义 模块:本质就是.py结尾的文件(逻辑上组织python代码)模块的本质就是实现一个功能 文件名就是模块名称 包: 一个有__init__.py的文件夹:用来存放模块文件 2.导入模块 import 模块名 form 模块名 import * from 模块名 import 模块名 as 新名称 3. 导入模块本质 import 模块名 ===> 将模块中所有的数据赋值给模块名,调用时需要模块名.方法名() from 模块名 import 方法名 ==>将该方法单独放到当前文件运行一遍

  • Python中Selenium模块的使用详解

    Selenium的介绍.配置和调用 Selenium(浏览器自动化测试框架) 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 10, 11),Firefox,Safari,Google Chrome,Opera等.这个工具的主要功能包括:测试浏览器的兼容性--测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上.测试系统功能--创建回归测试检验软件功能和用户需求.支持自动录制动作和自动生成 .

  • python urllib.request模块的使用详解

    python的urllib模块提供了一系列操作url的功能,可以让我们通过url打开任意资源.其中比较常用的就是request模块,本篇主要介绍requset模块. urllib子模块 urllib.request 打开或请求url urllib.error 捕获处理请求时产生的异常 urllib.parse 解析url urllib.robotparser 用于解析robots.txt文件 robots.txt是一种存放于网站根目录下文本文件,用来告诉网络爬虫服务器上的那些文件可以被查看.又被

  • Python对象类型及其运算方法(详解)

    基本要点: 程序中储存的所有数据都是对象(可变对象:值可以修改 不可变对象:值不可修改) 每个对象都有一个身份.一个类型.一个值 例: >>> a1 = 'abc' >>> type(a1) str 创建一个字符串对象,其身份是指向它在内存中所处的指针(在内存中的位置) a1就是引用这个具体位置的名称 使用type()函数查看其类型 其值就是'abc' 自定义类型使用class 对象的类型用于描述对象的内部表示及其支持的方法和操作 创建特定类型的对象,也将该对象称为该类

  • Python内建模块struct实例详解

    本文研究的主要是Python内建模块struct的相关内容,具体如下. Python中变量的类型只有列表.元祖.字典.集合等高级抽象类型,并没有像c中定义了位.字节.整型等底层初级类型.因为Python本来就是高级解释性语言,运行的时候都是经过翻译后再在底层运行.如何打通Python和其他语言之间的类型定义障碍,Python的内建模块struct完全解决了所有问题. 知识介绍: 在struct模块中最最常用的三个: (1)struct.pack:用于将Python的值根据格式符,转换为字符串(因

  • python os.path模块常用方法实例详解

    os.path模块主要用于文件的属性获取,在编程中经常用到,以下是该模块的几种常用方法.更多的方法可以去查看官方文档:http://docs.python.org/library/os.path.html 1.os.path.abspath(path) 返回path规范化的绝对路径. >>> os.path.abspath('test.csv') 'C:\\Python25\\test.csv' >>> os.path.abspath('c:\\test.csv') '

随机推荐