python模块导入及属性:import

http://blog.csdn.net/pipisorry/article/details/43313197

模块和包

1. python程序由包(package)、模块(module)和函数组成。

2. 是由一系列模块组成的集合。当不同作的模块进行按文件夹分类后再组成一个整体的库,可以称为包。为了让Python将目录当做内容包,目录中必须包含__init__.py文件,用于标识当前文件夹是一个包。最简单的情况下,只需要一个空的__init__.py文件即可。包就是一个完成特定任务的工具箱,包的作用是实现程序的重用。包导入会让模块扮演的角色更为明显,也使代码更具有可读性。

3. 模块是处理某一类问题的函数和类的集合,由代码、函数和类组成。函数是一段可以重复多次调用的代码。模块把一组相关的函数或代码组织到一个文件中,一个文件即是一个模块。每个模块文件是一个独立完备的命名空间,一个模块文件不能看到其他文件定义的变量名,除非它明确地导入了那个文件,模块文件起到了最小化命名冲突的作用。

皮皮blog

 

 

python中引入包

两种导入语句

导入模块使用import和from语句(都是隐性的赋值语句),以及reload函数。可以导入模块名,还可以指定目录路径(Python代码的目录就称为包),包导入就是把计算机上的目录变成另一个Python命名空间,包的属性就是该目录包含的子目录和模块文件。当多个同名程序文件安装在某机器上时,包导入可以偶尔用来解决导入的不确定性。导入包也使用import和from语句。

import语句

import file_name可以导入当前目录上file_name.py这个模块,但是里面的内容,必须通过file_name.func/file_name.var 来访问。

如果你一直在某个环境,比如解释器下面,你已经导入过某个模块 ,现在你对模块进行了修改,这里你需要用reload(modulename)来重新载入。

Python里,多次import的效果是只有第一次import有用,如果想要重新载入模块应该用reload(your_module)。

局部import时还可以使用这种语法

__import__('shutil').rmtree(DATA_DIR)

subpackage导入时要这样:

__import__('geopy.distance').distance.vincenty(i, j).miles

相当于import geopy.distance; distance.vincenty(i, j).miles?

 from import语句

另外一种导入方式为import func/var from file_name。这样func/var直接可用,但是file_name是没有定义的。

从file_name中导入所有:import * from file_name。这样会导入所有除了以下划线开头的命名。实际代码中这样做往往是不被鼓励的。

导入模块的两种方式的不同之处

1. 与import类似, 被导入的module仍然会执行且仅执行一次

2. from *** import 的实质

当以 "from *** import " 方式导入module时, python会在当前module 的命名空间中新建相应的命名.

即, "from t2 import var1" 相当于:

import t2

var1= t2.var1

在此过程中有一个隐含的赋值的过程

由于python赋值操作特性(参考python变量名本质),在当前代码中对"var1"的修改并不能影响到t2.py中"var1"的值。同时,在t2.py中对var1的修改也不能反映到当前的代码。如果实在要改,可以这样:import t2; t2.var1 = something

包的两种导入模块或模块内函数的/变量的方式可能出现的错误

from package import item  # 这种方式,item可以是包中的一个子模块或子包,也可以是包中定义的其他命名,像函数、类、变量。
import item.subitem.subsubitem # 这些子项必须是包,最后的子项是包或模块。但不能为函数、类或变量。否则出错:No module named ***

Note

1. 陷阱:使用from导入变量,而那些变量碰巧和作用域中现有的变量同名,本地变量就会被悄悄地覆盖掉;使用import则没这个问题。

2.当b.py文件以from filename import fun方式从a.py文件引入函数fun时,如果有可执行语句在函数fun外,则引入后,执行b.py也会同时执行a.py在函数外的可执行语句,故一般不要把可执行脚本直接写到函数外,实在不想写入函数中也可以使用if __name__ == '__main__':...的方式。

皮皮blog

 

 

模块的搜索路径

首先会搜索解析器的当前目录。然后会到sys.path变量中给出的目录列表中查找。

