Что именно делает Perl's Bless?

Насколько я понимаю, в Perl используется ключевое слово "bless" внутри метода "new" класса:

sub new {
    my $self = bless { };
    return $self;
}    

Но что именно «bless» делает с этой хеш-ссылкой?


person user47145    schedule 24.12.2008    source источник
comment
См. Bless My Referents за 1999 год. Выглядит довольно подробно. (В ручном вводе Perl, к сожалению, нечего сказать .)   -  person Jon Skeet    schedule 24.12.2008


Ответы (8)


Обычно bless связывает объект с классом.

package MyClass;
my $object = { };
bless $object, "MyClass";

Теперь, когда вы вызываете метод в $object, Perl знает, в каком пакете искать метод.

Если второй аргумент опущен, как в вашем примере, используется текущий пакет / класс.

Для ясности ваш пример можно записать следующим образом:

sub new { 
  my $class = shift; 
  my $self = { }; 
  bless $self, $class; 
} 

РЕДАКТИРОВАТЬ: см. kixx хороший ответ, чтобы получить более подробную информацию.

person Gordon Wilson    schedule 24.12.2008

bless связывает ссылку с пакетом.

Не имеет значения, на что идет ссылка, это может быть хэш (наиболее распространенный случай), массив (не так часто), скаляр (обычно это указывает на объект наизнанку), в регулярное выражение, подпрограмму или TYPEGLOB (см. книгу Объектно-ориентированный Perl: подробное руководство по концепциям и методам программирования от Дэмиана Конвея для полезных примеров) или даже ссылку на дескриптор файла или каталога (наименее распространенный случай).

Эффект bless-ing заключается в том, что он позволяет применять специальный синтаксис к благословенной ссылке.

Например, если благословенная ссылка хранится в $obj (связанном bless с пакетом «Класс»), то $obj->foo(@args) вызовет подпрограмму foo и передаст в качестве первого аргумента ссылку $obj, за которой следуют остальные аргументы (@args). Подпрограмма должна быть определена в пакете «Класс». Если в пакете «Класс» нет подпрограммы foo, будет выполнен поиск в списке других пакетов (взятых из массива @ISA в пакете «Класс») и будет вызвана первая найденная подпрограмма foo.

person kixx    schedule 24.12.2008
comment
Ваше первоначальное утверждение неверно. Да, bless принимает ссылку в качестве первого аргумента, но благословляется референтная переменная, а не сама ссылка. $ perl -le 'sub Somepackage :: foo {42}; % h = (); $ h = \% h; благослови $ h, Somepackage; $ j = \% h; print $ j- ›UNIVERSAL :: can (foo) -› () '42 - person converter42; 26.12.2008
comment
Kixx дает исчерпывающее объяснение. Не стоит возиться с подбором преобразователя по теоретическим мелочам. - person Blessed Geek; 26.07.2010
comment
@Blessed Geek, это не теоретические мелочи. Разница имеет практическое применение. - person ikegami; 30.06.2011
comment
Старая ссылка perlfoundation.org для вывернутого наизнанку объекта теперь, в лучшем случае, находится за стеной входа в систему. Ссылка Archive.org на оригинал здесь. - person ruffin; 12.01.2015
comment
Даже ссылка на archive.org больше не работает из-за robots.txt. - person harmic; 17.11.2016
comment
Возможно, это будет заменять неработающую ссылку, прокомментированную @harmic: perldoc.perl .org / perlobj.html # Inside-Out-objects - person Rhubbarb; 25.11.2016

Краткая версия: он отмечает этот хэш как прикрепленный к текущему пространству имен пакета (чтобы этот пакет предоставлял реализацию своего класса).

person chaos    schedule 24.12.2008

Эта функция сообщает объекту, на который ссылается REF, что теперь это объект в пакете CLASSNAME или текущий пакет, если CLASSNAME опущен. Рекомендуется использовать форму благословения с двумя аргументами.

Пример:

bless REF, CLASSNAME
bless REF

Возвращаемое значение

Эта функция возвращает ссылку на объект, благословленный в CLASSNAME.

Пример:

Ниже приведен пример кода, показывающий его основное использование, ссылка на объект создается путем благословения ссылки на класс пакета.

#!/usr/bin/perl

