什么是模块

模块(module) 是一个包含 Python 代码的文件,通常以 .py 结尾。模块可以包含变量、函数、类,甚至其他模块。通过模块,我们可以将代码组织成不同的文件,更好地管理和复用代码。

模块可以分为以下几类:

  1. 标准库模块:Python 自带的模块,如 mathossys 等。
  2. 第三方模块:由其他开发者编写的模块,通常可以通过 pip 安装。
  3. 自定义模块:用户自己编写的模块。

自定义模块

自定义模块就是一个包含 Python 代码的 .py 文件。你可以将函数、类、变量写入该文件中,然后在其他 Python 文件或脚本中通过 import 语句导入并使用它们。

  1. 创建一个 Python 文件:把你想要封装的代码写入一个 .py 文件中。

  2. 导入自定义模块:在另一个 Python 文件中,使用 import 语句导入这个模块。

创建和使用自定义模块

先创建一个py文件,名为my_module.py

# my_module.py

print("This is my moudle....")

def greet(name):
    return f"Hello, {name}!"

def add(a, b):
    return a + b

PI = 3.14159

块不仅仅是一个包含函数和类的文件,它还可以包含可执行的语句。这些可执行的语句通常用于模块的初始化,并且只会在模块首次导入时执行。为防止模块被重复导入,Python 采取了一些优化手段。

例如,当我们import一个模块的时候,python回会执行依次该模块中的所有顶层语句(不包括看书内部或者类内部定义的代码),这些语句通常用于初始化模块

拿上述模块举例,如果我们第一次import这个语句的时候,其中的print语句,定义greet和add两个函数的语句,以及定义PI常量的语句将会执行。

我们在同目录下面新建一个demo.py文件,来测试

import my_module

# Output:
This is my moudle....

导入模块时,Python 会为该模块创建一个独立的命名空间,模块内的所有变量、函数和类都属于该命名空间,从而避免与当前代码中的名称发生冲突。

调用模块中的常量/变量

import my_module
import my_module
import my_module

print(my_module.PI)

# Output:
This is my moudle....
3.14159

多次执行import语句,只会初始化一次

调用模块中定义的函数

我们可以直接调用模块中的函数来使用该函数提供的功能

import my_module

# 使用 my_module 中的函数和变量
print(my_module.greet("Alice"))
print(my_module.add(5, 3))
print(my_module.PI)

# Output:
This is my moudle....
Hello, Alice!
8
3.14159

总结:首次导入模块my_module时会做三件事

  1. 为源文件(my_module模块)创建新的名称空间

  2. 在新创建的命名空间中执行模块中包含的代码

  3. 创建名字my_module来引用该命名空间

模块别名

在import导入模块的时候,如果某个模块名字较长,我们可以给该模块给一个别名

# demo.py
import my_module as mm

print(mm.greet("牛老师yyds"))
print(mm.add(5, 3))
print(mm.PI)

# Output:
This is my moudle....
Hello, 牛老师yyds!
8
3.14159

当然对于别名的话,也有其他的用法,我们可以看如下案例:

有两中sql模块mysql和oracle,根据用户的输入,选择不同的sql功能

# mysql.py
def sqlparse():
    print('from mysql sqlparse')

# oracle.py
def sqlparse():
    print('from oracle sqlparse')

# test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db

db.sqlparse()

同时导入多个模块

import  sys, os, re

# 或者
import sys
import os
import re

通过from导入模块

对比import my_module,会将源文件的名称空间'my_module'带到当前名称空间中,使用时必须是my_module.名字的方式

而from 语句相当于import,也会创建新的名称空间,但是将my_module中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了

# demo.py

from my_module import greet,add

print(greet('牛老师'))
print(add(1,2))

# Output:
This is my moudle....
Hello, 牛老师!
3

如果代码中存在与模块中同名的函数会怎么样呢?

# demo.py

from my_module import greet,add

def add(x,y):
    return x*y

print(greet('牛老师'))
print(add(3,2))

# Output:
This is my moudle....
Hello, 牛老师!
6

根据上述案例表明,如果重新定义同名函数,那么模块里面定义的函数会被覆盖

如果想要一次性导入某个模块里面所有的方法,我们可以通过from my_module import *这样的方式

