Python-模块和包

from…import…😀



模块和包



一. 模块与包的意义


1.1 什么是模块?

在Python中,一个.py文件就称之为一个模块(Module)。


1.2 为什么要使用模块?

  • 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。
  • 随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。
  • 我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。
  • 使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。


1.3 什么是包?

  1. 如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录(文件夹)来组织模块的方法,称为包(Package)。
  1. 包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)
  1. 每一个包目录下面都会有一个 __init__.py 的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。它可以是空文件,也可以有Python代码,因为 __init__.py 本身就是一个模块。
  1. import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的 __init__.py导入包本质就是在导入该文件
  1. 文件夹里面还可以包含文件夹(可以有多级目录,组成多级层次的包结构)。但是每一个文件夹下面必须有一个 __init__.py 文件。
  1. 自己创建模块时要注意命名,不能和Python自带的模块名称冲突。例如,系统自带了sys模块,自己的模块就不可命名为sys.py,否则将无法导入系统自带的sys模块。


举例说明:

有个包按照如下目录存放文件:

mycompany
├─ __init__.py
├─ abc.py
└─ xyz.py

引入了包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。现在,abc.py 模块的名字就变成了 mycompany.abc ,类似的,xyz.py 的模块名变成了 mycompany.xyz


类似的,可以有多级目录,组成多级层次的包结构。比如如下的目录结构:

mycompany
├─ web
│ ├─ __init__.py
│ ├─ utils.py
│ └─ www.py
├─ __init__.py
├─ abc.py
└─ xyz.py

文件www.py 的模块名就是mycompany.web.www,两个文件utils.py的模块名分别是mycompany.utilsmycompany.web.utils


总结
模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。
创建自己的模块时,要注意:
1. 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
2. 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。



二. 使用模块 -- 无包组织


