python 常用语法及命令(九) 模块和包 分发包 搜索路径

目录

搜索路径

模块和包

构建一个模块的层级包

控制模块被全部导入的内容

使用相对路径名导入包中子模块

将模块分割成多个文件

利用命名空间导入目录分散的代码

 重新加载模块

运行目录或压缩文件

读取位于包中的数据文件

将文件夹加入到 sys.path

通过字符串名导入模块

分发包

__name__ == "__main__"


搜索路径

当导入模块的时候,编译器会自动在指定的路径中搜索,路径保存在sys.path中

import sys
sys.path
['', 'C:\\WINDOWS\\system32','D:\\install\\anaconda3\\Scripts', 
'D:\\install\\anaconda3\\python35.zip', 'D:\\install\\anaconda3\\DLLs', 
'D:\\install\\anaconda3\\lib', 'D:\\install\\anaconda3', 
'D:\\install\\anaconda3\\lib\\site-packages', 
'D:\\install\\anaconda3\\lib\\site-packages\\protobuf-3.3.0-py3.5.egg',
'D:\\install\\anaconda3\\lib\\site-packages\\win32',
'D:\\install\\anaconda3\\lib\\site-packages\\win32\\lib', 
'D:\\install\\anaconda3\\lib\\site-packages\\Pythonwin']

可以向sys.path中添加路径,用来搜索自己的模块

sys.path.append("E:\\test\\test")

也可以使用sys.path.insert(),这个方法添加的目录会优先与其他目录被import检查.程序向sys.path添加的目录只会在此程序的生命周期之内有效,其他所有的对sys.path的动态操作也是如此.

https://blog.csdn.net/qq_27923041/article/details/72878635 

 

模块和包

包就是一个文件夹,里面存放了相关模块,文件夹得名字就是包的名字

文件夹中需要创建一个__init__.py模块文件,内容可以为空

可以直接用 import导入包

构建一个模块的层级包

封装成包是很简单的。在文件系统上组织你的代码,并确保每个目录都定义了一个__init__.py 文件

graphics/
__init__.py
primitive/
    __init__.py
    line.py
    fill.py
    text.py
formats/
    __init__.py
    png.py
    jpg.py

一旦你做到了这一点,你应该能够执行各种 import 语句

import graphics.primitive.line
from graphics.primitive import line
import graphics.formats.jpg as jpg

定义模块的层次结构就像在文件系统上建立目录结构一样容易。文件 __init__.py的目的是要包含不同运行级别的包的可选的初始化代码。举个例子,如果你执行了语句import graphics,文件 graphics/__init__.py 将被导入, 建立 graphics 命名空间的内容。像 import graphics.format.jpg 这样导入,文件 graphics/__init__.py 和文件 graphics/formats/__init__.py 将在文件 graphics/formats/jpg.py 导入之前导入。
绝大部分时候让 __init__.py 空着就好。但是有些情况下可能包含代码。举个例子, __init__.py 能够用来自动加载子模块:

# graphics/formats/__init__.py
from . import jpg
from . import png

像这样一个文件, 用户可以仅仅通过 import grahpics.formats 来代替 import graphics.formats.jpg 以及 import graphics.formats.png。__init__.py 的其他常用用法包括将多个文件合并到一个逻辑命名空间。

控制模块被全部导入的内容

当使用’ from module import *’语句时,希望对从模块或包导出的符号进行精确控制。
在你的模块中定义一个变量 __all__ 来明确地列出需要导出的内容。

# somemodule.py
def spam():
    pass
def grok():
    pass
blah = 42
# Only export 'spam' and 'grok'
__all__ = ['spam', 'grok']

尽管强烈反对使用‘from module import *’ , 但是在定义了大量变量名的模块中频繁使用。如果你不做任何事, 这样的导入将会导入所有不以下划线开头的。另一方面,如果定义了 __all__ , 那么只有被列举出的东西会被导出。如果你将 __all__ 定义成一个空列表, 没有东西将被导入。如果 __all__ 包含未定义的名字, 在导入时引起 AttributeError。

使用相对路径名导入包中子模块

将代码组织成包, 想用 import 语句从另一个包名没有硬编码过的包的中导入子模块。使用包的相对导入,使一个模块导入同一个包的另一个模块举个例子,假设在你的文件系统上有 mypackage 包,组织如下:

