1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com
  1. Python 系列(一) 从零开始认识 Python,入门其实没那么难
  2. Python 系列(二) 学 Python 的第一步,把开发环境搭建好
  3. Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
  4. Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
  5. Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
  6. Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
  7. Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
  8. Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
  9. Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
  10. Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
  11. Python 系列(十一) list、tuple、set、dict,到底该怎么选?
  12. Python 系列(十二) List 列表实战,搞定数据增删改查与批量处理
  13. Python 系列(十三) Tuple 元组实战,搞懂不可变特性与 enumerate() 遍历
  14. Python 系列(十四) Set 集合实战,搞定数据去重与集合运算
  15. Python 系列(十五) Dict 字典详解,掌握快速查找与键值操作
  16. Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
  17. Python 系列(十七) 自定义函数入门,让重复代码一次编写多次使用
  18. Python 系列(十八) 函数参数详解,搞懂位置参数、关键字参数和默认参数
  19. Python 系列(十九) 从 *args 到 **kwargs,让函数调用更灵活
  20. Python 系列(二十) 变量作用域是什么?一文搞懂 global 和 nonlocal
  21. Python 系列(二十一) 搞懂变量、对象和引用,理解函数参数传递机制
  22. Python 系列(二十二) map() 和 filter() 详解,轻松实现批量数据处理与筛选
  23. Python 系列(二十三) 嵌套函数与 lambda 表达式详解,搞懂作用域与闭包
  24. Python 系列(二十四) 搞懂递归函数,从基线条件到实际应用
  25. Python 系列(二十五) 聊聊模块与 import,让代码告别重复编写
  26. Python 系列(二十六) Python 模块入门,从 import 到模块复用
  27. Python 系列(二十七) Python dir() 函数详解,快速查看模块中的函数与属性
  28. Python 系列(二十八) 不会用 Python 标准库?教你快速查找模块和函数
  29. Python 系列(二十九) 搞懂 Python sys 模块,让脚本更适合自动化运维
  30. Python 系列(三十) 为什么 0.1 + 0.2 不等于 0.3?彻底搞懂 Decimal
  31. Python 系列(三十一) 搞懂 datetime 日期时间,轻松处理时区与时间计算

上一篇我们学习了 Python 模块,了解了如何使用 import 导入模块、如何通过 from...import 导入指定对象,以及如何把一个 Python 脚本拆分成多个模块。

但问题来了:导入一个模块之后,怎么知道它里面到底有哪些函数、变量和属性?

比如,你导入了 Python 标准库中的 math 模块,知道它可以进行数学计算,但除了 sqrt(),还有哪些函数可以使用?

再比如,在 Linux 运维自动化脚本中,你想了解 os、sys 或 shutil 模块提供了哪些功能,难道每次都要去查文档吗?

其实,Python 已经提供了一个非常方便的内置函数:dir()。

今天,我们就从最简单的例子开始,逐步掌握 dir() 的用法,并弄清楚它与 globals()、locals() 以及模块中的 __all__ 到底有什么区别。

认识 dir() 函数

在学习 dir() 之前,我们先理解一个问题:Python 是怎么知道一个模块中有哪些名称的?

还记得上一篇学习的 import 吗?

当我们执行下面的代码时:

1
import math

Python 会导入 math 模块,并让我们通过 math 这个名称访问模块提供的功能。

例如:

1
2
3
4
import math

print(math.sqrt(16))
print(math.pi)

运行结果:

1
2
4.0
3.141592653589793

这里的 sqrt 是一个函数,pi 是一个表示圆周率的常量。

但是,math 模块中还有很多其他名称。如果不查阅文档,我们怎样快速了解它们呢?

这时候就可以使用 dir()。

dir() 是 Python 的内置函数,不需要额外导入任何模块。它可以返回一个对象相关名称组成的列表,帮助我们探索对象提供了哪些属性。

基本语法如下:

1
dir([object])

其中:

  • object:可选参数,表示要查看的对象。
  • 传入对象时,dir() 通常返回该对象相关的名称列表。
  • 不传参数时,dir() 返回当前局部作用域中的名称,具体结果与调用位置有关。

这里的“名称”不只是函数,还可能包括变量、类、方法以及其他属性。

需要注意,dir() 的主要目的是方便我们探索对象,它返回的名称不一定是该对象所有可能属性的完整清单。对于自定义对象,结果还可能受到对象自身 __dir__() 方法的影响。