>>> import sys
>>> sys.path
['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
  1. 输入脚 本的目录(当前目录)
  2. 环境变量PYTHONPATH表示的目录列表中搜索
  3. Ptyon的默认安装路径中搜索。

Note:sys.path包含输入模块的目录名列表。我们可以观察到sys.path的第一个字符串是空的——这个空的字符串表示当前目录也是sys.path的一部分,这与PYTHONPATH环境变量是相同的。这意味着你可以直接输入位于当前目录的模块。否则,你得把你的模块放在sys.path所列的目录之一。[python系统模块sys、os和路径、系统命令]

皮皮blog

 

 

从 * 导入包和__init__.py 中的__all__ 列表

当用户写下 from sound.Effects import * 时会发生什么事?理想中,总是希望在文件系统中找出包中所有的子模块,然后导入它们。这可能会花掉很有长时间,并且出现期待之外的边界效应,导出了希望只能显式导入的包。

对于包的作者来说唯一的解决方案就是给提供一个明确的包索引。

import 语句按如下条件进行转换:

执行 from package import *时,如果包中的__init__.py 代码定义了一个名为__all__ 的列表,就会按照列表中给出的模块名进行导入。新版本的包发布时作者可以任意更新这个列表。如果包作者不想 import * 的时候导入他们的包中所有模块,那么也可能会决定不支持它( import * )。例如,sounds/effects/__init__.py 这个文件可能包括如下代码:

__all__ = ["echo", "surround", "reverse"]

这意味着 from Sound.Effects import * 语句会从 sound 包中导入以上三个已命名的子模块。

如果没有定义 __all__from Sound.Effects import * 语句 不会 从 sound.effects 包中导入所有的子模块。无论包中定义多少命名,只能确定的是导入了 sound.effects 包(可能会运行__init__.py 中的初始化代码)以及包中定义的所有命名会随之导入。这样就从__init__.py 中导入了每一个命名(以及明确导入的子模块)。同样也包括了前述的 import 语句从包中明确导入的子模块。

考虑以下代码:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

在这个例子中,echo 和 surround 模块导入了当前的命名空间,这是因为执行 from...import 语句时它们已经定义在 sound.effects 包中了(定义了__all__时也会同样工作)。

尽管某些模块设计为使用 import * 时它只导出符全某种模式的命名,仍然不建议在生产代码中使用这种写法。

记住,from Package import specific_submodule 没有错误!事实上,除非导入的模块需要使用其它包中的同名子模块,否则这是推荐的写法。

皮皮blog

 

 

包的绝对导入和相对导入

 

python 2.7及之前版本

中默认是先“相对”后“绝对”的顺序搜索模块,也就是说先在模块所在同一个包内搜索然后在sys.path中搜索。为此,所有的导入现在都被认为是绝对的, 也就是说这些名字必须通过 Python 路径(sys.path 或是 PYTHONPATH )来访问。python 2.x在包内模块的导入,顺序是:
1、当前目录
2、上一个目录
3、找不到再往上
4、系统环境变量
5、标准库
6、前三个在搜索路径下的 .pth文件

Python 2.7之后版本

包的绝对导入

很多情况下导入子包会导致和真正的标准库模块发生(事实上是它们的名字)冲突。 包模块会把名字相同的标准库模块隐藏掉, 因为它首先在包内执行相对导入, 隐藏掉标准库模块。

从 Python 2.7 开始默认绝对导入。 python3.x 中的包内的模块导入原则编程了:如果要相对导入,那么一定是显式声明,并且,这个声明不是告诉python一个模块查找偏好,而是一种约束(也就是说,一旦声明,必须是相对导入)

from __future__ import absolute_import: 在 3.0 以前的旧版本中启用相对导入等特性所必须的 future 语句。

 

Python3绝对导入:按照sys.path顺序搜索,先主目录(sys.path中第一项''),然后PYTHONPATH环境变量、标准库路径、pth指定路径等。

包的相对导入

import 语句总是绝对导入的, 所以相对导入只应用于 from-import 语句

Python3相对导入:在模块所在同一个包内搜索,注意该包目录与主目录的区别。Python3中,每一个点号代替上一层目录。这样做的目的,主要是防止覆盖命名空间。

Note:从当前目录相对导入可以省略.号,不过不省略更好,以免之后修改系统不会自动调整(使用pycharm时,移动文件时,有.号它会自动修改导入路径)。

Example

Phone/

        __init__.py
       common_util.py           #其中包含setup
        Fax/
              __init__.py
              G3.py                       #其中包含dial
         Mobile/
               __init__.py
               Analog.py               #其中包含dial
               Digital.py

在 Mobile.Analog.Digital , 也就是 Digital.py 模块中, 不能简单地使用错误语法(只能工作在旧版本的 Python 下, 在新的版本中它会导致一个警告, 或者干脆不能工作)
import Analog
from Analog import dial

正确绝对导入方法:

from Phone.Mobile.Analog import dial

正确相对导入方法:
from .Analog import dial            # . 代表当前.py文件所在目录Mobile/
from ..common_util import setup
from ..Fax import G3.dial.

[Python核心编程2ed]

[Python 相对导入]

相对导入要注意的问题和常见错误

lz总结的一个模块导入法则:Use relative import only in modules and run the scripts outside the package. 也就是说使用相对导入的文件不应该是要运行执行的文件,而应该只是外层调用的模块。但是如果你非要在packges里面做测试的话,那就不要使用相对导入。Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.[documentation]

终极解决方案

import sys
import os
#将mymodule模块(目录)所在的目录(如这个py文件的../..)加入到pythonpath中就可以使用from mymodule import *了
sys.path.append(os.path.join(os.path.split(os.path.realpath(__file__))[0],"../..")) 

try:
 from .mymodule import myclass
except Exception: #ImportError
 from mymodule import myclass

 

示例

Note: 最顶层的SocialNetworks是一个单纯的目录,而下层的SocialNetworks开始就是一个python package.

运行MainTest.py,EBM中导入的就是try中的import

  

直接运行EBM.py,是从except中import的

也就是使用

try:
    from .mymodule import myclass
except Exception: #ImportError
    from mymodule import myclass

来区分包内和包外的运行测试。

[SystemError: Parent module '' not loaded, cannot perform relative import]

后来lz发现pycharm中不用加入路径就可以导入except中的语句,from SocialNetworks.SocialNetworks引入成功的原因是lz在pycharm中添加了add content roots to pythonpath了,这样sys.path中多了一个整个项目project的路径/media/pika/files/mine/python_workspace,里面的目录就被当成包使用,所以可以引入成功!在代码中加入路径后,在pycharm之外也可以正常运行from SocialNetworks.SocialNetworks了,所以lz建议关闭pycharm的add roots to pythonpath这种功能。

[Why does PyCharm always add “contents root” to my PYTHONPATH, and what PYTHONPATH?]

下面这几个错误好像都可以使用上述的方法解决,下面是以前总结的解决方案,不怎么好。

python拓展包的调用

>>> import sys
>>> sys.path
['', '/home/pipi/ENV/ubuntu_env/lib/python35.zip', '/home/pipi/ENV/ubuntu_env/lib/python3.5', '/home/pipi/ENV/ubuntu_env/lib/python3.5/plat-x86_64-linux-gnu', '/home/pipi/ENV/ubuntu_env/lib/python3.5/lib-dynload', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/home/pipi/ENV/ubuntu_env/lib/python3.5/site-packages']
可以看到/home/pipi/ENV/ubuntu_env/lib/python3.5/site-packages在sys.path中,所以可以在python中调用这些不同的拓展包(如使用pip安装的包)。

1.报错:ValueError: Attempted relative import beyond toplevel packages

案例1:

目录树
case1/
├── cat
│   ├── __init__.py
│   ├── cat.py
├── dog
│   ├── __init__.py
│   └── dog.py
├── __init__.py
└── main.py
 
代码
# case1/cat/cat.py
from .. import dog

# case1/main.py
import cat.cat

执行
python case1/main.py

错误原因:
这里的 case1 是一个包,但当你直接执行 main.py 的时候,就没有把完整的 case1 当作一个包来处理了( the package should be entirely self contained. It won't treat case1/ as a package when you're running main.py inside it.),而cat和dog还是包,并且已经是包树的顶层了。可想而知,下层的 cat.py (import cat.cat)自然找不到上层包了,想要相对导入成功,必须让下层的被导入对象是上层包或上层包内的对象。

所以使用相对导入的目录的平级目录内的所有py文件不允许单独执行(当然局部测试时是可以的,而不是将包中的py文件当作主应用程序执行),要保证这个模块不是入口文件(py文件中有if __name__ == '__main__',其实也不一定有这个,只要你执行就算是),只是作为被导入的模块才可以以这样使用。

python 中只能在package中使用相对导入,不能在用户的应用程序中使用相对导入,因为不论是相对导入还是绝对导入,都是相当于当前模块来说的,对于用户的主应用程序,也就是入口文件,模块名总是为“ __main__  ”, 所以用户的应用程序(如NLP目录下有一个main.py[包含__main__],要执行的)必须使用绝对导入,而package(如__main__调用的包NLP\TargetOpinion)中的导入可以使用相对导入。

 

When launching a python source file, it is forbidden to import another file, that is in the current package, using relative import.

解决:将主程序移出到package外,如整个python目录NLP\TargetOpinion\***,TargetOpinion中的packages可以互相相对导入,但是TargetOpinion目录下不能有main模块,要移到外面,如NLP目录下,与TargetOpinion平齐。

当然这个案例就是将man.py放到case1平齐的目录中,如case1_1。

报错2:SystemError: Parent module '' not loaded, cannot perform relative import?

这种情况一般是因为case1不是包(目录下没有__init__),而 cat.cat.py要调用dog.dog.py(from ..dog import dog),这样上层..不是包不能导入出错。

不过在pycharm中设置source root(或者加入../..到sys.path中)好像也可以解决。

[pycharm快捷键、常用设置、配置管理 ]

案例2:

目录树
case3/
├── alpaca.py
├── main.py
└── pets
    ├── __init__.py
    ├── cat
    │   ├── __init__.py
    │   └── cat.py
    └── dog
        ├── __init__.py
        └── dog.py

代码
# case3/pets/cat/cat.py
from ..dog import dog
from .. import dog
# case3/main.py
from pets.cat import cat

执行
python case3/main.py

请注意,这里的 cat.py 中是不能导入 alpaca 的,因为 pets 已经是这个包树的顶层了(case3中包含main.py其中包含main函数,这样case3就不会被当作包?)。

2.ImportError: No module named '***'

sys.path没有加入导入的模块所在目录

3.忌两个py文件循环导入

4. 使用相对导入非本级目录的py也禁止单独运行

否则报错: ValueError: Attempted relative import in non-package

 

[python的relation import]

[python package module import 完全总结]

[python 下 import 模块使用相对路径]

在软件包内部只进行相对导入

在子模块中你时常见到的一个简单错误,就是使用软件包的名字来导入软件包。

# within a sub-module

from a_package import APackageError

这样做会导致两个不好的结果:

  1. 子模块只有当软件包被安装在 PYTHONPATH 内才能正确运行。
  2. 子模块只有当这个软件包的名字是 a_package 时才能正确运行。

尽管第一条看上去并不是什么大问题,但是考虑一下,如果你在 PYTHONPATH 下的两个目录中,有两个同名的软件包。你的子模块可能最终导入了另一个软件包,你将无意间使得某个或某些对此毫无戒备的程序员(或是你自己)debug 到深夜。

所以说,在软件包中最好只进行相对导入,而运行的执行文件(__main__)都放在软件包外面。

AttributeError: 'module' object has no attribute '***'

如import scipy或者import dateutil后

使用scipy.misc或者dateutil.parser出错

原因:because the parser.py is a module in the dateutil package. It's a separate file in the folder structure.所以要手动添加。其实主要是ubuntu_env/lib/python3.5/site-packages/dateutil$ vi __init__.py中没有from . import subpackages,自己修改一下就好了,不过它本身没这么加肯定有它的原因,lz暂时还没搞清楚。

解决:所以最好还是使用import dateutil.parser或者from dateutil import parser来导入模块。

[Scipy教程 - python数值计算库 ]

皮皮blog

 

 

 

 

 

 

让模块保持较小的规模

你的模块应当比较小。记住,那个使用你软件包的程序员会在软件包作用域进行导入,同时你会使用你的 __init__.py 文件来作为一个组织工具,来暴露一个完整的接口。

好的做法是一个模块只定义一个类,伴随一些帮助方法和工厂方法来协助建立这个模块。

 

class APackageClass(object):

    '''One class'''

 

def apackage_builder(how_many):

    foriinrange(how_many):

        yieldAPackageClass()

 

如果你的模块暴露了一些方法,把一些相互依赖的方法分为一组放进一个模块,并且把不相互依赖的方法移动到单独的模块中,一个例子是 fsq/enqueue.py,它暴露了一系列的方法来为同一个功能提供不同的接口(就像 simplejson 中的l oad/loads)。尽管这个例子足够直观,让你的模块保持较小规模需要一些判断,但是一个好的原则是:当你有疑问的时候,就去创建一个新的子模块吧。

 

字节编译文件.pyc

 

输入一个模块相对来说是一个比较费时的事情,所以Python做了一些技巧,以便使输入模块更加快一些。

一种方法是创建 字节编译的文件 ,这些文件以.pyc作为扩展名。字节编译的文件与Python变换程序的中间状态有关。{一个用编译性语言比如C或C++写的程序可以从源文件(即C或C++语言)转换到一个你的计算机使用的语言(二进制代码,即0和1)。这个过程通过编译器和不同的标记、选项完成。当你运行你的程序的时候,连接/转载器软件把你的程序从硬盘复制到内存中并且运行。而Python语言写的程序不需要编译成二进制代码。你可以直接从源代码运行 程序。在计算机内部,Python解释器把源代码转换成称为字节码的中间形式,然后再把它翻译成计算机使用的机器语言并运行。}

当你在下次从别的程序输入这个模块的时候,.pyc文件是十分有用的——它会快得多,因为一部分输入模块所需的处理已经完成了。另外,这些字节编译的文件也是与平台无关的。

 

皮皮blog

 

 

模块内的所有定义

 

dir()函数

 

 

当你为dir()提供一个模块名的时候,它返回模块定义的名称列表(函数、类和变量)。如果不提供参数,它返回当前模块中定义的名称列表(当前解释器中定义的命名)。

 

>>> import fibo
>>> dir(fibo)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'fibo']

 

dir()并不会列出内置函数和变量名。如果你想列出这些内容,它们在标准模块__builtin__中定义。

curModuleDir=dir()  # get dir of current file(module)同时这句话放在模块(.py)文件函数或类外面

修改dir函数表现

class Shape(object):

        def__dir__(self):

            return['area','perimeter','location']

则返回指定的三个函数字符串

 

模块的__name__属性

 

当一个模块被第一次输入import的时候,这个模块的主块将被运行。假如我们只想在程序本身被使用的时候运行主块,而在它被别的模块输入的时候不运行主块,这可以通过模块的__name__属性完成。

 

image

每个Python模块都有它的__name__,如果它是'__main__',这说明这个模块被用户单独运行,我们可以进行相应的恰当操作。

[Python内置函数--dir]

 

皮皮blog

 

from:http://blog.csdn.net/pipisorry/article/details/43313197

ref:简明python教程-模块和包

深入Python模块的使用

构建健壮 Python 包的 5 个简单规则

深扒Python是如何找到模块的?

 

 

展开阅读全文
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值