ThankNeko's Blog ThankNeko's Blog
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)

Hoshinozora

尽人事,听天命。
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)
  • Python笔记

    • 基础知识

      • 常见规范与运行方式
      • 变量与垃圾回收机制
      • 输入与格式化输出
      • 运算符
      • 流程控制语句
      • 浅拷贝和深拷贝
      • 常用数据类型与分类
      • 数据类型方法
      • 字符编码
      • 文件操作
      • 函数与参数
      • 命名空间与作用域
      • 闭包函数与装饰器
      • 迭代器与生成器
      • 三元表达式与生成式
      • 函数递归
      • 面向过程式和函数式编程
      • 模块与包
        • 模块介绍
        • 模块导入
        • 测试模块功能
        • 循环导入问题
        • 模块编写规范
        • 包(模块)
        • 数据模型
      • 程序设计目录参考
      • 常用内置模块或函数
      • 序列化和猴子补丁
      • 日志模块-logging
    • 类与面向对象

    • 并发编程

    • Web编程

    • 模块笔记

    • 其他

  • C笔记

  • C++笔记

  • Arduino笔记

  • Dev
  • Python笔记
  • 基础知识
Hoshinozora
2023-02-15
目录

模块与包

# 模块介绍

# 介绍

模块是一系列功能(例如类、函数、配置等)的集合,他一共分为三类:

  • 内置模块 - python解释器内置的模块。
  • 第三方模块 - 别人写好的模块。
  • 自定义模块 - 自己编写的模块,可以使用(python/c/c++)语言编写。

事实上一个python文件就是一个模块,例如文件名func.py,则模块名就叫func。

# 模块的四种形式

使用Python编写的.py文件。

已被编译为共享库或DLL的C或C++扩展。

把一系列模块文件放到一个的文件夹中,文件夹下有一个__init__.py文件,该文件夹称之为包。

使用C编写并链接到python解释器的内置模块。

# 模块的作用

  • 内置与第三方模块
    • 拿来就用,无需定义,极大地提高效率
  • 自定义模块
    • 将各种功能函数代码等放到模块中为大家共享使用,减少代码冗余,程序组织结构更加清晰

# 模块导入

# 模块的导入方法

  • 导入模块
    • import [模块名]
      • 一行导入一个模块
      • 调用时需使用模块名如: fooooo.time()
    • import [模块名],[模块名],[模块名]... ...
      • 一行导入多个模块(不推荐,可读性不高)
    • import [模块名] as [模块别名]
      • 导入模块时,给模块添加别名
      • 调用时可直接使用别名如: foo.time()
  • 导入模块的功能
    • 会在当前名称空间拿到导入的名字,该名字与模块名称空间中名字一样,指向同一个内存地址
      • 只是将名字复制一份到当前py文件的名称空间
      • 其所指向的内存地址仍然是不变的
    • from [模块名] import [模块中功能]
      • 单独导入模块内的功能
      • 调入时,可直接使用功能名如: time()
    • from [模块名] import *
      • 导入模块中的所有名字
      • 但一般不会用,因为不知道导入了什么名字,所以极容易与当前名称空间的名字混淆
      • __all__
        • 可以用__all__去控制*的名字代表哪些
          • 默认是全都有
        • 在模块文件内 __all__ = [名字1,名字2...]
  • 导入模块方式的优缺点
    • 直接导入模块
      • 优点
        • 不会与当前py文件名称空间的名字起冲突
      • 缺点
        • 需要加前缀,调用不方便
    • 只导入模块中功能
      • 优点
        • 不需要加前缀,调用方便
      • 缺点
        • 可能与当前py文件名称空间的名字起冲突

# 模块导入的规范

约定俗成的导入顺序

  • 先导入python内置模块
  • 再导入第三方模块
  • 最后导入自定义模块

模块名的命名应该采用纯小写+下划线的风格。

# 模块导入的过程

  • 首次import导入模块后,会执行模块文件中的代码
  • 将模块代码运行过程中产生的名字丢到模块文件的名称空间中
  • 在当前文件中产生一个名称 (以导入的模块名为名),该名称指向模块文件的名称空间
  • 之后的导入会直接引用首次导入产生的名称空间,不会再执行模块文件中的代码