package Person;
sub new
{
    my $class = shift;
    my $self = {
        _firstName => shift,
        _lastName  => shift,
        _ssn       => shift,
    };
    # Print all the values just for clarification.
    print "First Name is $self->{_firstName}\n";
    print "Last Name is $self->{_lastName}\n";
    print "SSN is $self->{_ssn}\n";
    bless $self, $class;
    return $self;
}
person linuxtestside    schedule 03.06.2016

Я дам ответ здесь, так как те, что здесь, мне не совсем понравились.

Функция bless в Perl связывает любую ссылку со всеми функциями внутри пакета.

Зачем нам это нужно?

Начнем с выражения примера на JavaScript:

(() => {
    'use strict';

    class Animal {
        constructor(args) {
            this.name = args.name;
            this.sound = args.sound;
        }
    }

    /* [WRONG] (global scope corruption)
     * var animal = Animal({
     *     'name': 'Jeff',
     *     'sound': 'bark'
     * }); 
     * console.log(animal.name + ', ' + animal.sound); // seems good
     * console.log(window.name); // my window's name is Jeff?
     */

    // new is important!
    var animal = new Animal(
        'name': 'Jeff',   
        'sound': 'bark'
    );

    console.log(animal.name + ', ' + animal.sound); // still fine.
    console.log(window.name); // undefined
})();

Теперь давайте удалим конструкцию класса и обойдемся без нее:

(() => {
    'use strict';

    var Animal = function(args) {
        this.name = args.name;
        this.sound = args.sound;
        return this; // implicit context hashmap
    };

    // the "new" causes the Animal to be unbound from global context, and 
    // rebinds it to an empty hash map before being constructed. The state is
    // now bound to animal, not the global scope.
    var animal = new Animal({
        'name': 'Jeff',
        'sound': 'bark'
    });
    console.log(animal.sound);    
})();

Функция принимает хеш-таблицу неупорядоченных свойств (поскольку в 2016 году нет смысла писать свойства в определенном порядке на динамических языках) и возвращает хеш-таблицу с этими свойствами, или, если вы забыли ввести ключевое слово new, она вернет весь глобальный контекст (например, окно в браузере или глобальный в nodejs).

Perl не имеет «this», «new» или «class», но все же может иметь функцию, которая ведет себя аналогичным образом. У нас не будет ни конструктора, ни прототипа, но мы сможем создавать новых животных по желанию и изменять их индивидуальные свойства.

