Linux: Kniha kouzel, 2.15 (1. března 2025)
Veškerá moc příkazové řádky/příkazového řádku přehledně, pro začátečníky i pokročilé

15. Perl: moduly a objekty

Vývoj vanilkové příchuti Linuxu: Knihy kouzel byl 1. března 2025 ukončen. Tento text je zachován jako historický, ale chyby již nejsou opravovány. Odnože projektu pod kompatibilní licencí jsou vítány.

1. Úvod

Tato kapitola z programovacího jazyka Perl pokrývá dělení kódu do modulů, práci s nimi a objektově orientované programování.

Objektově orientované programování se v Perlu realizuje tak, že obyčejnému objektu (poli, asociativnímu poli či skaláru) je přes ukazatel přiřazen modul (třída), který obsahuje metody pro práci s ním. Když pak metodu zavoláte pomocí objektově orientovaného operátoru „->“, Perl ji automaticky najde v modulu, který je k objektu přiřazen (nebo v některém z jeho „rodičů“, jak je modul deklaruje), a při volání funkci vsune použitý ukazatel jako dodatečný parametr na začátku pole @ARG.

2. Definice

  • Symbol je identifikátor funkce nebo konstanty nebo identifikátor proměnné včetně rozlišovacího symbolu „$“, „@“ nebo „%“.
  • Modul (module) je pojmenovaný kontext pro umísťování funkcí a proměnných, oddělený od hlavního skriptu do samostatného souboru s příponou „.pm“ („Perl module“).
  • Hlavní skript je zdrojový soubor, který byl přímo spuštěn interpretem Perlu. Na hlavní skript se při jeho spuštění nabalí moduly odkazované přímo či nepřímo příkazy „use“ a „require“.
  • Název modulu je jednoznačné označení modulu, které se skládá z posloupnosti jednoho či více identifikátorů oddělených dvojí dvojtečkou („::“), např. „Digest::MD5“ nebo „English“. V adresářové cestě se pak dvojtečka nahradí za lomítko („/“) a na konec názvu se doplní „.pm“. Na rozdíl od jiných jazyků se název modulu v Perlu nezkracuje (vždy se uvádí celý) a mezi moduly není žádná automatická hierarchie.
  • Importovat symbol znamená zpřístupnit symbol z jiného modulu tak, jako by byl definován i v tomto modulu. To umožňuje daný symbol používat bez kvalifikace názvem modulu, případně ho dál exportovat. Importovat lze jen ty symboly, které jsou daným modulem exportovány.
  • Exportovat symbol znamená umožnit symbol ostatním modulům z tohoto modulu importovat.
  • Objekt je místo paměti, které má svůj datový typ a hodnotu, je to tedy skalár, pole nebo asociativní pole. Objekty se dělí na obyčejné objektyobjekty tříd (jimž byla přiřazena třída).

2/1 Objektově orientované programování

  • Jako metoda se označuje funkce použitá objektově orientovaným způsobem (volaná objektově orientovaným operátorem „->“) nebo k tomu uzpůsobená.
  • Třída (class) je modul obsahující metody (alespoň jednu). Kromě metod může obsahovat i funkce, které nejsou objektově orientované.
  • Rodič třídy je modul (třída) uvedený/á příkazem „use parent“. Není-li požadovaná metoda nalezena v třídě, která je objektu přímo přiřazena, bude ji Perl hledat v jejích rodičích.

3. Zaklínadla

3/1 Obvyklá struktura zdrojových souborů

@obvyklá struktura hlavního skriptu (*.pl)#1
[komentář]
[příkazy use]
[definice konstant]
[deklarace prototypů funkcí]
definice proměnných a hlavní kód programu
[definice funkcí]
@obvyklá struktura souboru modulu (*.pm)#2
[komentář]
use utf8;
package Název::Modulu;
use strict; use warnings; use v5.26.0;
use LinuxKnihaKouzel; use English;
use Exporter("import");
[další příkazy use]
[definice konstant]
[use parent("Rodič"[, DalšíRodič]);]
[use fields("seznam", "datových", "složek");]
[our @EXPORT = ("seznam", "symbolů", "pro", "výchozí", "import");]
our @EXPORT_OK = ("seznam", "ostatních", "exportovaných", "symb.");
[definice proměnných a inicializační kód]
definice funkcí
1;

