【问题标题】:Is it possible to get all valid methods for a particular Perl class?是否可以获得特定 Perl 类的所有有效方法?
【发布时间】:2013-08-25 18:18:33
【问题描述】:

是否可以获得特定 Perl 类的所有有效方法?

我正在尝试操作一个类的符号表并获取它的所有方法。我发现我可以通过$obj->can($method) 将子例程与非子例程分开,但这并不完全符合我的想法。

以下返回:

subroutine, Property, croak, Group, confess, carp, File

但是,subroutine 不是方法,(只是一个子程序),croakconfesscarp 都被导入到我的包中。

我真正想打印的是:

Property,Group, File

但我会接受:

subroutine, Property,Group, File

下面是我的程序:

#! /usr/bin/env perl

use strict;
use warnings;
use feature qw(say);

my $sections = Section_group->new;
say join ", ", $sections->Sections;

package Section_group;
use Carp;

sub new     {
    return bless {}, shift;
}

sub Add {
    my $self                = shift;
    my $section             = shift;
}

sub Sections {
    my $self                = shift;

    my @sections;
    for my $symbol ( keys %Section_group:: ) {
        next if $symbol eq "new";   # This is a constructor
        next if $symbol eq "Add";   # Not interested in this method
        next if $symbol eq "Sections";      # This is it's own method
        push @sections, $symbol if $self->can($symbol);
    }

    return wantarray ? @sections : \@sections;
}

sub subroutine {
    my $param1              = shift;
    my $param2              = shift;
}

sub Group {
    my $self                = shift;
    my $section             = shift;
}

sub File {
    my $self                = shift;
    my $section             = shift;
}

sub Property {
    my $self                = shift;
    my $section             = shift;
}

【问题讨论】:

    标签: perl class oop methods


    【解决方案1】:

    这是相当微不足道的。我们只想保留那些最初在我们的包中定义的子名称。每个 CV(代码值)都有一个指向定义它的包的指针。感谢B,我们可以检查一下:

    use B ();
    
    ...
    
    if (my $coderef = $self->can($symbol)) {
      my $cv = B::svref_2object $coderef;
      push @sections, $symbol if $cv->STASH->NAME eq __PACKAGE__;
    }
    
    # Output as wanted
    

    也就是说,我们使用svref_2object 执行自省。这将返回一个表示内部 perl 数据结构的 Perl 对象。

    如果我们查看 coderef,我们会得到一个 B::CV object,它代表内部的 CV。 CV 中的 STASH 字段指向定义它的 Stash。如您所知,Stash 只是一个特殊的哈希(内部表示为HV),因此$cv->STASH 返回一个B::HV。如果 HV 是 Stash,而不是常规哈希,HVNAME 字段包含 Stash 的完全限定包名称。

    现在我们有了我们需要的所有信息,并且可以将想要的包名称与 coderef 的存储名称进行比较。

    当然,这是简化的,您将希望通过@ISA 进行一般类的递归。


    没有人喜欢被污染的命名空间。值得庆幸的是,有些模块可以从 Stash 中删除外来符号,例如namespace::clean。当您调用的所有子程序的 CV 在编译时已知时,这没有问题。

    【讨论】:

    • 再一次,有时人们确实会从另一个包中导入方法(例如 use Exporter 'import'; 而不是 @ISA = qw(Exporter);)。
    • @cjm 当然,但我猜这不会经常发生,甚至可能是一种反模式。希望大多数人已经从 OOP(它解决了许多命名空间问题)中分离出过程编程(带有导入)。没有办法知道导入的 sub 是否是一种方法,所以这必须足够好。 (等等,也许我们可以访问:method 属性……)
    • 这很简单。琐碎的?你在周末制造超光速粒子发电机? svref_2object 上的 Perldoc:获取对任何 Perl 值的引用,并将引用的值转换为适当的 B::OP 派生类或 B::SV 派生类中的对象。 我不知道它在说什么。但是,它确实有效。我想现在是我深入研究以前未深入研究并进一步改进我的 Perl 游戏的时候了。要么,要么学习 Python。
    • @DavidW。是的,B 文档很糟糕。目标受众是已经熟悉 Perl Core 基础知识的人。我用illguts 的链接扩展了解释,之所以这样命名是因为它有插图(带有大量启发性的指针图),并涵盖了perlguts 手册页的主题。为什么是的,我确实在一个周末项目中学到了这一点,尽管我只是想用 B::Generate 编写一个 Hello World(在 Perl 中创建 perl 操作码)。 “琐碎”只是意味着我们不必编写任何 C 代码:有一个模块。
    • @amon 感谢您的链接。当 illguts 说自 5.10 以来 Perl 内部被重构了两次时,我很担心。我在 5.12 上测试了您的解决方案,但服务器使用的是 5.8.8。但是,您的解决方案仍然适用于 5.8.8。我是一名CM,多年来没有进行过认真的开发。我曾经了解 8085A 汇编器,并且在 Xenix 上使用 C 非常好,我想这让我有点过时了。我会通过 illguts 看看我的大脑能容纳多少。他们有很多 1980 年代的东西,但我会找到空间的。
    【解决方案2】:

    你想做什么?为什么一个类如何定义或实现它响应的方法很重要?

    Perl 是一种动态语言,这意味着方法根本不必存在。使用AUTOLOAD,方法可能非常好并且可调用,但永远不会出现在符号表中。一个好的接口可以让can 在这些情况下工作,但在某些情况下,类或对象可能会决定以 false 响应。

    Package::Stash 模块可以帮助您在特定命名空间中找到已定义的子例程,但正如您所说,它们可能不在同一个文件中定义。类中的方法可能来自继承的类。如果你关心他们来自哪里,那你可能做错了。

    【讨论】:

    • 我有一个名为 Sections 的类,它表示我的控制文件中的各种节类型,它是 Windows INI 格式。每个部分都有参数,但对于每种类型的部分,参数是不同的。 (目前有五个)。每种类型的节都是节的子类。我有另一个类为我保存所有不同的部分列表。每个部分类型在最后一个类中都有一个新方法。这样,我对该 INI 文件的整个定义都在一个对象中。我的类上的一种方法让我得到所有其他子类的名称。
    • 我曾经使用 AUTOLOAD,但我现在避免使用它。使用use strict 可以让编程变得更好。使用参考结构消除了这种安全性。我可能在一个地方有$foo->{BAR},在另一个地方有$foo->{BRA},而且严格不能'抓住这一点。 OO 放回了安全性。如果我的方法是 $foo->Bar,调用 $foo->Bra 会使我的程序崩溃。 AUTOLOAD 打破了这种设计。两者现在都是真正的方法。它还导致整体设计较差。我可以用 AUTOLOAD 来实现它。我不必考虑清楚。我的这个程序的上一个版本使用了 AUTOLOAD,由于 AUTOLOAD,它可能很难调试。
    • 我会在问题中添加所有解释:)
    猜你喜欢
    • 2011-12-10
    • 2010-11-08
    • 2013-05-24
    • 2021-12-29
    • 2010-12-29
    • 2018-05-25
    • 2013-02-24
    • 1970-01-01
    • 2020-05-02
    相关资源
    最近更新 更多