Python中module与package概念与

本文主要探讨模块和包两个概念,了解这两个概念,有助于我们更好地使用python进行模块化编程,通过模块化编程,我们能把大的工程拆分成小的子任务和子模块,在比较大的项目中,进行模块化编程的好处有以下几点:

  1. 简化编程,不必把重点放在整个项目上了;
  2. 可维护性好,即使出了问题也便于排查;
  3. 复用性好,直接使用编写好的模块去实现功能,当需要重复实现时,再次调用即可,不必再重新编写了;
  4. 范围性好,这个的意思就是每个模块都有单独的命名空间,避免发生一些例如变量命名上的冲突

在编程中多多使用函数,模块和包能够提升模块化编程水平。

基础概念

什么是命名?

每个变量都拥有一个名字,这个就是命名,给变量命名。变量命名也是让很多程序员头疼的一件事情,怎么样能起一些有意义,又高大上的名字。在Python中,一切皆对象,我们甚至可以给一个函数一个命名,命名就可以理解为所有对象的一个引用的名称。

什么是命名空间?

名空间就是用来保证命名之间不发生冲突的规则,分为:

  • 局部命名空间:在一个程序中为每个函数创建的命名空间,当函数执行完毕就失效
  • 全局命名空间:在程序中为所有模块创建的命名空间,当程序执行完毕失效
  • 内置命名空间:也就是默认的一些命名,退出程序失效

Python模块介绍

如何创建一个模块?

在python中,有三种方式去创建模块:

  1. 自己写一个python文件;
  2. 用C语言实现,然后在运行时动态加载,比如常用的正则表达式模块re;
  3. 内置模块,我们直接引用就可以了;
  4. 我们主要关注第一种方式,也就是说,python给我们提供了十分简单的方法去创建一个模块,我们只需要写一个python文件即可,也就是说写一个.py为后缀的文件,不必用额外的语法。

    举个例子,我们现在写了一个文件,命名为mod.py,如下:

    mod.py

  5. s = "If Comrade Napoleon says it, it must be right."
    a = [100, 200, 300]
    
    def foo(arg):
        print(f'arg = {arg}')
    
    class Foo:
        pass

    可以看到,我们在这个python文件里面创建了一下几个对象:

  6. s,一个字符串
  7. a,一个列表
  8. foo,一个函数
  9. Foo,一个类
  10. 假定这个文件路径正确,我们就可以把mod.py作为一个模块,在别的文件中引用,举例:

    >>> import mod
    >>> print(mod.s)
    If Comrade Napoleon says it, it must be right.
    >>> mod.a
    [100, 200, 300]
    >>> mod.foo(['quux', 'corge', 'grault'])
    arg = ['quux', 'corge', 'grault']
    >>> x = mod.Foo()
    >>> x
    <mod.Foo object at 0x03C181F0>

模块搜索路径

继续上面的例子,当执行这句

import mod

的时候,解释器都干了什么?

解释器主要是在如下几个路径中搜索mod.py文件:

  1. 首先在当前执行的文件所在的文件夹中查看是否存在,如果当前文件夹不包含mod.py文件,那么这个搜索就失败了;
  2. 在系统设置的python环境变量下的一些文件夹里面;
  3. 一些在python安装时指定的文件夹下面;

具体我们可以使用:

>>> import sys
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages']

来查看在路径搜索时,都有哪些路径,当然搜索的结果因为安装,版本和操作系统的原因都不会相同,每个人的结果可能都不会相同。

因此,当我们import一些模块出现问题的时候,我们可以查看这个模块文件是不是在搜索路径中。

当我们想要把自己写的文件作为一个模块的时候,但是又不在搜索路径中,我们可以自己添加进去,比如:

>>> sys.path.append(r'C:\Users\john')
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'C:\\Users\\john']
>>> import mod

因为sys.path返回的是路径的列表,我们直接使用append()方法把想要引用的模块路径添加进去即可。

让Python文件既可以被当作模块引用,也能被当作脚本执行

s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

if (__name__ == '__main__'):
    print('Executing as standalone script')
    print(s)
    print(a)
    foo('quux')
    x = Foo()
    print(x)

加上了if (__name__ == '__main__'):,这样python解释器就知道这个文件时单独在执行,还是被作为模块引用。在单独执行时,有输出,而在作为模块引用时,没有输出。

模块推荐导入顺序

在文件中需要导入内置模块、第三方模块或自定义模块时,建议的导入顺序是:标准库模块--第三方模块--自定义模块

什么是Python的包(package)?

简单来说,包就是多个模块的集合。当项目较大,模块较多时,我们就可以把模块放在包中,便于管理。

我们在包中经常能见到__init__.py文件,如下图:

 

 __init__.py文件到底是什么?

对于一个python项目,里面的每一个文件夹都可以认为是一个package,而每一个.py文件被认为是一个module。如果你用的IDE是PyCharm,那么当你新建一个Python Package的时候,PyCharm都会自动为你新建一个__init__.py文件。这个__init__.py文件可以看作这个package的初始化文件,具体用途且看下文。

__init__.py文件在做什么?

文件目录如下

"""
.
├── demo.py
├── package
|   ├── __init__.py
|   ├── module.py
"""

demo.py文件如下

# demo.py

import package

__init__.py文件如下

# __init__.py

print("__init.py file is called!")

那么当我执行demo.py文件时,输出结果如下

$ python demo.py 
__init.py file is called!

这说明__init__.py中的代码被执行了。如果把demo.py中的 import package 换成import package.module也是一样的结果。这说明,当我们从一个package里面调用东西的时候,该__init__.py文件内的代码会被首先执行。

__init__.py文件有什么用?

简化import语法

假设在module.py文件中有一个函数a_function()如下

# module.py

def a_function():
    print("Test function is called!")

如果我现在想从demo.py中调用它,没有__init__.py文件的话,只能这么写(方法一)

# demo.py

from package.module import a_function

a_function()

但是我可以在__init__.py中进行如下定义

# __init__.py

from package.module import a_function

这样定义完了以后在demo.py就可以如下调用了(方法二)

# demo.py

from package import a_function

a_function()

 

好像也没简洁到哪儿去?试想,如果你在paSckage里面有几十个module,那么当你想调用这几十个module里面的几十上百个函数的时候,你就需要在demo.py文件中写几十行import语句,这样无疑是不简洁的。而采用方法二的办法,你就可以把这些import语句统统放进__init__.py文件。但这不是__init__.py最重要的用途,最重要的是下面两点。

批量导入和规范化导入以及__all__

如果你在module.py中定义了很多函数,你想在demo.py中调用,怎么办呢?如module.py中有两个函数

# module.py

def a_function_1():
    print("Test function 1 is called!")

def a_function_2():
    print("Test function 2 is called!")

先在__init__.py批量导入

# __init__.py

from package.module import *

再在demo.py中批量导入

# demo.py

from package import *

a_function_1()

这样就可以实现package中诸多函数的批量导入了。注意批量导入遵从覆盖原则,即如果有多个类和方法名字相同,那么后导入的会覆盖先导入的。

但是如果你只允许a_function_1被外界调用呢,就可以把__init__.py改成

# __init__.py

from package.module import a_function_1

这样在demo.py文件中,调用a_function_2就会报错。

# demo.py

from package import *

a_function_2()    # 这里就会报错

更优雅一点可以调用__all__属性,在__init__.py中定义可以被外界调用的类和方法,如

# __init__.py

__all__ = ['a_function_1']     # 这样,在demo.py只能调用a_function_1方法

from package.module import *

 

 

这里举的例子都是函数/方法,对于类来说,是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值