【Python】模块和包

主要内容:

理解模块和包的基本概念
理解模块的导入过程
熟悉sys模块和os模块中的一些重要操作


Python 模块初识

当代码量比较大的时候, 我们最好把代码拆分成一些有组织的代码片段.
每个代码片段里面包含一组逻辑上有关联的函数或者类.
每一个片段里放在一个独立的文件中. 这样的片段就称为模块(module)
使用 import 可以在一个Python文件中引入其他的模块.

对比C语言的 include

回忆我们C语言中, 如果一个文件想使用其他文件的功能, 通过 #include 的方式来引入一个头文件.
C语言的 #include 本质上是文本替换. 因此可能会导致一系列的问题**(重复包含, 包含顺序相关).**

所以通常加上:

#pragma once 	//防止头文件重复包含

模块也是对象

Python的模块则避免了C语言中的这些问题. import 实际上是创建了一个模块对象. 通过这个对象来访问
模块中的具体方法.

import os
print(type(os))	#<class 'module'>  模块
print(id(os))	#2191830213400  有id,所以也是一个对象

模块的搜索路径

我们尝试 import 一个模块名时, Python解释器必须要知道模块文件的路径

例如, 我们此处有一个add.py, 那么对应的模块名就是add (去掉后缀)


sys 是python标准库自带的模块,一些和python解释器相关的模块

Python从sys.path中查找模块.

我们可以看到, sys.path其实是一个列表. 里面里面的内容是Python的一些默认搜索路径. 如果我们想再加
入一些新的路径, 直接修改sys.path即可. ->sys.path.append('....')