mypackage/
    __init__.py
    A/
        __init__.py
        spam.py
        grok.py
    B/
        __init__.py
        bar.py

如果模块 mypackage.A.spam 要导入同目录下的模块 grok,它应该包括的 import语句如下:

# mypackage/A/spam.py
from . import grok

如果模块 mypackage.A.spam 要导入不同目录下的模块 B.bar,它应该使用的import 语句如下:

# mypackage/A/spam.py
from ..B import bar

两个 import 语句都没包含顶层包名,而是使用了 spam.py 的相对路径
在包内,既可以使用相对路径也可以使用绝对路径来导入。举个例子

# mypackage/A/spam.py
from mypackage.A import grok # OK
from . import grok # OK
import grok # Error (not found)

尽管使用相对导入看起来像是浏览文件系统,但是不能到定义包的目录之外。也就是说,使用点的这种模式从不是包的目录中导入将会引发错误。
最后,相对导入只适用于在合适的包中的模块。尤其是在顶层的脚本的简单模块中,它们将不起作用。如果包的部分被作为脚本直接执行,那它们将不起作用例如:

% python3 mypackage/A/spam.py # Relative imports fail

如果你使用 Python 的-m 选项来执行先前的脚本,相对导入将会正确运行。例如:

% python3 -m mypackage.A.spam # Relative imports work

将模块分割成多个文件

程序模块可以通过变成包来分割成多个独立的文件。考虑下下面简单的模块:

# mymodule.py
class A:
    def spam(self):
        print('A.spam')
class B(A):
    def bar(self):
        print('B.bar')

假设你想 mymodule.py 分为两个文件,每个定义的一个类。要做到这一点,首先用 mymodule 目录来替换文件 mymodule.py。这这个目录下,创建以下文件:

mymodule/
    __init__.py
    a.py
    b.py

在 a.py 文件中插入以下代码:

# a.py
class A:
    def spam(self):
        print('A.spam')

在 b.py 文件中插入以下代码:

# b.py
from .a import A
class B(A):
    def bar(self):
        print('B.bar')

最后,在 __init__.py 中,将 2 个文件粘合在一起:

# __init__.py
from .a import A
from .b import B

如果按照这些步骤,所产生的包 MyModule 将作为一个单一的逻辑模块:

>>> import mymodule
>>> a = mymodule.A()
>>> a.spam()
A.spam
>>> b = mymodule.B()
>>> b.

延迟导入
__init__.py 文件一次导入所有必需的组件的。但是对于一个很大的模块,可能你只想组件在需要时被加载。要做到
这一点, __init__.py 有细微的变化:

# __init__.py
def A():
    from .a import A
    return A()
def B():
    from .b import B
    return B()

在这个版本中,类 A 和类 B 被替换为在第一次访问时加载所需的类的函数。对于用户,这看起来不会有太大的不同。例如

>>> import mymodule
>>> a = mymodule.A()
>>> a.spam()
A.spam

延迟加载的主要缺点是继承和类型检查可能会中断。你可能会稍微改变你的代码,例如

if isinstance(x, mymodule.A): # Error
...
if isinstance(x, mymodule.a.A): # Ok
...

利用命名空间导入目录分散的代码

你可能有大量的代码,由不同的人来分散地维护。每个部分被组织为文件目录,如一个包。然而,你希望能用共同的包前缀将所有组件连接起来,不是将每一个部分作为独立的包来安装。
从本质上讲,你要定义一个顶级 Python 包,作为一个大集合分开维护子包的命名空间。这个问题经常出现在大的应用框架中,框架开发者希望鼓励用户发布插件或附加包。

在统一不同的目录里统一相同的命名空间,但是要删去用来将组件联合起来的__init__.py 文件。假设你有 Python 代码的两个不同的目录如下:

foo-package/
    spam/
        blah.py

bar-package/
    spam/
        grok.py

在这 2 个目录里,都有着共同的命名空间 spam。在任何一个目录里都没有__init__.py 文件。让我们看看,如果将 foo-package 和 bar-package 都加到 python 模块路径并尝试导入会发生什么

>>> import sys
>>> sys.path.extend(['foo-package', 'bar-package'])
>>> import spam.blah
>>> import spam.grok