使用 dir() 查看模块

我们先从最容易理解的 math 模块开始。

创建一个 Python 文件:

1
2
3
mkdir -p ~/python-dir-demo
cd ~/python-dir-demo
vim dir_math.py

在文件中输入以下代码:

1
2
3
import math

print(dir(math))

保存后运行:

1
python dir_math.py

运行后,你会看到一长串名称,例如:

1
2
3
4
['__doc__', '__loader__', '__name__', '__package__',
'__spec__', 'acos', 'asin', 'atan', 'ceil', 'cos',
'degrees', 'e', 'exp', 'floor', 'log', 'pi', 'radians',
'sin', 'sqrt', 'tan', ...]

上面只是示意,实际输出会更长,具体名称可能随 Python 版本和运行环境有所不同。

先不用急着记住这些名称,我们重点关注几个内容:

  • sqrt:计算平方根。
  • sin、cos:三角函数。
  • floor:向下取整。
  • ceil:向上取整。
  • pi:圆周率。
  • e:自然常数。
  • __name__、__doc__:模块自身的特殊属性。

通过 dir(math),我们就能快速了解 math 模块提供了哪些名称,而不需要一开始就翻阅完整的官方文档。

不过,这里有一个细节需要注意:dir() 返回的名称不一定都是可以直接调用的函数。

例如:

1
2
3
4
import math

print(math.pi)
print(math.sqrt(25))

输出:

1
2
3.141592653589793
5.0

math.pi 是一个数值,不能像函数一样写成 math.pi()。

因此,看到 dir() 的输出后,我们还需要进一步确认每个名称的用途。

dir() 不只是查看模块

前面的示例都传入了模块对象,但 dir() 并不局限于模块。

它还可以用于查看字符串、列表等对象提供的名称。

例如:

1
2
3
text = "Linux"

print(dir(text))

你会看到类似下面这些名称:

1
2
3
4
5
6
7
['__add__', '__class__', '__contains__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', 'capitalize', 'casefold', 'center',
'count', 'endswith', 'find', 'format', 'index',
'isalnum', 'isalpha', 'isdigit', 'islower', 'join',
'lower', 'replace', 'split', 'startswith', 'strip',
'upper', ...]

具体列表可能因 Python 版本而有所不同。

这些名称中,有不少是我们熟悉的字符串操作方法:

  • upper():将字符串转换为大写。
  • lower():将字符串转换为小写。
  • split():拆分字符串。
  • replace():替换字符串中的内容。
  • strip():去掉字符串两端的空白字符。

例如,在 Linux 运维中,我们经常需要处理命令输出、日志内容或者服务名称。

1
2
3
4
service = "  nginx  "

print(service.strip())
print(service.strip().upper())

输出:

1
2
nginx
NGINX

这里的 strip() 和 upper() 都是字符串对象提供的方法。

我们可以先通过 dir(text) 查看有哪些可用名称,再根据实际需求选择相应的方法。

当然,dir() 本身并不会告诉我们每个方法的具体参数和使用规则。它更像是一份快速索引,帮助我们知道“有哪些东西可以进一步了解”。

不传参数的 dir() 到底查看什么?

前面我们使用了 dir(math) 和 dir(text),都是把对象传给 dir(),让它列出该对象相关的名称。

如果不传参数呢?

1
dir()

不带参数的 dir() 会尝试返回当前局部作用域中的名称。在常见的模块和函数场景中,可以这样理解:

  • 在模块顶层调用时,返回当前模块命名空间中的名称。此时,模块顶层的局部命名空间与全局命名空间相对应。
  • 在函数内部调用时,返回当前函数局部作用域中的名称。
  • 在类定义体等特殊执行环境中,结果还可能受到当前执行环境的影响。

这里需要注意:不带参数的 dir() 主要用于列出当前局部作用域中的名称,它不等于列出所有能够通过变量查找规则访问到的名称。

在模块顶层调用 dir()

创建文件 dir_scope.py:

1
2
3
4
5
6
7
8
9
name = "Linux"
count = 3


def check_server():
hostname = "server01"


print(dir())

运行:

1
python dir_scope.py

输出中会包含类似下面这些名称:

1
2
3
4
['__annotations__', '__builtins__', '__cached__',
'__doc__', '__file__', '__loader__', '__name__',
'__package__', '__spec__', 'check_server', 'count',
'name']

实际输出可能因 Python 版本和执行方式不同而有所差异。