import sysq	
print(sys.path)
#执行结果:
['E:\\Python文件存储', 'E:\\pythonProject1', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages']

import sys
for line in sys.path:
    print(line)
#执行结果:
E:\Python文件存储
E:\pythonProject1
C:\Users\Mango\AppData\Local\Programs\Python\Python37\python37.zip
C:\Users\Mango\AppData\Local\Programs\Python\Python37\DLLs
C:\Users\Mango\AppData\Local\Programs\Python\Python37\lib
C:\Users\Mango\AppData\Local\Programs\Python\Python37
C:\Users\Mango\AppData\Local\Programs\Python\Python37\lib\site-packages

import语句不一定非要写在源代码文件的最开始(推荐写在最上面)

如果在多个目录中, 都存在相同名字的模块, 这个时候Python解释器会从前往后顺序查找, 把找到的第一个
匹配的结果进行加载.


理解 “命名空间”

名字冲突问题: 如果一个项目中的代码量很大, 那么很可能函数/类的名字会冲突.

C语言采用的方案是,给函数名加一个又丑又长的前缀

void Huichuan_AdServer_Strategy_IndexAccessor_Load()

C++中使用命名空间来解决这个问题

namespace Huichuan {
    namespace AdServer {
        namespace Strategy {
            namespace IndexAccessor {
                void Load()
            }
        }
    }
}

Python的模块也相当于是命名空间. 例如 os.path.exists , 通过这样的层级结构, 将函数的名字组织起来, 就可以有效的避免命名冲突.


import语句

import语句导入模块, 可以一次import一个, 或者一次import多个->用,分割

#方式1:
import os
import sys

#方式2:
import os,sys #用逗号分割

推荐使用第一种方式, 这样代码看起来更好看一些


import语句的顺序: 我们推荐按 Python标准库, Python第三方库, 应用程序自定制模块 的顺序来import,
来提高我们代码的可读性.

模块也是一个对象, 也有作用域这样的概念. 在函数内部import, 那么这个模块的作用域就是在这个函
数内.

例子:

def func():
    import sys
    print('In func',sys.argv)	#In func ['E:/Python文件存储/Class01.py']
func()
print('Out func',sys.argv)	#报错 NameError: name 'sys' is not defined

既然模块也是一个对象, 那么也可以给这个对象赋值(相当于定义别名).

import os.path
p = os.path #起别名
print(p.exists('add.py'))   #add.py这个文件是否存在  True

这样可以一定程度上简化我们的代码(敲一个字符p肯定比敲一串字符os.path要方便).


import as语句

前面我们使用赋值的方式, 给模块起了一个很短的别名. 实际上使用import-as可以更方便的完成这个动作

import os.path as p
print(p.exists('add.py'))   #add.py这个文件是否存在  True

from-import 语句

import语句是直接导入一个模块. 而有时候我们只需要用到模块中的某一个或几个函数, 就可以使用from-
import

from-import相当于把模块中的名字引入了当前文件的命名空间中.可以直接使用导入的函数

from os.path import exists
print(exists('add.py')) #True

可以使用 from module import * 的方式将module模块中的所有名字都导入进来. 不推荐这么用.

*:通配符


导入模块意味着 “被执行”

模块导入意味着这个模块 “被执行” , 也就是说所有顶层的代码(无缩进部分的代码)都会被执行到. 这通常包括函数的定义和全局变量的定义.

例子:

add.py 的内容:

'''
定义两数相加的函数
'''
def _Add(x,y):
    return x+y

print("In Add函数内部")

test.py的内容

import add
#执行结果:
In Add函数内部

这往往不是我们期望的结果(比如导入模块是打印了一些奇怪的日志), 我们只是想使用模块中的一些函数和变量. 因此往往我们在实现一个模块时, 只将函数定义/类定义放到顶层代码中.


理解 “导入” 和 “加载”

我们写 import module 时, 其实有两个大的阶段, 先将这个模块的文件加载到内存中, 并创建一个对象来表示; 然后通过一个变量名来将这个模块引入到当前的命名空间中.

如果同一个模块(例如sys这样的常用模块)被import了多次, 其实加载动作只进行了一次(也就是说内存中只有一份sys的代码), 执行动作也只进行了一次

import add
import add
#执行结果:
In Add函数内部

模块的内置变量

我们可以通过globals()函数看到全局命名空间下有几个内置变量

print(globals())
#执行结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000026C824D6400>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/Python文件存储/Class01.py', '__cached__': None}

其中:

_builtins_ : 内建函数模块. 包含了open, help, doc等内建函数的定义
__name__ : **模块名字. 如果是从这个模块开始执行, 那么 __name__ 值为 __main__,否则就是当前文件不包含后缀的名称

入口文件是谁,__name__就是__name__

__file__ : 模块的文件名
__doc__ : 模块的文档字符串
__package__ :模块从属的包名


回忆我们刚刚讲过的, import的时候, 会执行这个模块. 我们期望, 这个模块被别人导入时, 只进行函数定义; 如果直接从这个模块开始运行, 则在函数定义完成后再进行一些测试

例子:

'''
定义两数相加的函数
'''
def Add(x,y):
    return x+y

#测试:
if __name__ == '__main__':
    print(Add(1,2))
    print(Add(2,3))

判定 __name__ 是否值为 _main_ , 如果是的话, 说明是直接执行add.py, 这时候就执行测试代码. 否则
认为add是被其他Python文件import的, 不执行测试代码.

包(Package)

当我们代码量进一步变大时, 光拆分成多个文件已经不足以满足需求, 还需要能按照一定的目录结构层次化的组织这些模块. 同时包也可以解决模块之间的名字冲突问题.

目录也是包的一种 ,包也是模块

如:以下面的方式组织代码结构:

test.py
calc/
	add.py
    __init__.py

在calc目录中增加一个 _init_.py 文件, calc这个目录就成了包(Package). 可以在test.py文件中import calc中的模块了

**_init.py_**是包的初始化文件, 会首先执行它


__init__.py 是在包加载的时候会进行执行, 负责一些包的初始化操作. 一般是空文件即可.


包中是可以嵌套其他的包的

评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芒果再努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值