Домены Perl наследуют импортированные модули и прагмы?

Предположим, что у вас есть родительский класс Perl в одном файле:

#!/usr/bin/perl
package Foo;
use strict;
use warnings;

use Data::Dumper;

sub new{
    my $class = shift;
    my %self = ();
    return bless %self, $class;
}
1;

и подкласс в другом файле:

#!/usr/bin/perl
package Bar;
use base "Foo";
1;

Будет ли подкласс наследовать операторы использования от родителя? Я знаю, что новый метод будет унаследован.

В основном я пытаюсь уменьшить количество шаблонов в моем коде, и я не могу найти четкого ответа на этот вопрос.

Ответ 1

А, хороший вопрос!

Will the subclass inherit the use statements from the parent? 

Хорошо, это зависит от того, что вы подразумеваете под наследованием. Я не буду делать никаких предположений до конца, но, может быть, ответ. Видите ли, perl смешивает идеи Classes, а Namespaces - a package - это термин, который может описывать любой из них. Теперь проблема заключается в утверждении use все, что она делает, это принудительное включение пакета и вызов целевых import() sub. Это означает, что он по существу имеет неограниченный контроль над вашим пакетом - и тем самым ваш класс.

Теперь составьте это, когда все методы в perl будут не более чем subs, которые принимают $self как первый аргумент по соглашению, и вы остаетесь с perl5. У этого есть огромный потенциал для тех, кто знает, как его использовать. Хотя строгая лексическая прагма, а что насчет Moose?

package BigMooseUser;
use Moose;

package BabyMooseUser;
our @ISA = 'BigMooseUser';

package Foo;
my $b = BabyMooseUser->new;
print $b->meta->name;

Теперь, где BabyMooseUser получил конструктор (новый) из? Откуда у него мета-класс? Все это предоставляется из одного use Moose; в родительском классе (пространство имен). Так

Will the subclass inherit the use statements from the parent?

Ну, здесь, в нашем примере, если эффекты инструкции use должны добавлять методы, чем, конечно.

Этот предмет довольно глубокий, и это зависит от того, говорите ли вы о прагмах или о более неясных объектных рамках или процедурных модулях. Если вы хотите, чтобы пространство имен родителей влияло на ваши собственные в парадигме OO, см. namespace::autoclean.

Ответ 2

Вы спросили в комментарии о Test:: Most и о том, как он уменьшает шаблон. Посмотрите на его метод import. Он загружает модули в свое пространство имен, добавляя эти символы в @EXPORT, а затем повторно вызывающий другой import через goto, чтобы, наконец, получить их в вызывающее пространство имен. Это какая-то серьезная черная магия, которую Куртис продолжает там, хотя мне интересно, почему он просто не использовал что-то вроде import_to_level. Возможно, есть некоторые побочные эффекты, о которых я не думаю.


Я немного об этом говорю в Избегайте случайного создания методов из экспорта модулей в Эффективный Perler. Это в другом контексте, но это некоторые из тех же проблем.

Вот другой пример.

Если какой-либо другой модуль загружает модуль, у вас есть к нему доступ. Нехорошо зависеть от этого. Вот три отдельных файла:

Top.pm

use 5.010;

package Top;
use File::Spec;

sub announce { say "Hello from top!" }
1;

Bottom.pm

package Bottom;
use parent qw(Top);

sub catfiles { File::Spec->catfile( @_ ) }

1;

test.pl

use 5.010;

use Bottom;

say Bottom->catfiles( qw(foo bar baz) );

say File::Spec->catfile( qw( one two three ) );

Я загружаю только File:: Spec в Top.pm. Однако после загрузки я могу использовать его в любом месте в своей программе Perl. Результат показывает, что я смог "использовать" модуль в других файлах, хотя я только загрузил его в один:

Bottom/foo/bar/baz
one/two/three

Чтобы это сработало, часть кода, загружающего модуль, должна загружаться, прежде чем какая-либо другая часть кода попытается использовать этот модуль. Как я уже сказал, это плохая идея, чтобы зависеть от этого: вещи ломаются, если последовательность загрузки изменяется или модуль загрузки исчезает.

Если вы хотите импортировать символы, вы должны явно загрузить модуль, который хотите, пока вы находитесь в пакете, который хотите импортировать. Это так, что модуль экспорта определяет символы в этом пакете. Это не то, что зависит от объема.

Ответ 3

Для сокращения шаблонов у меня есть несколько стратегий: большинство моих классов Moose классы, которые заботятся о настройке OO а также дает мне строгие предупреждения. Если я хочу иметь функции, доступные во многих пакетах, я создам специальный проект MyProject::Util, который использует Sub-Exporter, чтобы предоставить мне с моими собственными функциями и с моим собственным интерфейсом. Это делает его более последовательным, и если я по какой-то причине решил изменить Дампер (например), мне не нужно менять много кода. Это также позволит вам группировать экспорт. Класс обычно выглядит примерно так:

package Foo;
use Moose;
use MyProject::Util qw( :parsing :logging );

use namespace::autoclean;

# class implementation goes here

1;

Если есть другие вещи, которые вы считаете шаблонами и хотите упростить их включение, это, конечно, зависит от того, что это за вещи.

Ответ 4

Прагматичный ответ на вашу проблему: либо используйте, либо посмотрите, как Modern::Perl делает это для соблюдения строгих правил и предупреждений.

Ответ 5

Вы можете получить окончательный ответ, просмотрев таблицы символов для каждого пакета:

# examine-symbol-tables.pl
use Bar;

%parent_names = map{$_ => 1} keys %Foo::;
%child_names = map{$_ => 1} keys %Bar::;

delete $parent_names{$_} && ($common_names{$_} = delete $child_names{$_}) foreach keys %child_names;

print "Common names in symbol tables:\n";
print "@{[keys %common_names]}\n\n";

print "Unique names in Bar symbol table:\n";
print "@{[keys %child_names]}\n\n";

print "Unique names in Foo symbol table:\n";
print "@{[keys %parent_names]}\n\n";

$ perl inherit.pl
Common names in symbol tables:
BEGIN

Unique names in Bar symbol table:
ISA isa import

Unique names in Foo symbol table:
Dumper new VERSION