为什么会出现 name、count 和 check_server?

因为它们都是在模块顶层定义的名称:

1
2
3
4
5
6
name = "Linux"
count = 3


def check_server():
hostname = "server01"

其中,name 和 count 是模块级变量,check_server 是模块级函数。

在模块顶层调用不带参数的 dir() 时,这些名称通常都会出现在结果中。

注意,定义函数时,函数体并不会立即执行。因此,函数内部的 hostname 此时不会出现在模块顶层的 dir() 结果中。

在函数内部调用 dir()

现在修改 dir_scope.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
name = "Linux"
count = 3


def check_server():
hostname = "server01"
port = 22

print(dir())


print("模块顶层:")
print(dir())

print("函数内部:")
check_server()

运行:

1
python dir_scope.py

模块顶层的结果会包含 name、count 和 check_server 等名称;函数内部的结果则会包含 hostname 和 port 等局部名称。

这里仅列出关键名称,完整输出还可能包含函数参数以及其他局部名称。

为什么两次调用的结果不同?

因为它们位于不同的作用域中。

当程序执行:

1
print(dir())

时,调用发生在模块顶层,因此返回模块当前命名空间中的名称。

而执行:

1
2
3
4
def check_server():
hostname = "server01"
port = 22
print(dir())

时,调用发生在函数内部,因此返回当前局部作用域中的名称。在这个例子中,hostname 和 port 都是函数的局部变量。

这里还要区分两个概念:作用域中的名称,以及变量查找时能够访问到的名称。

例如,name 是模块级变量。在函数内部,如果没有同名的局部变量,通常也可以直接读取它:

1
2
3
4
5
6
7
8
name = "Linux"


def check_server():
print(name)


check_server()

输出:

1
Linux

但是,在函数内部调用不带参数的 dir(),并不会因此把所有外层作用域中的名称都列出来。它主要反映当前局部作用域中的名称。

可以用下面的流程理解:

flowchart TD
    A["调用 dir() 不传参数"] --> B{"调用位置"}
    B -->|"模块顶层"| C["列出模块当前命名空间中的名称"]
    B -->|"函数内部"| D["列出函数当前局部作用域中的名称"]
    C --> E["返回名称列表"]
    D --> E

如果需要明确查看模块的全局命名空间,应该使用 globals();如果需要查看当前局部命名空间,可以使用 locals()。

dir()、globals() 与 locals() 有什么区别?

这三个函数看起来有些相似,但返回结果和用途并不相同。

使用 globals() 查看全局命名空间

globals() 返回当前模块的全局命名空间字典。

例如,创建文件 scope_demo.py:

1
2
3
4
5
6
7
8
9
10
11
12
name = "Linux"
count = 3


def check_server():
hostname = "server01"

print("全局变量 name:", globals()["name"])
print("局部变量 hostname:", hostname)


check_server()

运行:

1
python scope_demo.py

输出:

1
2
全局变量 name: Linux
局部变量 hostname: server01

这里的:

1
globals()["name"]

表示从当前模块的全局命名空间字典中,读取键为 name 的值。

需要注意,即使在函数内部调用 globals(),它返回的仍然是当前模块的全局命名空间,而不是函数的局部命名空间。

例如:

1
2
3
4
5
6
7
8
9
10
11
name = "Linux"


def check_server():
hostname = "server01"

print("globals 中有 name:", "name" in globals())
print("globals 中有 hostname:", "hostname" in globals())


check_server()

输出:

1
2
globals 中有 name: True
globals 中有 hostname: False

原因很简单:name 定义在模块顶层,而 hostname 是函数局部变量。

这也说明了 globals() 与不带参数的 dir() 并不是同一回事。

使用 locals() 查看局部命名空间

locals() 用于获取当前局部命名空间的映射。

例如:

1
2
3
4
5
6
7
8
def check_server():
hostname = "server01"
port = 22

print(locals())


check_server()

输出:

1
{'hostname': 'server01', 'port': 22}

在这个函数中,locals() 返回的映射包含 hostname 和 port 等局部变量及其值。

与 dir() 返回名称列表不同,locals() 返回的是名称与对象之间的映射关系。

需要注意,在模块顶层调用 locals() 时,它与 globals() 返回的是同一个模块命名空间。函数内部则要根据当前执行环境理解其结果。