两个不同的包目录被合并到一起,你可以导入 spam.blah 和 spam.grok,并且它们能够工作。
在这里工作的机制被称为“包命名空间”的一个特征。从本质上讲,包命名空间是一种特殊的封装设计,为合并不同的目录的代码到一个共同的命名空间。对于大的框架,这可能是有用的,因为它允许一个框架的部分被单独地安装下载。它也使人们能够轻松地为这样的框架编写第三方附加组件和其他扩展。

包命名空间的关键是确保顶级目录中没有 __init__.py 文件来作为共同的命名空间。缺失 __init__.py 文件使得在导入包的时候会发生有趣的事情:这并没有产生错误,解释器创建了一个由所有包含匹配包名的目录组成的列表。特殊的包命名空间模块被创建,只读的目录列表副本被存储在其 __path__ 变量中。举个例子:

>>> import spam
>>> spam.__path__
_NamespacePath(['foo-package/spam', 'bar-package/spam'])

在定位包的子组件时,目录 __path__ 将被用到 (例如, 当导入 spam.grok 或者spam.blah 的时候).包命名空间的一个重要特点是任何人都可以用自己的代码来扩展命名空间。举个例子,假设你自己的代码目录像这样:

my-package/
    spam/
        custom.py

如果你将你的代码目录和其他包一起添加到 sys.path,这将无缝地合并到别的spam 包目录中:

>>> import spam.custom
>>> import spam.grok
>>> import spam.blah

一个包是否被作为一个包命名空间的主要方法是检查其 __fle__ 属性。如果没有,那包是个命名空间。这也可以由其字符表现形式中的“namespace”这个词体现出来。

>>> spam.__file__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__file__'
>>> spam
<module 'spam' (namespace)>

 重新加载模块

使用 imp.reload() 来重新加载先前加载的模块。

>>> import spam
>>> import imp
>>> imp.reload(spam)
<module 'spam' from './spam.py'>

重新加载模块在开发和调试过程中常常很有用。但在生产环境中的代码使用会不安全,因为它并不总是像您期望的那样工作。
reload() 擦除了模块底层字典的内容,并通过重新执行模块的源代码来刷新它。模块对象本身的身份保持不变。因此,该操作在程序中所有已经被导入了的地方更新了模块。
尽管如此, reload() 没有更新像” from module import name”这样使用 import 语句导入的定义。举个例子:

# spam.py
def bar():
    print('bar')
def grok():
    print('grok')
>>> import spam
>>> from spam import grok
>>> spam.bar()
bar
>>> grok()
grok

不退出 Python 修改 spam.py 的源码,将 grok() 函数改成这样:

def grok():
    print('New grok')

重新加载模块

>>> import imp
>>> imp.reload(spam)
<module 'spam' from './spam.py'>
>>> spam.bar()
bar
>>> grok() # Notice old output
grok
>>> spam.grok() # Notice new output
>>> import imp
>>> imp.reload(spam)
<module 'spam' from './spam.py'>
>>> spam.bar()
bar
>>> grok() # Notice old output
grok
>>> spam.grok() # Notice new output
New grok

运行目录或压缩文件

如果你的应用程序已经有多个文件,你可以把你的应用程序放进它自己的目录并添加一个 __main__.py 文件。举个例子,你可以像这样创建目录:

myapplication/
    spam.py
    bar.py
    grok.py
    __main__.py

如果 __main__.py 存在,你可以简单地在顶级目录运行 Python 解释器:

bash % python3 myapplication

解释器将执行 __main__.py 文件作为主程序。
如果你将你的代码打包成 zip 文件,这种技术同样也适用,举个例子:

bash % ls
spam.py bar.py grok.py __main__.py
bash % zip -r myapp.zip *.py
bash % python3 myapp.zip
... output from __main__.py ...

创建一个目录或 zip 文件并添加 __main__.py 文件来将一个更大的 Python 应用打包是可行的。这和作为标准库被安装到 Python 库的代码包是有一点区别的。相反,这只是让别人执行的代码包。

由于目录和 zip 文件与正常文件有一点不同,你可能还需要增加一个 shell 脚本,使执行更加容易。例如,如果代码文件名为 myapp.zip,你可以创建这样一个顶级脚本:

#!/usr/bin/env python3 /usr/local/bin/myapp.zip

读取位于包中的数据文件

包中包含代码需要去读取的数据文件

mypackage/
    __init__.py
    somedata.dat
    spam.py

现在假设 spam.py 文件需要读取 somedata.dat 文件中的内容。你可以用以下代码来完成:

# spam.py
import pkgutil
data = pkgutil.get_data(__package__, 'somedata.dat')

由此产生的变量是包含该文件的原始内容的字节字符串
要读取数据文件,你可能会倾向于编写使用内置的 I/ O 功能的代码,如 open()。但是这种方法也有一些问题。
首先,一个包对解释器的当前工作目录几乎没有控制权。因此,编程时任何 I/O 操作都必须使用绝对文件名。由于每个模块包含有完整路径的 __fle__ 变量,这弄清楚它的路径不是不可能,但它很凌乱。
第二,包通常安装作为.zip 或.egg 文件,这些文件并不像在文件系统上的一个普通目录里那样被保存。因此,你试图用 open() 对一个包含数据文件的归档文件进行操作,它根本不会工作。

pkgutil.get_data() 函数是一个读取数据文件的高级工具,不用管包是如何安装以及安装在哪。它只是工作并将文件内容以字节字符串返回给你。get_data() 的第一个参数是包含包名的字符串。你可以直接使用包名,也可以使用特殊的变量,比如 __package__。第二个参数是包内文件的相对名称。如果有必要,可以使用标准的 Unix 命名规范到不同的目录,只有最后的目录仍然位于包中。

将文件夹加入到 sys.path

你无法导入你的 Python 代码因为它所在的目录不在 sys.path 里。你想将添加新目录到 Python 路径,但是不想硬链接到你的代码

有两种常用的方式将新目录添加到 sys.path。第一种,你可以使用 PYTHONPATH环境变量来添加。例如

bash % env PYTHONPATH=/some/dir:/other/dir python3
Python 3.3.0 (default, Oct 4 2012, 10:17:33)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/some/dir', '/other/dir', ...]

在自定义应用程序中,这样的环境变量可在程序启动时设置或通过 shell 脚本
第二种方法是创建一个.pth 文件,将目录列举出来,像这样

# myapplication.pth
/some/dir
/other/dir

这个.pth 文件需要放在某个 Python 的 site-packages 目录,通常位于/usr/local/lib/python3.3/site-packages 或者 ~/.local/lib/python3.3/sitepackages。当解释器启动时,.pth 文件里列举出来的存在于文件系统的目录将被添加到 sys.path。安装一个.pth 文件可能需要管理员权限,如果它被添加到系统级的 Python 解释器。

比起费力地找文件,你可能会倾向于写一个代码手动调节 sys.path 的值。例如:

import sys
sys.path.insert(0, '/some/dir')
sys.path.insert(0, '/other/dir')

这种方法的问题是,它将目录名硬编码到了你的源代码。如果你的代码被移到一个新的位置,这会导致维护问题。更好的做法是在不修改源代码的情况下,将 path 配置到其他地方。如果您使用模块级的变量来精心构造一个适当的绝对路径,有时你可以解决硬编码目录的问题,比如 __fle__。举个例子:

import sys
from os.path import abspath, join, dirname
sys.path.insert(0, join(abspath(dirname(__file__)), 'src'))

这将 src 目录添加到 path 里,和执行插入步骤的代码在同一个目录里。site-packages 目录是第三方包和模块安装的目录。如果你手动安装你的代码,它将被安装到 site-packages 目录。虽然用于配置 path 的.pth 文件必须放置在 site-packages里,但它配置的路径可以是系统上任何你希望的目录。因此,你可以把你的代码放在一系列不同的目录,只要那些目录包含在.pth 文件里。
 

通过字符串名导入模块

使用 importlib.import_module() 函数来手动导入名字为字符串给出的一个模块或者包的一部分。

>>> import importlib
>>> math = importlib.import_module('math')
>>> math.sin(2)
0.9092974268256817
>>> mod = importlib.import_module('urllib.request')
>>> u = mod.urlopen('http://www.python.org')