# 函数内导入模块

  • 函数内导入的模块只对函数内有效,但再次调用该也仍然是直接引用首次导入产生的名称空间,不会再执行模块文件中的代码

# 模块导入查找顺序

  • 先查找内存
    • 如果内存有则直接用内存的
    • 只要程序没结束,即便del解绑名称,内存空间也不会被释放,这是python为了防止再次导入而造成性能消耗做的优化
      • 只有程序运行结束才会释放
    • 可用过 print(sys.modules) 查看已经加载到内存的模块
  • 再查找硬盘
    • 按照 sys.path 变量下的路径按顺序进行查找
      • print(sys.path)
    • 第一是在当前路径下找

# 模块的引用

  • [模块名].[模块中名称]
    • 如:
      • func.times - 变量
      • func.time() - 函数
  • 无论是查看还是修改变量,操作的都是模块的名称空间中的名称
  • 也就是调用模块中的函数时,函数如果调用了变量,调用的也是模块名称空间中的变量名称

# 模块名称空间的回收

  • 当模块的内存空间没有被人引用时,就会被回收,也就是引用计数为0时

# 测试模块功能

# name

  • 每一个Python文件都内置的变量
  • 直接运行时值为:__main__
  • 当被作为模块导入时值为:[模块名]
  • 可以在测试模块功能时使用 if 判断,以防在被作为模块导入时,测试代码被执行

# 例如:

if __name__ == '__main__':
    print('test')
1
2

# 循环导入问题

# 原因

  • 在一个模块加载/导入的过程中导入另外一个模块,而在另外一个模块中又返回来导入第一个模块中的名字
  • 由于第一个模块尚未加载完毕,所以引用失败、抛出异常
  • 同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会去重复执行内部代码

# 解决方式

  • 不要出现模块之间的互相调用
  • 或者如果只是一个函数需要用到,可以只在函数内导入,而非全局中导入

# 模块编写规范

# 模块编写规范顺序

  • 模块的描述说明
  • 导入引用的模块
  • 全局变量 (尽量不要用)
  • 类 (要有描述说明)
  • 函数 (要有描述说明)
  • 测试

# 例如

#!/usr/bin/env python #通常只在类unix环境有效,作用是可以使用脚本名来执行,而无需直接调用解释器。
"The module is used to..." #模块的文档描述
#导入模块
import sys
#定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能
x=1
#定义类,并写好类的注释
class Foo:
    'Class Foo is used to...'
    pass
#定义函数,并写好函数的注释
def test():
    'Function test is used to…'
    pass
#主程序
if __name__ == '__main__':
    #在被当做脚本执行时,执行此处的代码
    test()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 包(模块)

# 介绍

  • 包就是一个包含有__init__.py文件的文件夹
  • 反之有__init__.py文件的文件夹,就是一个包
  • 包本质上就是模块的一种形式,用来被当做模块导入
    • 多个模块放在包中
    • 然后用__init__.py文件导入

# 包定义方式

  • 模块放在文件夹中
    • 同时要有__init__.py文件
  • 在__init__.py文件中导入其他模块
  • 导入模块方式
    • 包名 即是 文件夹名
    • __init__.py 文件中导入模块
      • 绝对导入
        • import 直接导入模块
          • 在包下找到模块并导入
          • import 包名.模块名
        • from...import... 导入模块中的功能
          • 在包下找到模块并导入其中的功能
          • from 包名.模块名 import 功能名
          • 例如: from func.test import get
      • 相对导入
        • 包内模块之间的导入,推荐用相对导入
        • 用 .和..
        • 如:
          • /aa/bb
          • 在bb的init中调用aa中的模块
            • from ..get import get
    • 因为sys.path是以运行文件为准,所以想要让它找到,就必须指定什么包名下的什么模块
  • 导入子包
    • from [包] import [子包]
    • import [包].[子包]
      • 调用也要 [包].[子包]
      • 同时 [包] 的函数也可以调用
        • 因为在导入子包时包的__init__.py会被执行
    • 即便只导入子包,找到子包所路过的包的__init__都会被执行一遍,因为要检索其中是否有
      • 如: pack.a.b.c.d
      • 如果没有就会去子文件夹找