from mymodule import * 把my_module中所有的不是以下划线()开头的名字都导入到当前位置 大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差。

那么,如果想要设定from my_module import *导入哪些模块,我们可以在模块中加上如下代码:

.....
__all__ = ['greet','add']
# 这样在另外一个文件中用from my_module import *就这能导入列表中规定的两个名字
注意:如果my_module.py中的名字前加`_`,即`_`money,则from my_module import *,则`_`money不能被导入
  • 编写好的一个python文件可以有两种用途:

  • 脚本,一个文件就是整个程序,用来被执行

  • 模块,文件中存放着一堆功能,用来被导入使用
  • python为我们内置了全局变量__name__

  • 当文件被当做脚本执行时:__name__ 等于'__main__'

  • 当文件被当做模块导入时:__name__等于模块名

  • 作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码)

  • if __name__ == '__main__':

def fib(n):
    a, b = 0, 1
    while b < n:
        print(b, end=',')
        a, b = b, a+b
    print()

if __name__ == "__main__":
    print(__name__)
    num = input('num :')
    fib(int(num))    

print(globals())

模块的搜索路径

模块的查找顺序是:内存中已经加载的模块->自建模块->sys.path路径中包含的模块

  1. 在第一次导入某个模块时(比如my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用 ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看
  2. 如果没有,解释器则会查找同名的内建模块
  3. 如果还没有找到就从sys.path给出的目录列表中依次寻找my_module.py文件。
注意:自定义的模块名不应该与系统内置模块重名

编译python文件

为了提高加载模块的速度,python解释器会在__pycache__目录中下缓存每个模块编译后的版本,格式为:module.version.pyc。通常会包含python的版本号。例如,在CPython3.3版本下,mymodule.py模块会被缓存成`_pycache/my_module.cpython-33.pyc`。这种命名规范保证了编译后的结果多版本共存。

包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来

需要强调的是:

  1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

  2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块

为何要使用包

包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来 随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性

注意事项

  1. 关于包相关的导入语句也分为importfrom ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

  2. import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件

  3. 包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间

包的使用

示例文件

glance/                   #Top-level package
├── __init__.py      #Initialize the glance package
├── api                  #Subpackage for api
│   ├── __init__.py
│   ├── policy.py
│   └── versions.py
├── cmd                #Subpackage for cmd
│   ├── __init__.py
│   └── manage.py
└── db                  #Subpackage for db
    ├── __init__.py
    └── models.py

文件内容

#文件内容

#policy.py
def get():
    print('from policy.py')

#versions.py
def create_resource(conf):
    print('from version.py: ',conf)

#manage.py
def main():
    print('from manage.py')

#models.py
def register_models(engine):
    print('from models.py: ',engine)

使用import导入包

import glance.db.models

glance.db.models.register_models('mysql')

单独导入包名称时不会导入包中所有包含的所有子模块

import glance
# 在导入glance的时候会执行glance下的__init__.py中的代码
glance.cmd.manage.main()

解决方法

# glance/__init__.py
from . import cmd

# glance/cmd/__init__.py
from . import manage

使用from (具体的路径) import (具体的模块)

需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法

from glance.db import models
from glance.db.models import register_models

models.register_models('mysql')
register_models('mysql')

from glance.api import * 想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all__

x = 10

def func():
    print('from api.__init.py')

__all__=['x','func','policy']
from glance.api import *

func()
print(x)
policy.get()

绝对导入和相对导入

  • 绝对导入:以glance作为起始
  • 相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)

绝对导入: 以执行文件的sys.path为起始点开始导入,称之为绝对导入

  1. 优点: 执行文件与被导入的模块中都可以使用
  2. 缺点: 所有导入都是以sys.path为起始点,导入麻烦

相对导入: 参照当前所在文件的文件夹为起始开始查找,称之为相对导入

  1. 符号: .代表当前所在文件的文件加,..代表上一级文件夹,...代表上一级的上一级文件夹
  2. 优点: 导入更加简单
  3. 缺点: 只能在导入包中的模块时才能使用
学前沿IT,到英格科技!本文发布时间: 2024-10-19 09:01:33

results matching ""

    No results matching ""