Perl模块如何“工作"? [英] How do Perl modules "work"?

查看:83
本文介绍了Perl模块如何“工作"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Perl模块感到困惑.我知道可以使用模块 将大量的subs转储到整齐的主代码中.

I am confused about Perl modules. I get that a module can be used to dump a whole bunch of subs into, tidying main code.

但是,模块之间的关系是什么?

But, what is the relationship between modules?

模块可以使用"其他模块吗?

Can modules "use" other modules?

我必须使用导出,还是可以放弃这些东西?

Must I use export, or can I get away with dropping that stuff?

我该如何解决循环使用问题? (Security.pm使用Html.pmHtml.pm使用Security.pm).我知道明显的答案,但是在某些情况下,我需要在Html.pm中使用Security.pm例程,反之亦然-不确定如何解决该问题.

How do I solve circular use? (Security.pm uses Html.pm and Html.pm uses Security.pm). I know the obvious answer, but in some cases I need to use Security.pm routines in Html.pm and vice versa - not sure how to get around the problem.

如果我从所有模块中删除所有"use"子句,则必须使用完整的子限定符.例如,Pm::Html::get_user_friends($dbh, $uid)将使用Security来确定朋友是否是被禁止的用户(被禁止属于Security的一部分).

If I remove all "use" clauses from all of my modules ... Then I have to use full sub qualifiers. For example, Pm::Html::get_user_friends($dbh, $uid) will use Security to determine if a friend is a banned user or not (banned is part of Security).

我只是没有得到这个模块的东西.所有的教程"都只讲一个模块,从不讲多个模块,也不用真实的例子.

I just don't get this module thing. All the "tutorials" only speak of one module, never multiple, nor do they use real world examples.

我遇到多个模块的唯一时间是使用OO代码时.但是没有什么可以明确地告诉我多个模块如何交互的方式.

The only time I've come across multiple modules is when using OO code. But nothing that tells me definitively one way or another how multiple modules interact.

推荐答案

Perl中的模块具有多种风格,并且具有使它们成为模块的几种不同方式.

Modules in Perl come in several flavors and have several different things that make them a module.

某些东西可以视为模块:

  • the filename ends in .pm,
  • there is a package declaration in the file,
  • the package name matches the filename and path; Data/Dumper.pm contains package Data::Dumper,
  • it ends with a 1 or another true value.

然后有一些公认的约定:

Then there are some accepted conventions:

  • 模块通常只应包含一个package
  • 模块名称应为骆驼套,并且不得包含下划线_(例如:Data::DumperWWW::Mechanize::Firefox)
  • 完全用小写字母表示的
  • 模块不是模块,它们是 pragmas .
  • modules should usually only contain one package,
  • module names should be camel case and should not contain underscores _ (example: Data::Dumper, WWW::Mechanize::Firefox)
  • modules that are in small letters completely are not modules, they are pragmas.

通常,一个模块要么包含功能集合(sub s),要么是面向对象.让我们先来看一下这些集合.

Usually a module either contains a collection of functions (subs) or it is object oriented. Let's look at the collections first.

一个捆绑了一系列相关功能的典型模块,使用一种方法将这些功能导出到您的代码命名空间中.一个典型的示例是 List :: Util .有几种导出内容的方法.最常见的是导出器.

A typical module that bundles a bunch of functionality that is related uses a way to export those functions into your code's namespace. A typical example is List::Util. There are several ways to export things. The most common one is Exporter.

当您从模块中获取一个函数以将其放入代码中时,这称为导入.如果您想多次使用该函数,这将很有用,因为它会使名称简短.导入时,可以直接通过其名称调用它.

When you take a function from a module to put it into your code, that's called importing it. That is useful if you want to use the function a lot of times, as it keeps the name short. When you import it, you can call it directly by its name.

use List::Util 'max';
print max(1, 2, 3);

不导入时,需要使用标准名称.

When you don't import it, you need to use the fully qualified name.

use List::Util (); # there's an empty list to say you don't want to import anything
print List::Util::max(1, 2, 3); # now it's explicit

之所以有效,是因为Perl将List::Util::max后面的函数的引用安装到名称空间max下的名称空间中.如果不这样做,则需要使用全名.有点像Windows桌面上的快捷方式.