# self contained scope 
(sub {
    my $Animal = (sub {
        return {
            'name' => $_[0]{'name'},
            'sound' => $_[0]{'sound'}
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    print $animal->{sound};
})->();

Теперь у нас есть проблема: что, если мы хотим, чтобы животное издавало звуки само по себе, а не мы печатали его голос. То есть нам нужна функция performSound, которая печатает собственный звук животного.

Один из способов добиться этого - научить каждое отдельное Животное издавать звук. Это означает, что у каждой кошки есть собственная дублирующая функция для выполнения звука.

# self contained scope 
(sub {
    my $Animal = (sub {
        $name = $_[0]{'name'};
        $sound = $_[0]{'sound'};

        return {
            'name' => $name,
            'sound' => $sound,
            'performSound' => sub {
                print $sound . "\n";
            }
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    $animal->{'performSound'}();
})->();

Это плохо, потому что performSound помещается как совершенно новый объект функции каждый раз, когда создается животное. 10000 животных означает 10000 звуков. Мы хотим, чтобы у всех животных была одна функция performSound, которая ищет свой звук и печатает его.

(() => {
    'use strict';

    /* a function that creates an Animal constructor which can be used to create animals */
    var Animal = (() => {
        /* function is important, as fat arrow does not have "this" and will not be bound to Animal. */
        var InnerAnimal = function(args) {
            this.name = args.name;
            this.sound = args.sound;
        };
        /* defined once and all animals use the same single function call */
        InnerAnimal.prototype.performSound = function() {
            console.log(this.name);
        };

        return InnerAnimal;
    })();

    /* we're gonna create an animal with arguments in different order
       because we want to be edgy. */
    var animal = new Animal({
        'sound': 'bark',
        'name': 'Jeff'
    });
    animal.performSound(); // Jeff
})();

На этом параллель с Perl вроде бы заканчивается.

Оператор new в JavaScript не является обязательным, без него "this" внутри методов объекта повреждает глобальную область видимости:

(() => {
    // 'use strict'; // uncommenting this prevents corruption and raises an error instead.

    var Person = function() {
        this.name = "Sam";
    };
//    var wrong = Person(); // oops! we have overwritten window.name or global.main.
//    console.log(window.name); // my window's name is Sam?
    var correct = new Person; // person's name is actually stored in the person now.

})();

Мы хотим иметь одну функцию для каждого Animal, которая будет искать собственный звук этого животного, а не жестко кодировать его при создании.

Благословение позволяет нам использовать пакет в качестве прототипа объектов. Таким образом, объект знает о «пакете», на который он «ссылается», и, в свою очередь, может заставить функции в пакете «обращаться» к конкретным экземплярам, ​​которые были созданы из конструктора этого «объекта пакета»:

package Animal;
sub new {
    my $packageRef = $_[0];
    my $name = $_[1]->{'name'};
    my $sound = $_[1]->{'sound'};

    my $this = {
        'name' => $name,
        'sound' => $sound
    };   

    bless($this, $packageRef);
    return $this;
}

# all animals use the same performSound to look up their sound.
sub performSound {
    my $this = shift;
    my $sound = $this->{'sound'};
    print $sound . "\n";
}

package main;
my $animal = Animal->new({
    'name' => 'Cat',
    'sound' => 'meow'
});
$animal->performSound();

Сводка / TL; DR:

В Perl нет «этого», «класса» или «нового». благословение объекта на пакет дает этому объекту ссылку на пакет, и когда он вызывает функции в пакете, их аргументы будут смещены на 1 слот, а первый аргумент ($ _ [0] или shift) будет эквивалентен javascript "это". В свою очередь, вы можете имитировать модель прототипа JavaScript.

К сожалению, это делает невозможным (насколько я понимаю) создание «новых классов» во время выполнения, поскольку вам нужно, чтобы каждый «класс» имел свой собственный пакет, тогда как в javascript вам не нужны пакеты вообще, как ключевое слово «new» составляет анонимную хэш-карту для использования в качестве пакета во время выполнения, в которую вы можете добавлять новые функции и удалять функции на лету.

Есть некоторые библиотеки Perl, которые создают свои собственные способы преодоления этого ограничения в выразительности, например Moose.

Почему возникла путаница?:

Из-за пакетов. Наша интуиция подсказывает нам привязать объект к хэш-карте, содержащей его прототип. Это позволяет нам создавать «пакеты» во время выполнения, как это делает JavaScript. Perl не обладает такой гибкостью (по крайней мере, не встроен, вы должны изобрести его или получить из других модулей), и, в свою очередь, ваша выразительность во время выполнения затруднена. Назвать это «благословением» тоже не очень помогает.

Что мы хотим сделать:

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

Вот наивная попытка сделать это: проблема в том, что «call» не знает, «что вызвало это», поэтому это также может быть универсальная функция perl «objectInvokeMethod (object, method)», которая проверяет, есть ли у объекта метод , или его прототип имеет это, или его прототип есть, пока он не достигнет конца и не найдет его или нет (прототипное наследование). В Perl для этого есть прекрасная магия eval, но я оставлю это на то, что я могу попробовать сделать позже.

В любом случае вот идея:

(sub {

    my $Animal = (sub {
        my $AnimalPrototype = {
            'performSound' => sub {
                return $_[0]->{'sound'};
            }
        };

        my $call = sub {
            my $this = $_[0];
            my $proc = $_[1];

            if (exists $this->{$proc}) {
                return $this->{$proc}->();
            } else {
                return $this->{prototype}->{$proc}->($this, $proc);
            }
        };

        return sub {
            my $name = $_[0]->{name};
            my $sound = $_[0]->{sound};

            my $this = { 
                'this' => $this,
                'name' => $name,
                'sound' => $sound,
                'prototype' => $AnimalPrototype,
                'call' => $call                
            };
        };
    })->();

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound'=> 'bark'
    });
    print($animal->{call}($animal, 'performSound'));
})->();

В любом случае, надеюсь, кто-нибудь сочтет этот пост полезным.

person Dmitry    schedule 26.08.2016
comment
Во время выполнения возможно создание новых классов. my $o = bless {}, $anything; благословит объект в $anything класс. Точно так же {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++}; создаст метод с именем somesub в классе, указанном в $anything. Все это возможно во время выполнения. Однако возможность использования в повседневном коде не является хорошей практикой. Но это полезно при создании систем наложения объектов, таких как Moose или Moo. - person DavidO; 23.10.2019
comment
интересно, так что вы говорите, что я могу благословить референт в класс, имя которого определяется во время выполнения. Выглядит интересно и аннулирует мою unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime претензию. Я предполагаю, что моя проблема в конечном итоге сводилась к тому, что это было значительно менее интуитивно понятным для управления / анализа системы пакетов во время выполнения, но до сих пор мне не удалось показать то, что она по своей сути не может сделать. Система пакетов, похоже, поддерживает все инструменты, необходимые для добавления / удаления / проверки / изменения себя во время выполнения. - person Dmitry; 09.11.2019
comment
Это верно; вы можете программно управлять таблицей символов Perl и, следовательно, управлять пакетами Perl и членами пакета во время выполнения, даже не объявив где-либо пакет Foo. Проверка и изменение таблицы символов во время выполнения, семантика AUTOLOAD, атрибуты подпрограмм, привязка переменных к классам ... Есть много способов проникнуть в суть. Некоторые из них полезны для автоматического создания API, инструментов проверки, API автодокументирования; мы не можем предсказать все варианты использования. Возможным исходом такой уловки также является простреление себе стопы. - person DavidO; 09.11.2019

Наряду с рядом хороших ответов, что конкретно отличает ссылку bless-ed, так это то, что SV для нее подбирает дополнительные FLAGS (OBJECT) и STASH

perl -MDevel::Peek -wE'
    package Pack  { sub func { return { a=>1 } } }; 
    package Class { sub new  { return bless { A=>10 } } }; 
    $vp  = Pack::func(); print Dump $vp;   say"---"; 
    $obj = Class->new;   print Dump $obj'

Отпечатки с одними и теми же (и не имеющими отношения к этому) частями подавлены

SV = IV(0x12d5530) at 0x12d5540
  REFCNT = 1
  FLAGS = (ROK)
  RV = 0x12a5a68
  SV = PVHV(0x12ab980) at 0x12a5a68
    REFCNT = 1
    FLAGS = (SHAREKEYS)
    ...
      SV = IV(0x12a5ce0) at 0x12a5cf0
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 1
---
SV = IV(0x12cb8b8) at 0x12cb8c8
  REFCNT = 1
  FLAGS = (PADMY,ROK)
  RV = 0x12c26b0
  SV = PVHV(0x12aba00) at 0x12c26b0
    REFCNT = 1
    FLAGS = (OBJECT,SHAREKEYS)
    STASH = 0x12d5300   "Class"
    ...
      SV = IV(0x12c26b8) at 0x12c26c8
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 10

При этом известно, что 1) это объект 2) какому пакету он принадлежит, и это информирует о его использовании.

Например, когда встречается разыменование этой переменной ($obj->name), в пакете (или иерархии) ищется подпрограмма с таким именем, объект передается в качестве первого аргумента и т. Д.

person zdim    schedule 21.11.2019

Следуя этой мысли, я руководствуюсь разработкой объектно-ориентированного Perl.

Благослови связывать любую ссылку на структуру данных с классом. Учитывая, как Perl создает структуру наследования (в виде дерева), легко использовать преимущества объектной модели для создания объектов для композиции.

Эту ассоциацию мы назвали объектом, чтобы при разработке всегда иметь в виду, что внутреннее состояние объекта и поведение класса разделены. И вы можете благословить / разрешить любой ссылке на данные использовать любое поведение пакета / класса. С пакетом можно понять «эмоциональное» состояние объекта.

person Steven Koch    schedule 05.05.2015
comment
Здесь же рассказывается о том, как Perl работает с пространствами имен пакетов и как работает с состояниями, зарегистрированными в вашем пространстве имен. Поскольку существуют такие прагмы, как use namespace :: clean. Но постарайтесь, чтобы все было проще. - person Steven Koch; 05.05.2015

Например, если вы можете быть уверены, что любой объект Bug будет благословенным хешем, вы можете (наконец!) Заполнить недостающий код в методе Bug :: print_me:

 package Bug;
 sub print_me
 {
     my ($self) = @_;
     print "ID: $self->{id}\n";
     print "$self->{descr}\n";
     print "(Note: problem is fatal)\n" if $self->{type} eq "fatal";
 }

Теперь, когда метод print_me вызывается через ссылку на любой хэш, добавленный в класс Bug, переменная $ self извлекает ссылку, которая была передана в качестве первого аргумента, а затем операторы печати обращаются к различным записям благословенного хеша.

person Bimlesh Sharma    schedule 16.06.2011
comment
@darch Из какого источника этот ответ был плагиатом? - person Anderson Green; 19.09.2013