# 包功能调用方式

  • __init__.py 文件以 import 方法导入
    • 包名.模块名.功能名()
    • 如
      • __init__中
        • import foo.get
      • 运行文件中
        • import foo
        • foo.mod.get()
  • __init__.py 文件以 import...as... 方法导入
    • __init__.py 导入时添加别名
    • 调用时可直接:包名.别名.功能名()
  • __init__.py 文件以 from...import... 方法导入
    • 包名.功能名()
    • 因为在包中,以from...import...直接导入了功能名到__init__.py文件的名称空间
    • 如
      • __init__中
        • from foo.get import get
      • 运行文件中
        • import foo
        • foo.get()

# 包的导入过程

  • 找到导入的文件夹名的下的__init__.py文件
  • 运行该文件,将运行过程中产生的名字都丢到产生的名称空间去
  • 在当前导入模块的名称空间中,拿到导入的模块的名字,该名字指向模块运行产生的名称空间。

# 数据模型

# 数据模型介绍

Python最好的品质之一是一致性,一致性可以保证我们在面对不同的对象时,使用相同的方法实现某些特定的事情,比如说len()可以用来取不同对象的长度等。

Python风格(Pythonic)的设计思想完全体现在Python的数据模型上,数据模型是对Python框架的描述,它规范了这门语言自身构建模块的接口。

数据模型所描述的API,可以让你使用最地道的语言特性来构建你自己的对象提供了工具,比如:__getitem__、__len__等。

# 特殊方法介绍

特殊方法又称魔术方法、双下方法。特殊方法的调用是隐式的,解释器碰到特殊的句法时,会使用特殊方法去调用对象操作。

比如dict["key"]会调用字典对象中的dict.__getitem__(key)方法。

比如len()方法执行时,如果对象是一个是自定义类的对象时,则会调用该对象的__len__方法,如果对象是一个内置类型会直接返回PyVarObject.ob_size属性。

比如for i in x:会调用iter(x),而这个函数的背后则是x.__iter__()。

注意:特殊方法的存在是为了被解释器调用,所以无需直接使用特殊方法,除非有大量的元编程存在。

但__init__方法是个特例,一般在子类中会使用__init__方法来调用超类的构造器。

# 常用特殊方法

__init__ 在对象创建时,会被执行。

__getitem__ 在使用obj[key]时会被执行。

__len__ 在使用len()函数时会被执行。

__repr__ 在obj被作为字符串时会被执行,如:str()、print()。

和__str__差不多,但当__str__不存在但又要用到str()时,解释器会用__repr__来代替,所以一般用__repr__即可。

__repr__所返回的字符串应该准确、无歧义,并且尽可能表达出这个被打印的对象。

__add__ 在使用obj做加法运算时会被执行,如:obj1 + obj2。

__mul__ 在使用obj做乘法运算时会被执行,如:obj1 * obj2。

__bool__ 在使用obj做布尔运算时会被执行。如:bool()、if obj:等。

# 使用案例

# 创建一套卡牌

import random
import collections

Card = collections.namedtuple("Card", ["rank", "suit"])

class PokeCards:
    def __init__(self):
        ranks = [str(n) for n in range(2, 11)] + list("JQKA")
        suits = "红桃 黑桃 梅花 方块".split()
        self._cards = [Card(rank, suit) for suit in suits for rank in ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, index):
        return self._cards[index]

cards = PokeCards()
print(len(cards))
print(Card("2","红桃") in cards)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#模块#包
面向过程式和函数式编程
程序设计目录参考

← 面向过程式和函数式编程 程序设计目录参考→

最近更新
01
二〇二五年四月十七日随笔
04-17
02
二〇二五年四月十六日随笔
04-16
03
二〇二五年四月九日随笔
04-09
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Hoshinozora | MIT License
湘ICP备2022022820号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式