This works because Perl installs a reference to the function behind List::Util::max into your namespace under the name max. If you don't do that, you need to use the full name. It's a bit like a shortcut on your desktop in Windows.

您的模块不必提供导出/导入.您可以将其用作东西的集合,并用它们的全名来称呼它们.

Your module does not have to provide exporting/importing. You can just use it as a collection of stuff and call them by their full names.

尽管每个称为 module .pm文件,人们通常也将作为 distribution 的整个事物集合称为一个模块.我想到了 DBI 之类的东西,其中包含很多.pm文件,它们都是模块,但是仍然人们只谈论 DBI模块.

While every .pm file called a module, people often also refer to a whole collection of things that are a distribution as a module. Something like DBI comes to mind, which contains a lot of .pm files, which are all modules, but still people talk about the DBI module only.

并非每个模块都需要包含独立功能.一个模块(现在我们将更多地讨论上面的模块)也可以包含一个 .在这种情况下,它通常不导出任何功能.实际上,我们不再调用sub函数,而是调用方法. package名称成为类的名称,您创建名为 objects 的类的 instances ,然后在这些对象上调用方法,这些方法最终成为您的包裹.

Not every module needs to contain stand-alone functions. A module (now we're more talking about the one directly above) can also contain a class. In that case it usually does not export any functions. In fact, we do not call the subs functions any more, but rather methods. The package name becomes the name of the class, you create instances of the class called objects, and you call methods on those objects, which ends up being the functions in your package.

在Perl中加载模块有两种主要方法.您可以在编译时运行时进行操作. perl 1 编译器(是的,虽然有解释型语言,但有一个编译器)会加载文件,对其进行编译,然后切换到运行时来运行已编译的代码.当它遇到要加载的新文件时,它将切换回编译时,编译新代码,等等.

There are two main ways of loading a module in Perl. You can do it at compile time and at run time. The perl1 compiler (yes, there is a compiler although it's interpreted language) loads files, compiles them, then switches to run time to run the compiled code. When it encounters a new file to load, it switches back to compile time, compiles the new code, and so on.

编译时间

要在编译时加载模块,请use.

To load a module at compile time, you use it.

use Data::Dumper;
use List::Util qw( min max );
use JSON ();

这等效于以下内容.

BEGIN {
  require Data::Dumper;
  Data::Dumper->import;

  require List::Util;
  List::Util->import('min', 'max');

  require JSON;
  # no import here
}

BEGIN在编译期间被调用.链接文档中的示例有助于理解这些来回切换的概念.

The BEGIN block gets called during compile time. The example in the linked doc helps understand the concept of those switches back and forth.

use语句通常位于程序的顶部.首先要进行 pragmas (在执行shebang之后,use strictuse warnings应该始终是您的第一件事),然后是use语句.应该使用它们,以便您的程序在启动过程中加载它所需的所有内容.这样在运行时,它将更快.对于长时间运行或无关紧要的事情,例如在 Plack <上运行的Web应用程序/a>这就是您想要的.

The use statements usually go at the top of you program. You do pragmas first (use strict and use warnings should always be your very first things after the shebang), then use statements. They should be used so your program loads everything it needs during startup. That way at run time, it will be faster. For things that run for a long time, or where startup time doesn't matter, like a web application that runs on Plack this is what you want.

运行时间

要在运行时加载某些内容,请使用require.它不会为您导入任何内容.它还切换到暂时为新文件编译时间,但是然后返回到其剩余位置的运行时间.这样就可以有条件地加载模块,这在CGI上下文中尤其有用,在CGI上下文中,在运行期间解析新文件所花费的额外时间超过了为程序的每次调用加载所有内容的成本,尽管可能并非如此.需要.

When you want to load something during run time, you use require. It does not import anything for you. It also switches to compile time for the new file momentarily, but then goes back to run time where it left of. That makes it possible to load modules conditionally, which can be useful especially in a CGI context, where the additional time it takes to parse a new file during the run outweighs the cost of loading everything for every invocation of the program although it might not be needed.

require Data::Dumper;

if ($foo) {
    require List::Util;
    return List::Util::max( 1, 2, 3, $foo );
}

还可以将字符串或变量传递给require,因此您不仅可以有条件地加载内容,还可以动态加载.

It is also possible to pass a string or a variable to require, so you can not only conditionally load things, but also dynamically.

my $format = 'CSV'; # or JSON or XML or whatever
require "My::Parser::$format";

这是相当高级的,但是有一些用例.

This is pretty advanced, but there are use-cases for it.

此外,还可以在运行时以.pl结尾的require普通Perl文件.这通常是在旧版代码(我称其为意大利面条)中完成的.不要在新代码中这样做.也不要在旧代码中这样做.这是不好的做法.

In addition, it's also possible to require normal Perl files with a .pl ending at run time. This is often done in legacy code (which I would call spaghetti). Don't do it in new code. Also don't do it in old code. It's bad practice.

通常,您应该始终userequire在任何给定模块中依赖的每个模块.永远不要依赖于以下事实的其他 下游 部分您的代码为您加载了东西.模块旨在用于封装功能,因此它们至少应能够站立在其上拥有一点.如果您想在以后重用其中一个模块,而又忘记包含一个依赖项,则会引起麻烦.

In general you should always use or require every module that you depend on in any given module. Never rely on the fact that some other downstream part of your code loads things for you. Modules are meant to encapsulate functionality, so they should be able to at least stand on their own a little bit. If you want to reuse one of your modules later, and you forgot to include a dependency it will cause you grief.

这也使阅读代码更容易,如顶部明确说明的依赖关系和导入可帮助维护人员(或未来)来了解您的代码是关于什么,它做什么以及如何做.

It also makes it easier to read your code, as clearly stated dependencies and imports at the top help the maintenance guy (or future you) to understand what your code is about, what it does and how it does it.

Perl会为您解决这个问题.在编译时解析代码时,它会跟踪其已加载的内容.将这些内容放入超全局变量%INC 中,该哈希是已加载的名称以及它们的来源.

Perl takes care of that for you. When it parses the code at compile time, it keeps track of what it has loaded. Those things to into the super-global variable %INC, which is a hash of names that have been loaded, and where they came from.

$ perl -e 'use Data::Dumper; print Dumper \%INC'
$VAR1 = {
          'Carp.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Carp.pm',
          'warnings.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/warnings.pm',
          'strict.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/strict.pm',
          'constant.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/constant.pm',
          'XSLoader.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/x86_64-linux/XSLoader.pm',
          'overloading.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/overloading.pm',
          'bytes.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/bytes.pm',
          'warnings/register.pm' => '/home/julien/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/warnings/register.pm',
          'Exporter.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Exporter.pm',
          'Data/Dumper.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/x86_64-linux/Data/Dumper.pm',
          'overload.pm' => '/home/foo/perl5/perlbrew/perls/perl-5.20.1/lib/5.20.1/overload.pm'
        };

每次调用userequire都会在该哈希中添加一个新条目,除非该条目已经存在.在这种情况下,Perl不会再次加载它.如果您use d该模块,它仍然会为您导入名称.这样可以确保没有循环依赖项.

Every call to use and require adds a new entry in that hash, unless it's already there. In that case, Perl does not load it again. It still imports names for you if you used the module though. This makes sure that there are no circular dependencies.

关于遗留代码要记住的另一重要事项是,如果您require普通.pl文件,则需要正确找到路径.因为%INC中的键不是模块名,而是您传递的字符串,所以执行以下操作将导致同一文件被加载两次.

Another important thing to keep in mind with regards to legacy code is that if you require normal .pl files, you need to get the path right. Because the key in %INC will not be the module name, but instead the string you passed, doing the following will result in the same file being loaded twice.

perl -MData::Dumper -e 'require "scratch.pl"; require "./scratch.pl"; print Dumper \%INC'
$VAR1 = {
          './scratch.pl' => './scratch.pl',
          'scratch.pl' => 'scratch.pl',
          # ...
        };

模块从何处加载

就像%INC一样,还有一个超级全局变量@INC ,其中包含Perl在其中查找模块的路径.您可以使用lib编译指示或通过环境变量PERL5LIB向其中添加内容.

Where modules are loaded from

Just like %INC, there is also a super global variable @INC, which contains the paths that Perl looks for modules in. You can add stuff to it by using the lib pragma, or via the environment variable PERL5LIB among other things.

use lib `lib`;
use My::Module; # this is in lib/My/Module.pm

命名空间

您在模块中使用的软件包在Perl中定义了名称空间.默认情况下,创建不带package的Perl脚本时,您位于软件包main中.

Namespaces

The packages you use in your modules define namespaces in Perl. By default when you create a Perl script without a package, you are in the package main.

#!/usr/bin/env perl
use strict;
use warnings;

sub foo { ... }

our $bar;

sub foo在主.pl文件中将以foo的形式提供,但在其他任何地方也将以main::foo的形式提供.简写为::foo.包变量$bar也是如此.确实是$main::bar$::bar.谨慎使用.您不希望脚本中的内容泄漏到模块中.这是一个非常糟糕的做法,以后会再次出现并咬住你.

The sub foo will be available as foo inside the main .pl file, but also as main::foo from anywhere else. The shorthand is ::foo. The same goes for the package variable $bar. It's really $main::bar or just $::bar. Use this sparingly. You don't want stuff from your script to leak over in your modules. That's a very bad practice that will come back and bite you later.

在您的模块中,事物位于声明它们的包的名称空间中.这样,您可以从外部访问它们(除非它们被 lexical作用域定义为my),应该为大多数事情做).多数情况下还可以,但是您不应该将其他代码的内部弄得一团糟.除非您想破坏东西,否则请使用已定义的接口.

In your modules, things are in the namespace of the package they are declared in. That way, you can access them from the outside (unless they are lexically scoped with my, which you should do for most things). That is mostly ok, but you should not be messing with internals of other code. Use the defined interface instead unless you want to break stuff.

将某些内容导入名称空间时,它就是如上所述的快捷方式.这可能很有用,但是您也不想污染您的名称空间.如果您将很多东西从一个模块导入到另一个模块,那么这些东西也将在该模块中可用.

When you import something into your namespace, all it is is a shortcut as described above. This can be useful, but you also do not want to pollute your namespaces. If you import a lot of things from one module to another module, those thing will become available in that module too.

package Foo;
use List::Util 'max';

sub foo { return max(1, 2, 3) }

package main; # this is how you switch back
use Foo;

print Foo::max(3, 4, 5); # this will work

由于您通常不希望发生这种情况,因此应仔细选择要导入到名称空间中的内容.另一方面,您可能不在乎,这也可以.

Because you often do not want this to happen, you should chose carefully what you want to import into your namespace. On the other hand you might not care, which can be fine, too.

Perl不了解私有或公共的概念.当您知道名称空间的工作方式后,您几乎可以了解所有非词汇的内容.甚至有很多方法可以进入词汇表,但是它们涉及一些神秘的黑魔法,我不再赘述.

Perl does not understand the concept of private or public. When you know how the namespaces work you can pretty much get to everything that is not lexical. There are even ways to get to lexicals to, but they involve some arcane black magic and I'll not go into them.

但是,关于如何将事物标记为私有存在约定.每当函数或变量以下划线开头时,应将其视为私有.像 Data :: Printer 这样的现代工具在显示数据时都会考虑到这一点.

However, there is a convention on how to mark things as private. Whenever a function or variable starts with an underscore, it should be considered private. Modern tools like Data::Printer take that into account when displaying data.

package Foo;

# this is considered part of the public interface
sub foo { 
    _bar();
}

# this is considered private
sub _bar {
    ...
}

优良作法是开始执行此类操作,并远离CPAN上的模块内部.诸如此类的名称被认为是不稳定的,它们不是API的一部分,并且可以随时更改.

It's good practice to start doing things like that, and to keep away from the internals of modules on CPAN. Things that are named like that are not considered stable, they are not part of the API and they can change at any time.

这是此处涉及的一些概念的非常广泛的概述.一旦使用几次,大多数内容将很快成为您的第二天性.我记得在接受开发人员培训的过程中,我花了大约一年的时间来解决这个问题,尤其是在出口方面.

This was a very broad overview of some of the concepts involved here. Most of it will quickly become second nature to you once you've used it a few times. I remember that it took me about a year during my training as a developer to wrap my head around that, especially exporting.

启动新模块时,perldoc页面 perlnewmod 很有帮助.您应该阅读并确保您理解它的意思.

When you start a new module, the perldoc page perlnewmod is very helpful. You should read that and make sure you understand what it says.

1:注意 perl 中的小p吗?我在这里说的是程序,不是语言的名称,即 Perl .

1: notice the small p in perl? I'm talking about the program here, not the name of the language, which is Perl.

这篇关于Perl模块如何“工作"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