第 10 章:模块与包
第 10 章:模块与包
“模块是代码复用的单位”
Perl 的模块系统是 CPAN 生态的基础。理解 package、use、require 和 Exporter 是编写可维护代码的关键。
10.1 package — 命名空间
# 命名空间(Namespace)
package MyApp::Utils;
use strict;
use warnings;
our $VERSION = "1.00";
sub hello {
my ($name) = @_;
return "Hello, $name!";
}
# 包内函数是完全限定名
# MyApp::Utils::hello("World")
1; # 模块必须返回真值
命名空间规则
# 包名使用 :: 分隔
package MyApp::Core::Database; # 对应文件 MyApp/Core/Database.pm
# 变量在包空间内
package Config;
our $debug = 1; # $Config::debug
package main; # 默认包
print $Config::debug, "\n"; # 1
10.2 use vs require
| 特性 | use | require |
|---|
| 执行时机 | 编译时 | 运行时 |
| 导入符号 | ✅ 自动导入 | ❌ 不导入 |
| 调用方式 | use Module LIST | require Module |
| 等价于 | BEGIN { require Module; Module->import(LIST) } | 直接加载 |
# use — 编译时加载并导入
use List::Util qw(max min sum);
print max(1, 2, 3), "\n";
# require — 运行时加载
require List::Util;
print List::Util::max(1, 2, 3), "\n";
# 动态加载
my $module = "JSON::XS";
my $file = "$module.pm";
$file =~ s{::}{/}g;
require $file;
10.3 @INC — 模块搜索路径
# 查看模块搜索路径
perl -e 'print join "\n", @INC, "\n"';
# 运行时添加路径
use lib "/home/user/mylibs";
use lib "/opt/perl/site/lib";
# 环境变量
# export PERL5LIB="/extra/libs:/another/path"
# 命令行
# perl -I/path/to/libs script.pl
10.4 创建自己的模块
步骤 1:创建模块文件
# lib/MyApp/Math.pm
package MyApp::Math;
use strict;
use warnings;
use Exporter 'import';
our $VERSION = "1.00";
our @EXPORT_OK = qw(add multiply PI);
our %EXPORT_TAGS = (
all => \@EXPORT_OK,
basics => [qw(add multiply)],
);
use constant PI => 3.14159265358979;
sub add {
my ($a, $b) = @_;
return $a + $b;
}
sub multiply {
my ($a, $b) = @_;
return $a * $b;
}
1; # 必须返回真值
步骤 2:使用模块
#!/usr/bin/env perl
use strict;
use warnings;
use lib "lib";
use MyApp::Math qw(:all);
print add(3, 5), "\n"; # 8
print multiply(4, 6), "\n"; # 24
print PI, "\n"; # 3.14159265358979
10.5 Exporter — 符号导出
| 变量 | 说明 |
|---|
@EXPORT | 默认导出的符号(不推荐) |
@EXPORT_OK | 可选导出的符号(推荐) |
%EXPORT_TAGS | 标签分组 |
package MyModule;
use Exporter 'import';
our @EXPORT = qw(always_imported); # 自动导入(不推荐)
our @EXPORT_OK = qw(optional_func); # 按需导入(推荐)
our %EXPORT_TAGS = (
utils => [qw(func1 func2)],
all => [qw(func1 func2 optional_func)],
);
# 使用时:
use MyModule; # 导入 @EXPORT
use MyModule qw(optional_func); # 导入指定函数
use MyModule qw(:utils); # 导入标签组
use MyModule qw(:all); # 导入所有
use MyModule (); # 不导入任何符号
10.6 Perl 的模块打包规范
MyApp-Utils-1.00/
├── lib/
│ └── MyApp/
│ └── Utils.pm
├── t/
│ ├── 01-basic.t
│ └── 02-functions.t
├── Changes
├── LICENSE
├── Makefile.PL
├── MANIFEST
├── README.md
└── cpanfile
Makefile.PL
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'MyApp::Utils',
VERSION_FROM => 'lib/MyApp/Utils.pm',
ABSTRACT => 'Utility functions for MyApp',
AUTHOR => 'Your Name <[email protected]>',
LICENSE => 'perl_5',
PREREQ_PM => {
'JSON::XS' => '4.0',
'Try::Tiny' => '0.30',
},
TEST_REQUIRES => {
'Test::More' => '0.96',
},
);
10.7 业务场景:配置管理模块
# lib/MyApp/Config.pm
package MyApp::Config;
use strict;
use warnings;
use Exporter 'import';
use JSON::XS;
use File::Basename;
our @EXPORT_OK = qw(load_config get_config);
our $VERSION = "1.00";
my %CONFIG;
sub load_config {
my ($file) = @_;
die "配置文件不存在: $file\n" unless -f $file;
open my $fh, '<:encoding(UTF-8)', $file
or die "无法打开 $file: $!\n";
local $/;
my $json = <$fh>;
close $fh;
%CONFIG = %{decode_json($json)};
return \%CONFIG;
}
sub get_config {
my ($key, $default) = @_;
return $CONFIG{$key} // $default;
}
1;
本章小结
| 要点 | 内容 |
|---|
package | 定义命名空间 |
use | 编译时加载并导入模块 |
require | 运行时加载模块 |
@INC | 模块搜索路径 |
Exporter | 控制符号导出 |
@EXPORT_OK | 推荐的按需导出方式 |
练习
- 创建一个
MyApp::String 模块,提供 trim、upper、lower 函数 - 使用
%EXPORT_TAGS 将函数分为 :basic 和 :all 两组 - 创建一个加载 JSON 配置文件的模块
- 研究
perldoc -l Module::Name 查看模块路径 - 编写一个支持
use MyApp::Config (file => "config.json") 的模块
扩展阅读