另外,在函数内部,locals() 适合用来检查局部变量,但不要依赖修改它返回的映射来改变函数局部变量,因为这种修改在优化后的函数作用域中并不保证生效。

三者的区别

函数返回结果主要用途
dir()名称列表查看对象相关的名称;不带参数时查看当前局部作用域中的名称
globals()全局命名空间字典获取当前模块的全局名称及其对应对象
locals()局部命名空间映射查看当前局部环境中的名称及其对应对象

初学阶段,可以这样记忆:

  • 想了解模块提供了哪些名称,使用 dir(module)。
  • 想查看当前局部作用域中有哪些名称,使用不带参数的 dir()。
  • 想获取当前模块的全局命名空间,使用 globals()。
  • 想查看函数当前的局部变量及其值,使用 locals()。

如何判断一个名称是函数还是变量?

现在,我们已经能够列出模块中的名称了。

但是,看到一个名称后,怎样知道它究竟是函数、变量,还是其他类型的对象?

例如:

1
2
3
import math

print(dir(math))

输出中有 sqrt,也有 pi。

如果我们希望进一步判断它们的类型,可以结合 type() 和 callable() 使用。

创建文件 dir_inspect.py:

1
2
3
4
5
6
7
import math

print("sqrt 的类型:", type(math.sqrt))
print("pi 的类型:", type(math.pi))

print("sqrt 是否可以调用:", callable(math.sqrt))
print("pi 是否可以调用:", callable(math.pi))

运行:

1
python dir_inspect.py

在常见的 CPython 环境中,输出类似:

1
2
3
4
sqrt 的类型: <class 'builtin_function_or_method'>
pi 的类型: <class 'float'>
sqrt 是否可以调用: True
pi 是否可以调用: False

我们逐行解释。

type(math.sqrt) 用于查看 sqrt 对象的类型。在当前常见的 CPython 环境中,它通常显示为 builtin_function_or_method。

type(math.pi) 则返回 float,因为 pi 是浮点数。

callable() 用于判断一个对象是否可以被调用。对于这里的两个对象,math.sqrt 可以调用,而 math.pi 不可以。

注意,callable() 返回 True 并不意味着调用一定成功。例如,传入错误的参数仍然可能导致异常。

这套组合非常实用:

1
2
3
dir(obj)
type(obj.attr)
callable(obj.attr)

这里的 obj 和 attr 只是示意名称,使用时需要替换成实际的对象和属性。

先查看名称,再检查类型,最后判断是否可以调用。对于初学者来说,这是一种探索陌生模块的简单方法。

结合 help() 查看具体用法

dir() 可以告诉我们有哪些名称,但无法直接解释每个函数应该传入什么参数。

例如,看到 math.sqrt 后,我们可能会问:

  • 它需要几个参数?
  • 参数应该是什么类型?
  • 它返回什么结果?
  • 参数不合法时会发生什么?

这时候就可以使用 Python 内置的 help() 函数。

例如:

1
2
3
import math

help(math.sqrt)

执行后,Python 会显示 sqrt() 的帮助信息,其中包含函数的用途、参数说明和返回值等文档信息。具体显示内容取决于该对象提供的帮助文档。

你也可以直接查询整个模块:

1
2
3
import math

help(math)

不过,整个模块的帮助信息可能很长。

如果只是想了解一个具体函数,通常建议直接查询它:

1
2
3
4
5
import math

help(math.floor)
help(math.ceil)
help(math.sqrt)

在 Python 交互式解释器中,还可以先导入模块,再逐步探索:

1
2
3
4
5
>>> import math
>>> dir(math)
>>> help(math.sqrt)
>>> math.sqrt(81)
9.0

整个过程可以概括为:

flowchart LR
    A["导入模块"] --> B["使用 dir() 查看名称"]
    B --> C["选择感兴趣的名称"]
    C --> D["使用 type() 和 callable() 检查"]
    D --> E["使用 help() 查看说明"]
    E --> F["编写代码并验证结果"]

这样,即使遇到一个之前没有使用过的模块,也可以先自己探索,再决定是否查阅更详细的官方文档。

理解 dir() 与 all 的区别

上一篇学习模块时,我们接触过 from module import * 这种导入方式。

例如:

1
from math import *

这种写法会根据模块的导入规则,将相应名称导入当前命名空间。

那么,问题来了:模块中有那么多名称,使用 import * 时,究竟哪些名称会被导入?

这里就涉及一个特殊名称:__all__。

先看结论:

dir() 用于查看名称,而 __all__ 用于指定通配符导入时公开的名称列表。两者的用途不同。

创建一个自定义模块

为了理解它们的区别,我们自己创建一个模块。

进入之前的练习目录:

1
cd ~/python-dir-demo

创建 server_tools.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def check_cpu():
print("检查 CPU 使用率")


def check_memory():
print("检查内存使用情况")


def check_disk():
print("检查磁盘空间")


version = "1.0"

__all__ = ["check_cpu", "check_disk"]

这里定义了三个函数:

  • check_cpu():模拟检查 CPU。
  • check_memory():模拟检查内存。
  • check_disk():模拟检查磁盘。

此外,还有一个 version 变量,用来表示模块版本。

最重要的是这一行:

1
__all__ = ["check_cpu", "check_disk"]

它表示:当其他代码使用 from server_tools import * 导入这个模块时,指定导入的名称为 check_cpu 和 check_disk。

注意,__all__ 不是安全机制,也不会禁止别人通过 import server_tools 访问模块中的其他名称。

使用 dir() 查看模块

创建文件 test_dir.py:

1
2
3
import server_tools

print(dir(server_tools))

运行:

1
python test_dir.py

输出中会包含:

1
2
3
4
5
check_cpu
check_disk
check_memory
version
__all__

上面仅列出部分关键名称。实际输出还会包含模块的其他属性,具体内容可能有所不同。

可以看到,虽然 __all__ 只列出了两个函数,但 dir(server_tools) 仍然可以看到 check_memory、version 和 __all__ 等名称。

这说明 __all__ 并不会限制 dir() 的输出。

使用 from…import * 导入

再创建一个文件 test_all.py:

1
2
3
4
from server_tools import *

check_cpu()
check_disk()

运行:

1
python test_all.py

输出:

1
2
检查 CPU 使用率
检查磁盘空间

如果我们改成下面这样:

1
2
3
from server_tools import *

check_memory()

运行后,就会出现类似下面的错误:

1
NameError: name 'check_memory' is not defined

为什么?

因为 server_tools.py 定义了 __all__,而 check_memory 不在这个列表中,所以它不会通过 from server_tools import * 自动导入到当前命名空间。

但如果使用普通导入:

1
2
3
import server_tools

server_tools.check_memory()

仍然可以正常运行:

1
检查内存使用情况

这就体现了两者的区别。

对比项dir()__all__
类型内置函数模块中可定义的特殊名称
主要作用查看对象相关的名称指定通配符导入时公开的名称
是否控制 dir() 输出否否
是否影响 from module import *不直接影响会影响
是否禁止访问未列出的名称否否

实际开发中,通常更推荐明确导入需要使用的名称:

1
from server_tools import check_cpu, check_disk

或者:

1
2
3
4
import server_tools

server_tools.check_cpu()
server_tools.check_disk()

这样代码的依赖关系更加清晰,也不容易因为名称冲突导致难以排查的问题。

Linux 运维实战:探索 shutil 模块

前面我们主要使用 math 模块来演示。

现在,把这些知识放到实际的 Linux 运维场景中。

假设你正在编写一个 Python 脚本,希望检查 Linux 服务器的磁盘空间,但暂时不熟悉 shutil 模块。

我们可以先查看模块有哪些名称。

创建文件 dir_shutil.py:

1
2
3
import shutil

print(dir(shutil))

运行:

1
python dir_shutil.py

输出会包含许多名称,例如:

1
2
3
4
5
6
7
8
9
10
copy
copy2
copyfile
disk_usage
get_terminal_size
make_archive
move
rmtree
unpack_archive
...

这些名称对应着不同的文件操作功能。

例如:

  • copy():复制文件。
  • move():移动文件或目录。
  • rmtree():删除目录树。
  • make_archive():创建归档文件。
  • disk_usage():查询文件系统的空间使用情况。

如果我们的目标是检查磁盘空间,那么可以进一步查询 disk_usage():

1
2
3
import shutil

help(shutil.disk_usage)

查看帮助信息后,再编写一个简单的磁盘检查脚本。

创建 check_disk.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
import shutil

path = "/"

total, used, free = shutil.disk_usage(path)

usage = used / total * 100

print(f"检查路径:{path}")
print(f"磁盘总容量:{total / (1024 ** 3):.2f} GiB")
print(f"已使用空间:{used / (1024 ** 3):.2f} GiB")
print(f"剩余空间:{free / (1024 ** 3):.2f} GiB")
print(f"空间使用率:{usage:.2f}%")