使用模块的几种语句:
1. import 语句: import module1[, module2[,... moduleN]
2. from … import 语句: from modname import name1[, name2[, ... nameN]]
3. from … import * 语句: from modname import *
4. __name__属性: 用来分清该模块是调用者还是被其他模块调用。
5. dir() 函数: 用来查找模块中定义的名字,返回一个有序字符串列表。


2.1 import 语句

想使用 Python 源文件,只需在另一个源文件里执行 import 语句,语法如下:
import module1[, module2[,... moduleN]
  1. 使用 Python 源文件,只需在另一个源文件里执行 import 语句。
  2. 一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。
  3. 调用者引用模块后可以:模块名.函数名(参数)来使用。


举例说明:
当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。如想要导入模块 support,需要把命令放在脚本的顶端:

support.py 文件代码

def print_func( par ):
    print ("Hello : ", par)
    return

------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
test.py 文件代码
# 导入模块
import support

# 现在可以调用模块里包含的函数了
support.print_func("Runoob")
------------------------------------------------------------------------------------------
运行test.py实例输出结果:
Hello :  Runoob


2.2 from … import 语句

Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,这种访问函数时可以直接使用函数名而不需要前缀,语法如下:
from modname import name1[, name2[, ... nameN]]


举例说明:
例如,要导入模块 fibo 的 fib 函数,使用如下语句:

fibo.py
# 斐波那契(fibonacci)数列模块

def fib(n):    # 定义到 n 的斐波那契数列
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a+b
    print()   //输出为None

def fib2(n): # 返回到 n 的斐波那契数列
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
test.py
from fibo import fib, fib2
print(fib(500))
print(fib2(500))

运行结果:
1 1 2 3 5 8 13 21 34 55 89 144 233 377
None
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]


2.3 from … import * 语句

  • 这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
  • 这种方法,可以一次性的把模块中的所有(函数,变量)名称都导入到当前模块的字符表。
  • 这将把所有的名字都导入进来,但是那些由单一下划线(_)开头的名字不在此例。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。


把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

from modname import *



三. 使用模块 -- 有包组织

  • 目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。
  • 最简单的情况,放一个空的 __init__.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。


1. import 语句:

用户可以每次只导入一个包里面的特定模块,他必须使用全名去访问:

import 包名.子包名.模块名                 导入模块

包名.子包名.模块名 .函数名(参数)           使用模块


2. import …from 语句:

同样会导入子模块,他不需要那些冗长的前缀,只需要模块名.函数名(参数)即可,推荐使用!

from 包名.子包名 import 模块名           导入模块

模块名.函数名(参数)                      使用模块


3. import …from 语句:

还有一种变化就是直接导入一个函数或者变量:

from 包名.子包名.模块名  import 函数名      导入模块
函数名(参数)                               使用模块



4. import …from * 语句:
注意:这种导入方法必须要注意__init__.py里面的属性__all__已经设置好。__all__是用于控制from…import *
Python 会进入文件系统,找到这个包里面所有的子模块,一个一个的把它们都导入进来。但是很不幸,这个方法在 Windows平台上工作的就不是非常好,因为Windows是一个大小写不区分的系统。 在这类平台上,没有人敢担保一个叫做 ECHO.py 的文件导入为模块 echo 还是 Echo 甚至 ECHO。
from 包名.子包名 import *                 导入所有模块
模块名.函数名(参数)                        使用模块

具体使用方法请看下面章节四。


总结使用时注意事项:

  1. 注意当使用from package import item这种形式的时候,对应的item既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量
  2. import语法会首先把item当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,恭喜,一个:exc:ImportError 异常被抛出了。
  3. 反之,如果使用形如import item.subitem.subsubitem这种导入形式,最后一项可以是包或者模块(不可以是类,函数或者变量的名字),除了最后一项,都必须是包。



四. __init__、__all__、__name__、__author__、__doc__、dir()函数


4.1 __init__、__all__

在使用from 包名.子包名 import \* 语句时必须修改__init__.py文件的__all__ 列表变量。
  1. 如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import *的时候就把这个列表中的所有名字作为包内容导入。
  2. 作为包的作者,可别忘了在更新包之后保证 __all__ 也更新了啊。你说我就不这么做,我就不使用导入*这种用法,好吧,没问题。这里有一个例子
  3. 这表示当你使用from sound.effects import *这种用法时,你只会导入如下包里面这三个子模块。即这种语法导入的是__all__指定的模块。
  4. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
  5. import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件。



上述的例子:

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


4.1 __name__、__main__

  1. __name__是一个变量。前后加了爽下划线是因为是因为这是系统定义的名字。普通变量不要使用此方式命名变量。
  2. __name__就是标识模块的名字的一个系统变量。这里分两种情况:

    1. 假如当前模块是主模块(也就是调用其他模块的模块),那么此模块名字就是main,即__name__==__main__。可以执行后面的内容;
    2. 假如此模块是被import的,其值为被调用模块所属的路径。


下面举一个例子:

test1.py

if __name__ == '__main__':
   print('程序自身在运行')
else:
   print('我来自另一模块')

------------------------------------------------------------------------------------------
test2.py
import test1
------------------------------------------------------------------------------------------

运行test2.py输出结果:
我来自另一模块

运行test1.py输出结果:
程序自身在运行


4.3 __author__、__doc__

文档注释:任何模块代码的第一个字符串都被视为模块的文档注释; __doc__可以访问一个文件中的第一个单引号(1、2、3对)、双引号(1、2、3对)注释的文档。 __author__可以将一个文件的作者名字赋给它。


应用示例如下:

# -*- coding: utf-8 -*-

' a test module ' 
'fgbhjknml'

__author__ = 'Directoree'

a = 10

print(a)
print(__doc__)
print(__doc__)
print(__author__)

运行结果:
10
 a test module
 a test module
Directoree


4.4 dir()函数

内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回
无参数: 只能列举当前模块已经被定义的名字;
有参数: 参数只能是已经引用的模块名字,不能为自身名字,否则会报错。


举例子说明:

test3.py
# -*- coding: utf-8 -*-

' a test module ' 
'fgbhjknml'
__author__ = 'Directoree'
a = 10

print(__doc__)
print(__author__)
print(dir())

运行结果:
 a test module
Directoree
['__annotations__', '__author__', '__builtins__', '__cached__', '__doc__', 
'__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'bbb']

------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
test3.py
# -*- coding: utf-8 -*-
import test3  
print(dir(test3))

运行结果:
['__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', 
'__name__', '__package__', '__spec__', 'a', 'bbb']
['__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', 
'__name__', '__package__', '__spec__', 'a', 'bbb']



五. 非公开函数

外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public

类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;

之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

private函数或变量不应该被别人引用,那它们有什么用呢?请看例子:

def _private_1(name):
    return 'Hello, %s' % name

def _private_2(name):
    return 'Hi, %s' % name

def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)


我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。



  Reprint please specify: Treecatee Python-模块和包

 Previous
Python-函数式编程 Python-函数式编程
装饰器还没搞懂,搞懂了再来继续写吧~函数既可作为返回值也可作为函数的参数…一切都是对象,一切都是指针,一切都是东西…返回函数(引用)和返回函数值是不一样滴…. ◡̈⃝︎⋆︎* 函数式编程 函数式编程的一个特点就是,允许把函
2019-03-04
Next 
Python-生成器与迭代器 Python-生成器与迭代器
生成器生成想要的数据,可控制循环暂停,迭代器把可迭代的对象转换为生成器。( ⸝⸝⸝•_•⸝⸝⸝ )♡︎♡︎ 生成器与迭代器 一. 迭代、列表生成式1.1 迭代迭代:如果给定一个list或tuple,我们可以通过for循环来遍历
2019-03-03
  TOC