2010-08-11 8 views
10

Digamos que tiene una clase Perl matriz en un archivo:¿Las subclases de Perl heredan módulos y pragmas importados?

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

use Data::Dumper; 

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

y una subclase en un archivo diferente:

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

¿La subclase heredará las declaraciones de uso de los padres? Sé que el método nuevo será heredado.

Básicamente estoy tratando de reducir la cantidad de texto estándar en mi código y no puedo encontrar una respuesta clara a esta pregunta.

+0

Pregunto acerca de esta funcionalidad porque Test :: Most y Moose afirman hacerlo, pero no he descubierto cómo lo hacen. – chotchki

+3

Mire el método de importación() en Test :: Most. Así es como lo hace. Carga manualmente todos esos módulos repetitivos y los exporta a dos niveles. –

+0

¡Gracias por toda la ayuda a todos! El sub de importación fue la clave. (Puedo salvarme el dolor en el futuro y simplemente ir a Moose). – chotchki

Respuesta

4

Ah, buena pregunta!

Will the subclass inherit the use statements from the parent? 

Bueno, esto depende de lo que quiere decir heredar. No haré ninguna suposición hasta el final, pero la respuesta es quizás. Usted ve, perl mezcla las ideas de Classes, y Namespaces - un package es un término que puede describir cualquiera de ellos. Ahora el problema es la declaración use todo lo que hace es forzar la inclusión de un paquete, y llamar a los objetivos import() sub. Esto significa que, en esencia, tiene un control ilimitado sobre su paquete y, a través de eso, su clase.

Ahora, este compuesto con todos los métodos en Perl no ser más que subs que tienen $self como primer argumento, por convención, y uno se queda con perl5. Esto tiene un enorme potencial para aquellos que saben cómo usarlo. Si bien strict es un pragma léxico, ¿qué pasa con Moose?

package BigMooseUser; 
use Moose; 

package BabyMooseUser; 
our @ISA = 'BigMooseUser'; 

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

Ahora, ¿de dónde BabyMooseUser obtener el constructor (nuevo) de la? ¿De dónde salió la metaclase? Todo esto se proporciona desde un solo use Moose; en la clase principal (espacio de nombres). Así

Will the subclass inherit the use statements from the parent? 

Bueno, aquí, en nuestro ejemplo, si los efectos de la declaración uso son agregar métodos, que sin duda.

Este tema es bastante profundo, y depende de si se trata de pragmas, marcos de objetos más oscuros o módulos de procedimientos. Si desea mitigar el espacio de nombres de un padre para que no afecte al propio en el paradigma OO, vea namespace::autoclean.

+1

En realidad, el uso no obliga a incluir un paquete.Busca un archivo basado en el nombre del paquete que desea usar(). Ese archivo no tiene que cargar ese paquete. Por convención, nombramos nuestros archivos con el nombre del paquete que contiene, pero eso no es algo que le importe a use(). Por ejemplo, Module :: Build :: Version está realmente allí para cargar la versión. Las cargas de uso de archivos() pueden incluso tener más de un paquete. –

+2

BabyMooseUser obtiene su nuevo() porque declara una relación de herencia. La única llamada de Moose no lo configura para usted. Y, autoclean solo tratará con símbolos importados. No va a restringir el acceso a cualquier otra cosa que desee obtener. –

+2

Es un poco raro decir que los métodos son subs que * toman * $ self como primer argumento. Los métodos son subs, pero terminas con su referente como primer argumento sin especificarlo explícitamente como argumento. Ese referente podría ser el nombre de la clase (una cadena) o una referencia a un objeto, dependiendo de lo que esté a la izquierda de -> (ignorando las llamadas indirectas). Tampoco es tan simple como buscar literales o variables en el lado izquierdo. :) –

1

Usted puede obtener una respuesta definitiva mediante el examen de las tablas de símbolos para cada paquete:

# 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 
4

Para la reducción estándar, tengo un par de estrategias: la mayoría de mis clases son Moose, que se encarga de la configuración de OO y también me da estrictas y advertencias. Si quiero tener funciones disponibles en muchos paquetes, crearé un módulo específico de proyecto MyProject::Util que usa Sub-Exporter para proporcionar mis propias funciones y mi propia interfaz. Esto lo hace más consistente, y si decido cambiar el Dumper (por ejemplo) más tarde por cualquier razón, no tengo que cambiar muchos códigos. Eso también te permitirá agrupar las exportaciones.Una clase entonces por lo general se ve algo como esto:

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

use namespace::autoclean; 

# class implementation goes here 

1; 

Si hay otras cosas que se consideran como repetitivo y desea hacer más simple para incluir, que por supuesto depende de cuáles son esas cosas.

7

Ha preguntado en un comentario acerca de Test::Most y cómo se reduce la repetición. Mira su método import. Está cargando los módulos en su espacio de nombres, agregando esos símbolos al @EXPORT, luego volviendo a llamar a otro import a través de un goto para finalmente colocarlos en el espacio de nombres de la llamada. Es una magia negra seria que Curtis está pasando allí, aunque me pregunto por qué simplemente no usó algo como import_to_level. Tal vez haya algunos efectos secundarios en los que no estoy pensando.


que hablar un poco acerca de este tipo de cosas en Avoid accidently creating methods from module exports en The Effective Perler. Está en un contexto diferente, pero son algunos de los mismos problemas.

Aquí hay un ejemplo diferente.

Si algún otro módulo carga un módulo, tiene acceso a él. Sin embargo, no es bueno depender de eso. Aquí hay tres archivos separados:

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)); 

Solo cargo File :: Spec en Top.pm. Sin embargo, una vez cargado, puedo usarlo en cualquier parte de mi programa Perl. El resultado muestra que yo era capaz de "utilizar" el módulo en otros archivos pesar de que sólo cargué en uno:

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

Para que esto funcione, la parte del código que carga el módulo tiene que cargarse antes cualquier otra parte del código intenta usar ese módulo. Como dije, es una mala idea depender de esto: las cosas se rompen si la secuencia de carga cambia o el módulo de carga desaparece.

Si desea importar símbolos, sin embargo, tiene que cargar explícitamente el módulo que desee mientras se encuentre en el paquete que desea importar. Eso es solo para que el módulo de exportación defina los símbolos en ese paquete. No es algo que dependa del alcance.

2

Una respuesta pragmática a su problema: O utilice, o vea cómo Modern::Perl lo hace para hacer cumplir estrictas y advertencias.

+0

Moderno :: Perl tira en estricto y advertencias, que son módulos que futz con $^H. La mayoría de los módulos no van a querer hacer eso. –

Cuestiones relacionadas