运行:

1
python check_disk.py

输出示例:

1
2
3
4
5
检查路径:/
磁盘总容量:100.00 GiB
已使用空间:35.50 GiB
剩余空间:64.50 GiB
空间使用率:35.50%

这里的容量仅为示例,实际输出取决于运行脚本的文件系统。

我们解释一下关键代码:

1
total, used, free = shutil.disk_usage(path)

shutil.disk_usage() 返回一个包含 total、used 和 free 三个字段的命名元组,分别表示文件系统的总容量、已使用容量和剩余容量,单位都是字节。

接下来:

1
usage = used / total * 100

通过已使用容量除以总容量,再乘以 100,计算空间使用率。

最后使用格式化字符串:

1
print(f"空间使用率:{usage:.2f}%")

其中 :.2f 表示保留两位小数。

通过这个例子,我们就把 dir()、help() 和实际编程串联起来了。

如果以后需要检查其他系统信息,也可以采用相同思路:

  1. 导入相关模块。
  2. 使用 dir() 查看名称。
  3. 找到可能需要的函数。
  4. 使用 help() 查看用法。
  5. 编写一个最小示例并验证结果。

需要注意,磁盘空间检查只是一个基础示例。如果用于生产环境,还需要考虑挂载点选择、异常处理、阈值告警以及容器环境下的文件系统语义等问题。

使用 dir() 时需要注意什么?

学会 dir() 之后,也不要把它当作万能工具。

dir() 不等于完整 API 文档

dir() 主要列出对象相关的名称,但不会完整说明每个名称的参数、返回值、适用场景和使用限制。

因此,探索模块时,建议配合 help() 或官方文档使用。

dir() 不代表所有名称都能直接调用

例如:

1
2
3
import math

print(math.pi)

这里的 pi 是一个浮点数,而不是函数。

所以,不要看到 dir() 中有一个名称,就直接假设它可以加括号调用。

不要把 dir() 的输出写死在程序里

不同 Python 版本、不同对象以及不同实现可能会有不同的名称列表。

如果你正在编写自动化程序,不要依赖某次 dir() 输出的完整内容来判断功能是否存在。

如果确实需要检查某个属性,可以考虑:

1
2
3
4
import math

if hasattr(math, "sqrt"):
print(math.sqrt(16))

hasattr() 可以判断对象是否具有指定名称的属性。不过,如果你需要调用该属性,还应该根据实际情况确认它是否可调用,以及调用方式是否正确。

不要依赖 from module import * 来组织大型项目

虽然 __all__ 可以控制通配符导入时的公开名称,但在大型项目中,from module import * 往往会让代码的名称来源不够清楚。

相比之下,显式导入通常更容易阅读和维护:

1
from shutil import disk_usage

或者:

1
2
3
import shutil

shutil.disk_usage("/")

选择哪种方式取决于项目规范和使用场景,但对于初学者,建议优先掌握明确导入的写法。

写在最后

今天,我们学习了 Python 中的 dir() 函数。

回顾一下:

  • dir(module) 可以快速查看模块中的名称。
  • dir(object) 也可以用于探索字符串等其他对象。
  • 不带参数的 dir() 会根据调用位置返回当前局部作用域中的名称;在模块顶层通常对应模块命名空间,在函数内部通常对应函数局部作用域。
  • globals() 返回当前模块的全局命名空间字典,即使在函数内部调用也一样。
  • locals() 用于获取当前局部命名空间的映射。
  • type() 和 callable() 可以帮助我们判断对象的类型以及是否可调用。
  • help() 可以进一步查看函数和模块的帮助信息。
  • __all__ 用于指定 from module import * 时公开的名称,它不会限制 dir() 的输出,也不是访问权限控制机制。

对于 Python 初学者来说,学习一门新语言,不必一开始就记住所有函数和模块。

更重要的是掌握探索工具:遇到陌生模块,先使用 dir() 找到可能需要的功能,再通过 help() 了解具体用法,最后编写代码验证结果。

这样,随着学习深入,你就能逐渐建立起独立探索 Python 标准库和第三方模块的能力。

下一篇,我们将继续学习 Python 标准库,看看有哪些值得掌握的常用模块,以及它们如何帮助我们减少重复开发工作,让 Linux 运维自动化变得更加高效。