3/2 Vyhledávání a připojování modulů

@připojit modul a importovat výchozí symboly/konkrétní symboly/neimportovat žádné symboly#1 (1)
use Název::Modulu;
use Název::Modulu("seznam", "symbolů", "k", "importu");
use Název::Modulu();
@hledat moduly i v zadaném adresáři#2 (2)
use lib("cesta"[, "další/cesta"]);
@hledat moduly i v adresáři hlavního skriptu/jeho podadresáři#3
use lib((MAIN_SCRIPT_DIR));
use lib((MAIN_SCRIPT_DIR) . "/relativní/cesta");
@hledat moduly i ve stejném adresáři, kde se nachází daný zdrojový soubor#4
use lib(((__FILE__ =~ s/^[^\/]*$/.\/x/r) =~ s/\/[^\/]*$//r));
@podmíněně nahrát modul#5 (3)
BEGIN {if (podmínka) {
require Název::Modulu;
[Název::Modulu->import(["seznam", "symbolů"]);]
}}
@načíst zdrojový soubor jako modul, bez importu symbolů#6 (4)
BEGIN {require("[.]/cesta/k/souboru")}
@je modul dostupný?#7 (5)
eval("require Název::Modulu; 1")

3/3 Přístup do modulů

@přístup k proměnným/funkcím v jiném modulu#1 (6)
$@%Název::Modulu::identifikátor
Název::Modulu::identifikátor([parametry])
@přístup z modulu k proměnným/funkcím v hlavním skriptu#2
$@%main::identifikátor
main::identifikátor([parametry])

3/4 Třídy a metody

@obvyklá definice metody#1
sub název_metody [: lvalue]
{
my [Název::Modulu] $self = shift(@ARG);
tělo metody
}
@zavolat metodu přes ukazatel na objekt (obecně/příklad)#2 (7)
ukazatel->název_metody(seznam, parametrů)
my $hodnota = $objekt->jeho_metoda(1, 2, "");
@zavolat metodu přes třídu (typické pro volání konstruktorů)(obecně/příklad)#3 (8)
Název::Modulu->název_metody(seznam, parametrů)
my Můj::Test $můjtest = Můj::Test->new();
@zavolat metodu cizího modulu přes objekt (alternativy)#4 (9)
ukazatel->Název::Modulu::název_metody(seznam, parametrů)
Název::Modulu::název_metody(ukazatel[, seznam, parametrů])
@zavolat funkci modulu neobjektově#5 (10)
Název::Modulu::název_metody(seznam, parametrů)
@přiřadit objektu třídu#6 (11)
bless(ukazatel, Název::Modulu)
@obvyklý tvar konstruktoru (kromě tříd s pevnou strukturou)#7 (12)
sub new
{
my [Název::Modulu] $self = bless({}, $ARG[0]);
tělo konstruktoru
return $self;
}
@obvyklá struktura destruktoru#8
sub DESTROY
{
[local ($NR, $ERRNO, $EVAL_ERROR, $CHILD_ERROR, $EXTENDED_OS_ERROR);
my [Název::Modulu] $self = shift(@ARG);
příkazy]
}

3/5 Třída s pevnou strukturou (deklarace)

@deklarovat seznam dovolených klíčů#1
V záhlaví modulu uvést:
use fields("seznam", "dovolených", "klíčů");
@obvyklý tvar konstruktoru (liší se od obecné třídy)#2
sub new
{
my Název::Modulu $self = fields::new($ARG[0]);
tělo konstruktoru
return $self;
}
@vytvořit prázdný objekt třídy s pevnou strukturou#3 (13)
^use fields();
fields::new("Název::Třídy")
@vytvořit asociativní pole s dynamicky danou pevnou strukturou#4
^use Hash::Util;
[$ukazatel =] Hash::Util::lock_ref_keys({}, seznam, dovolených, klíčů);
@vytvořit typované asociativní pole s pevnou strukturou dynamicky#5
^use Hash::Util;
[$ukazatel =] Hash::Util::lock_ref_keys(bless({}, "Název::Modulu"), seznam, dovolených, klíčů);

3/6 Specializované proměnné

@deklarovat specializovanou proměnnou#1 (14)
my|state|our Název::Modulu $identifikátor [= inicializace];

3/7 Zkoumání modulů a objektů

@je cíli ukazatele přiřazen modul (třída)?#1 (15)
^use Scalar::Util;
defined(Scalar::Util::blessed(ukazatel))
@která třída je přiřazena cíli ukazatele?#2 (16)
^use Scalar::Util;
Scalar::Util::blessed(ukazatel)
@je možno nad daným objektem zavolat metodu určitého názvu?#3
ukazatel->can("název_metody")
@je odkazovanému objektu přiřazen určitý modul nebo jeho potomek?#4
ukazatel->isa("Název::Modulu")

3/8 Zkoumání tříd s pevnou strukturou a jejich objektů

@ukazuje ukazatel na třídu s pevnou strukturou?#1
^use Hash::Util;
Hash::Util::hashref_locked(ukazatel)
@získat pole dovolených klíčů třídy s pevnou strukturou (z ukazatele/z třídy)#2
^require fields;
^use Hash::Util;
Hash::Util::legal_ref_keys(ukazatel)
Hash::Util::legal_ref_keys(fields::new("Název::Modulu"))
@získat asociativní pole dovolených klíčů#3
^use Hash::Util;
[%asocPole =] (map {($ARG, exists ukazatel->{$ARG} ? 1 : 0)} Hash::Util::legal_ref_keys(ukazatel))
@je klíč dovolený?#4
^use Hash::Util;
alength(array(grep {$ARG eq "klíč"} Hash::Util::legal_ref_keys(ukazatel)))

4. Instalace na Ubuntu

Všechny použité nástroje jsou základní součástí Ubuntu, přítomnou i v minimální instalaci.

5. Ukázka

5/1 Hlavní skript (ukázka.pl)

# Jednoduchý testovací skript
use lib((MAIN_SCRIPT_DIR));
use Ukázka;
use constant TEXT => "Hello, world.";
my $výsledek = Ukázka::vypsat_text(TEXT);
printf("Návratová hodnota: %s\n", $výsledek);

5/2 Modul (Ukázka.pm)

use utf8;
package Ukázka;
use strict; use warnings; use v5.26.0;
use LinuxKnihaKouzel; use English;
use Exporter("import");
our @EXPORT_OK = ("vypsat_text");
sub vypsat_text
{
return 0 + printf("%s\n", $ARG[0]);
}
1;

6. Tipy a zkušenosti

  • Abyste se vyhnul/a problémům s názvy modulů, každá část názvu musí začínat velkým písmenem a obsahovat alespoň jedno malé písmeno. Např. názvy „Č7á“ nebo „Ay“ jsou vyhovující, názvy „xAb“ či „Z9“ ne.
  • Někteří uživatelé příkazem „use lib(".");“ nastavují, aby Perl moduly vyhledával i v aktuálním adresáři. To však není vůbec dobrý nápad, protože aktuální adresář při spuštění skriptu může být zcela nečekaný. Proto doporučuji to nedělat a adresář pro vyhledávání modulů předávat jinak, nejlépe parametrem Perlu -I.
  • V Perlu je zvykem nevyužívat výchozí import (proměnnou „@EXPORT“), pokud to nezbytně nepotřebujete. Nechte na uživateli, aby si vybral, které symboly bude chtít importovat.
  • Jmenné prostory („package“) lze používat i v rámci jednoho souboru, tato kapitola se ale takovému použití záměrně vyhýbá, protože jsou s ním spojeny problémy — některé příkazy „use“ totiž účinkují na zdrojový soubor (a tedy nebudou účinkovat v tomtéž jmenném prostoru v jiných zdrojových souborech), zatímco jiné účinkují na jmenný prostor (a tedy zase nebudou účinkovat v tomtéž souboru v jiných jmenných prostorech), některé možná kombinují oba účinky.
  • Některé moduly používají seznam importovaných symbolů k jiným účelům než jako seznam symbolů, např. k předání nastavení.

7. Další zdroje informací

8. Zákulisí kapitoly

V této verzi kapitoly chybí:

  • export/import symbolů po skupinách

Tato kapitola záměrně nepokrývá:

  • přetěžování operátorů (jsou s ním komplikované problémy)
1 Perl prohledá standardní adresáře a adresáře zadané příkazem „use lib“ (resp. parametrem „-I“). Z každého takového adresáře hledá cestu sestavenou z komponent názvu modulu, kde poslední komponentu doplní o příponu „.pm“. Např. příkaz „use Digest::MD5“ bude hledat soubor „Digest/MD5.pm“. Nalezený modul se načte a z jeho jmenného prostoru se importují požadované symboly. Toto je obvyklý způsob používání modulů v Perlu.
2 Cesta může být absolutní nebo relativní. Relativní cesta se však vyhodnocuje relativně vůči aktuálnímu adresáři, což nemusí být adresář, ve kterém se nachází běžící skript. Nově přidané cesty se prohledávají před dříve přidanými.
3 Vynechání volání „import“ je ekvivalentní příkazu „use“ s prázdným seznamem (neimportuje nic). Volání „import“ s prázdným seznamem je ekvivalentní příkazu „use“ bez seznamu (importuje výchozí symboly).
4 Uvedená cesta musí začínat „/“ (v případě absolutní cesty), nebo „./“ v případě relativní cesty. Soubor nemusí mít příponu „.pm“.
5 Tento test je možno provést i v době překladu (např. v definici konstanty nebo v bloku BEGIN)
6 Tuto syntaxi doporučuji používat jen u málo používaných symbolů; symboly, které používáte ve zdrojovém kódu často, doporučuji v příkazu „use“ importovat.
7 Při tomto způsobu volání dostane metoda jako dodatečný první parametr zadaný ukazatel.
8 Při tomto způsobu volání dostane metoda jako dodatečný první parametr řetězec obsahující název modulu.
9 Tato syntaxe pouze obchází vyhledávání metody; volaná metoda dostane dodatečný první parametr jako normálně.
10 Při tomto způsobu volání dostane funkce jen předané parametry, dodatečný první parametr nebude před vsunut.
11 Funkce bless() vrací předaný ukazatel beze změny, ale až poté, co odkazovanému objektu přiřadila modul. Pozor! Jednou vytvořené přiřazení u daného objektu nelze zrušit ani přepsat, nepřenáší se však při kopírování, takže stačí přiřadit objekt (ne ukazatel na něj) do nové proměnné a získáte „čistou“ kopii, bez přiřazeného modulu.
12 Název metody „new“ není závazný. Nový objekt může být voláním funkce „bless()“ vytvořen kdekoliv v programu.
13 Příkaz „use fields();“ je potřeba jen v případě, že v záhlaví modulu není uveden jiný příkaz „use fields“.
14 Specializace nepředstavuje pro proměnnou žádné zásadní omezení, stále může obsahovat jakýkoliv skalár. Pokud je ovšem specializovaná na třídu s pevnou strukturou, použití neplatného konstantního klíče způsobí chybu již před spuštěním programu, což se vyplatí (je to jedna z mála „statický“ kontrol v Perlu).
15 Poznámka: některým vestavěným typům Perlu je také přiřazena třída; např. regulární výrazy (vzniklé operátorem „qr//“) mají přiřazenu třídu „Regexp“.
16 Pokud předaný skalár není ukazatel na objekt s přiřazenou třídou, tato funkce vrátí undef.
[BY-SA]

Veškerý obsah této stránky (text, obrázky, zdrojový kód) je možno upravovat a šířit pod podmínkami licence Creative Commons Attribution-ShareAlike 4.0 International. Upozorňuji, že uvedená licence vyžaduje uvedení seznamu autorů, licence a zdroje a poskytnutí stejné či kompatibilní licence k provedeným změnám, jsou-li nějaké. Příslušné údaje jsou dostupné na stránce „Přehled autorů“. Šíření obsahu bez těchto údajů nebo šíření upravené verze bez poskytnutí adekvátní licence k provedeným úpravám je pravděpodobně porušení licenčních podmínek a může být postihováno. Poskytování zdrojového kódu při šíření není touto licencí vyžadováno.

Pro nové verze, další informace, aktuální zdrojový kód a možnost se zapojit do projektu „Linux: Kniha kouzel“ navštivte jeho repozitář na GitHubu.