import_module 只是简单地执行和 import 相同的步骤,但是返回生成的模块对象。你只需要将其存储在一个变量,然后像正常的模块一样使用。如果你正在使用的包, import_module() 也可用于相对导入。但是,你需要给它一个额外的参数。例如:

import importlib
# Same as 'from . import b'
b = importlib.import_module('.b', __package__)

使用 import_module() 手动导入模块的问题通常出现在以某种方式编写修改或覆盖模块的代码时候。例如,也许你正在执行某种自定义导入机制,需要通过名称来加载一个模块,通过补丁加载代码。在旧的代码,有时你会看到用于导入的内建函数 __import__()。尽管它能工作,但是 importlib.import_module() 通常更容易使用。
 

分发包

如果你想分发你的代码,第一件事就是给它一个唯一的名字,并且清理它的目录结构。例如,一个典型的函数库包会类似下面这样:

projectname/
    README.txt
    Doc/
        documentation.txt
    projectname/
        __init__.py
        foo.py
        bar.py
        utils/
            __init__.py
            spam.py
            grok.py
    examples/
        helloworld.py
        ...

要让你的包可以发布出去,首先你要编写一个 setup.py ,类似下面这样:

# setup.py
from distutils.core import setup
setup(name='projectname',
    version='1.0',
    author='Your Name',
    author_email='you@youraddress.com',
    url='http://www.you.com/projectname',
    packages=['projectname', 'projectname.utils'],
)

下一步,就是创建一个 MANIFEST.in 文件,列出所有在你的包中需要包含进来的非源码文件:

# MANIFEST.in
include *.txt
recursive-include examples *
recursive-include Doc *

确保 setup.py 和 MANIFEST.in 文件放在你的包的最顶级目录中。一旦你已经做了这些,你就可以像下面这样执行命令来创建一个源码分发包了:

% bash python3 setup.py sdist

它会创建一个文件比如” projectname-1.0.zip”或“projectname-1.0.tar.gz” , 具体依赖于你的系统平台。如果一切正常,这个文件就可以发送给别人使用或者上传至Python Package Index.
对于纯 Python 代码,编写一个普通的 setup.py 文件通常很简单。一个可能的问题是你必须手动列出所有构成包源码的子目录。一个常见错误就是仅仅只列出一个包的最顶级目录,忘记了包含包的子组件。这也是为什么在 setup.py 中对于包的说明包含了列表 packages=['projectname', 'projectname.utils']大部分 Python 程序员都知道,有很多第三方包管理器供选择,包括setuptools、
distribute 等等。有些是为了替代标准库中的 distutils。注意如果你依赖这些包,用户可能不能安装你的软件,除非他们已经事先安装过所需要的包管理器。正因如此,你更应该时刻记住越简单越好的道理。最好让你的代码使用标准的 Python 3 安装。如果其他包也需要的话,可以通过一个可选项来支持。
 

__name__ == "__main__"

1、if  __name__ == "__main__"

让程序知道那一部分作为测试主程序,哪一部分作为模块导入

导入模块之后,执行import语句,模块里的代码会按照顺序全部执行一遍,有些代码部分在想在本模块中执行,这就需要 

if  __name__ == "__main__": ,告诉程序下面的代码只在当前模块执行。

另外,当执行一个模块是,当前模块的__name__ 就是“__main__”,而导入的模块,它的__name__是包名,所以可以使用

if  __name__ == "__main__" 这种方式选择只在本模块执行的代码。

#moduleA.py

print('module A……')

print('A中__name__的值:{}'.format(__name__))

print('只在A中运行部分')

 

#moduleB.py

from moduleA import *

print('module B……')

print('A中__name__的值:{}'.format(__name__))

#执行结果是
module A……
A中__name__的值:moduleA
只在A中运行部分
module B……
B中__name__的值:__main__

当执行模块B时结果模块A全部被执行。

现在在模块A中加上 if  __name__ == "__main__",

#moduleA.py

print('module A……')

print('A中__name__的值:{}'.format(__name__))

if  __name__ == "__main__":

    print('只在A中运行部分')

再执行模块B,结果是

module A……
A中__name__的值:moduleA
module B……
B中__name__的值:__main__

print("只在A中执行")这一句没有执行,另外可以看到两个模块的__name__值是什么。

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值