python 导入模块,python模块导入的几种方法
本文不讨论Python的导入机制(底层实现细节),只讨论与模块和包以及导入语句相关的概念。通常,导入模块使用以下语句:
进口.进口.如同.从.进口.从.进口.如同.
一般来说,使用上面的语句导入模块就足够了。但是,在某些特殊情况下,可能需要其他导入方法。比如Python还提供__import__内置函数和importlib模块,实现动态导入。动态导入的好处是可以延迟模块的加载,只有在使用模块时才支持导入动作。
虽然使用__import__函数和importlib模块可以实现模块的延迟加载,但是它的缺点是在任何需要指定模块的地方都必须实现相同的导入语句,维护起来很不方便,非常麻烦。如果能在顶层实现lazy import,是更好的选择,这也是本文的最后一点。
在讨论一些高级用法之前,我们需要理解模块和包的概念。
模块和封装
模块可以理解为Python可以加载和执行的代码文件。代码文件不仅可以。py文件,而且。so和其他类型的文件。Python只有一种模块对象类型,所有模块都是这种类型。为了组织多个模块并提供模块层次结构的命名,Python提供了包的概念。
你可以简单的把包想象成一个文件系统的目录,把模块想象成目录中的代码文件(注意你不能完全这么想,因为包和模块不仅仅来自文件系统,还来自压缩文件、网络等。).类似于文件系统的目录结构,包是分层组织的,包本身也可以包含子包和常规模块。
事实上,一个包可以被看作是一种特殊的模块。比如常规包的目录(下面会介绍常规包的概念)需要包含__init__。py文件。在导入包的时候,这个文件的顶层代码是隐式执行的,就像导入模块的时候执行顶层代码一样,这个文件就跟包代码一样。所以这个包是一种特殊的模块。记住所有的包都是模块,但不是所有的模块都是包。子包和模块具有__path__属性。具体来说,任何包含__path__属性的模块都被视为一个包。的所有模块都有一个名称,类似于标准的属性访问语法,子包及其父包的名称用点分隔。
Python定义了两种类型的包,即常规包和命名空间包。常规包是Python 3.2和更早版本中存在的传统包。常规包是包含__init__的目录。py文件。导入常规包时,__init__。py文件是隐式执行的,它定义的对象被绑定到包名称空间中的名称。__init__。py文件可以包含任何其他模块可以包含的相同Python代码,Python会在导入模块时给模块添加一些额外的属性。
从Python 3.3开始,Python引入了名称空间包的概念。名称空间包是不同文件集的组合,每个文件集向父包贡献一个子包,所有包都不需要包含__init__。py文件。文件集可以存储在文件系统的不同位置。集合的搜索包括Python在导入过程中搜索的压缩文件,网络或其他地方。名称空间包可能直接对应于文件系统的对象,也可能不直接对应于文件系统的对象。它们可以是没有特定表达式的真实模块。请参考PEP 420获取更新命名空间包的说明。
命名空间包的__path__属性和常规的包不同,使用自定义迭代器类型,遍历命令空间包的所有路径。如果其父包的路径(或高级包的sys.path)发生变化,它将在下次尝试导入时自动重新搜索该包中的包部分。
如果有以下目录结构:
酒吧套餐
nsp
吧. py
美食套餐
nsp
美食
那么nsp可以是一个名称空间包。下面是测试代码(记得用Python 3.3和更高版本运行测试):
importsys
Sys.path.extend ([foo-package , bar-package ])importnsportnsp . barimportnsp . foo prin(NSP。_ _ path _ _) # output: # _命名空间路径([foo-package/NSP , bar-
命名空间包具有以下特征:
1.现有版本的所有导入规则之后,优先级最低。
2.不需要包含__init__。包中的py文件。
3.您可以用分散的目录导入和组织代码。
4.这取决于sys.path中从左到右的搜索顺序
__导入_ _
__import__函数可以用来导入模块,import语句也会调用该函数。它被定义为:
__import__(名称[,全局变量[,局部变量[,从列表[,级别]]])
参数介绍:
Name(必选):加载的模块的名称。
Globals(可选):包含全局变量的字典。这个选项很少使用,缺省值是global()。
Locals(可选):包含局部变量的字典,这些变量不在内部标准实现中使用,默认值是-local()
Fromlist(可选):导入的子模块的名称
Level(可选):导入路径选项,在Python 2中默认为-1,表示同时支持绝对导入和相对导入。Python 3中的默认值是0,这意味着只支持绝对导入。如果大于0,则表示相对导入的父目录数,即1类似于“.”而2类似于 .。
使用示例:
#导入垃圾邮件
spam=_ _ import _ _( spam )# import spam . ham
spam=_ _ import _ _( spam.ham )#从spam . ham进口鸡蛋、香肠作为saus
_temp=__import__(spam.ham ,fromlist=[eggs ,香肠])
eggs=_temp.eggs
saus=_temp .香肠
模块缓存
执行模块导入时,Python的导入系统会先尝试从sys.modules中查找,Sys.modules是所有导入模块的缓存,包括中间路径。也就是说,如果导入foo.bar.baz,那么sys.modules将包含foo、foo.bar和foo.bar.baz模块的缓存。其实一个dict类型,每个key都有自己的值,对应对应的模块对象。
在导入过程中,首先在sys.modules中查找模块名,如果存在,将返回到该模块并结束导入过程。如果没有找到模块名,Python将继续搜索该模块(从sys.path中找到并加载它)。Sys.modules是可写的。删除一个键会使指定模块的缓存实现,下次导入会再次搜索指定模块,类似于模块重载。
注意,如果保留模块对象引用,使sys.modules中的缓存无效,然后重新导入指定的模块,那么两个模块对象将是不同的。相比之下,importlib.reload()在重新加载模块时将使用相同的模块对象,并通过重新运行模块代码来简单地重新初始化模块内容。
Imp和importlib模块
Imp模块为import语句的内部实现提供了一些接口。比如find_module,load_module等。(模块的导入过程会包括模块查找、加载、缓存等步骤)。您可以使用该模块简单地实现内置的__import__函数:
importimportsysdef _ _ import _ _(name,globals=none,locales=none,fromlist=none): #首先从缓存中查找。
try:return sys . modules[name]except key error:pass
#如果它不在模块缓存中,请从sys.path开始查找该模块
FP,pathname,description=imp . find _ module(name)#如何找到模块并加载。
try:returnimp.load_module(名称,fp,路径名,描述)最后:iffp:
fp.close()
LIB模块是在python 2.7中创建的,只包含一个函数:
importlib.import_module(名称,包=无)
这个函数是__import__的封装,用于更方便的动态导入模块。例如,用它来实现相对导入:
Importlib #类似于 from。导入b
b=importlib.import_module(。 b ,__包_ _)
从python 3开始,内置的重载函数被移到了imp模块中。但是从Python 3.4开始,imp模块被拒绝,不再推荐使用,其包含的函数被移到了importlib模块中。也就是说,从Python 3.4开始,importlib模块就是之前imp模块和importlib模块的集合。
惰性进口
前面介绍的大部分内容都是为惰性导入的实现做铺垫,其他小部分内容只是一个扩展(也就是多一点内容而已)。惯性导入意味着延迟模块的导入,模块的导入动作只有在模块实际使用时才会执行。如果不使用这些模块,导入操作将永远不会发生。
惰性进口要求仍然非常普遍。一般推荐的模块只在顶层导入,但是有时候在顶层导入模块并不是最好的选择。例如,当一个模块只在一个函数或类方法中使用时,可以使用局部导入(在局部范围内导入),这样只有在执行函数或方法时才能导入该模块,从而避免在顶级命名空间中引入模块变量。比如在我负责的项目中,需要用到熊猫包,熊猫包导入的时候会占用一些内存(不多也不少,几十兆),所以我们希望熊猫包不用的时候不要导入。有些我们自己实现的包会花很长时间加载(因为要读取配置等,导入要几秒到十几秒。),所以我们也需要懒人导入的功能。
下面是懒导入的一个简单实现,供参考:
importsysfrom类型importModuleTypeclassLazyModuleType(ModuleType):
@propertydef_mod(self):
name=super(LazyModuleType,self)。_ _ getattribute _ _( _ _ name _ _ )if name not in sys . modules:_ _ import _ _(name)return sys . modules[name]def _ _ getattribute _ _(self,name):if name== _ mod :return super(LazyModuleType,self)。_ _ getattribute _ _(name)try:return self。_mod。_ _ getattribute _ _(name)except attribute error:return super(LazyModuleType,self)。_ _ getattribute _ _(name)def _ _ setattr _ _(self,name,value):
自我。_mod。__setattr__(name,value)def lazy_import(name,package=None):if name . starts with( . ):if notpackage:raise TypeError(“相对导入需要“package”参数”)
level=0for字符inname:if字符!=.:特征级别=1
如果不是hasattr(package, rindex ):raise value error( package 未设置为字符串)
dot=len(package)for _ in range(level,1,-1):try:
Dot=package.rindex( . ,0,dot)except value error:raise value error(尝试相对导入热情的家伙顶级
包装’)
name={}。{}.format(package[:dot],name[level:])返回LazyModuleType(name)
参考数据
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。