10

Pythonにはメタクラスの概念があり、私が正しく理解していれば、構築時にクラスのオブジェクトを変更できます。クラスを変更するのではなく、作成してから初期化するオブジェクトを変更します。

Python(少なくとも3.0の時点では、私は信じています)にもクラスデコレータのアイデアがあります。繰り返しになりますが、私が正しく理解していれば、クラスデコレータは、宣言されている時点でクラス定義を変更できます。

今では、Rubyのクラスデコレータと同等の機能があると思いますが、現在、メタクラスと同等の機能を認識していません。いくつかの関数を介してRubyオブジェクトを簡単にポンピングし、それに対して実行できることは確かですが、メタクラスのように設定する機能は言語にありますか?

繰り返しになりますが、RubyにはPythonのメタクラスに似たものがありますか?

編集私はPythonのメタクラスに参加していませんでした。メタクラスとクラスデコレータは、見た目と非常によく似た動作をします。どちらも、定義時にクラスを変更しますが、方法は異なります。うまくいけば、Pythonの第一人者がやって来て、Pythonのこれらの機能についてよりよく説明するでしょう。

ただし、クラスまたはクラスの親は__new__(cls[,..])、オブジェクトがで初期化される前に、オブジェクトの構造をカスタマイズする関数を実装できます__init__(self[,..])

編集この質問は主に、2つの言語がこれらの機能でどのように比較されるかについての議論と学習のためのものです。私はPythonに精通していますが、Rubyには精通しておらず、興味がありました。うまくいけば、2つの言語について同じ質問をしている他の人が、この投稿が役に立ち、啓発的であることがわかるでしょう。

4

2 に答える 2

24

Rubyにはメタクラスがありません。Rubyにはいくつかの構造があり、メタクラスを誤って呼び出す人もいますが、そうではありません(これは際限のない混乱の原因です)。

ただし、メタクラスで行うのと同じ結果をRubyで実現する方法はたくさんあります。しかし、あなたが何をしたいのかを正確に教えてくれなければ、それらのメカニズムが何であるかはわかりません。

要するに:

  • Rubyにはメタクラスがありません
  • Rubyには、Pythonのメタクラスに対応する構造が1つもありません。
  • Pythonがメタクラスで実行できることはすべてRubyでも実行できます
  • ただし、単一の構成はありません。正確に何をしたいかに応じて、異なる構成を使用します。
  • これらの構成のいずれかには、メタクラスに対応しない他の機能も含まれている可能性があります(ただし、Pythonでは他の機能に対応している可能性があります)
  • PythonのメタクラスでできることはRubyでもできますが、必ずしも簡単ではないかもしれません。
  • 多くの場合、エレガントなよりルビーっぽいソリューションがありますが
  • 最後になりましたが、RubyではPythonのメタクラスでできることは何でもできますが、それを行うことが必ずしもRubyWayであるとは限りません。

では、メタクラスとは正確には何ですか?まあ、それらはクラスのクラスです。では、一歩後退しましょう。クラスとは正確には何ですか?

クラス …

  • オブジェクトの工場です
  • オブジェクトの動作を定義する
  • クラスのインスタンスであることが何を意味するかを形而上学的レベルで定義する

たとえば、このArrayクラスは配列オブジェクトを生成し、配列の動作を定義し、「配列らしさ」の意味を定義します。

メタクラスに戻ります。

メタクラス…

  • クラスの工場です
  • クラスの動作を定義する
  • 形而上学的レベルでクラスであることの意味を定義する

Rubyでは、これら3つの責任は3つの異なる場所に分割されます。

  • クラスはClassクラスを作成し、動作の少しを定義します
  • 個々のクラスの固有クラスは、クラスの動作の少しを定義します
  • 「クラスネス」の概念はインタープリターに組み込まれています。インタープリターは、動作の大部分も実装します(たとえば、Classメソッドを別の方法でルックアップする新しい種類のクラスを作成するために継承することはできません。メソッドルックアップアルゴリズムです。インタプリタに組み込まれています)

したがって、これら3つのものは一緒にメタクラスの役割を果たしますが、それらの1つはメタクラスではなく(それぞれがメタクラスの機能のごく一部を実装するだけです)、それらの合計もメタクラスではありません(それ)。

残念ながら、クラスの固有クラスをメタクラスと呼ぶ人もいます。(最近まで、私は最終的に光を見るまで、それらの見当違いの魂の1人でした。)他の人々はすべての固有クラスをメタクラスと呼びます。(残念ながら、そのうちの1人は、RubyメタプログラミングとRubyオブジェクトモデルに関する最も人気のあるチュートリアルの作成者です。)一部の人気のあるライブラリは、オブジェクトの固有クラス(ActiveSupport、Facets、metaidなど)を返すmetaclassメソッドを追加します。すべての仮想クラス(つまり、固有クラスとインクルードクラス)をメタクラスとObject呼ぶ人もいます。メタクラスと呼ぶ人もいます。Rubyのソースコード自体の中でも、「メタクラス」という言葉は、メタクラスではないものを指すために使用されます。Class

于 2010-04-20T20:02:48.763 に答える
13

更新された質問は、今ではまったく異なって見えます。私があなたを正しく理解しているなら、あなたはオブジェクトの割り当てと初期化にフックしたいと思うでしょう。それはメタクラスとはまったく関係がありません。(しかし、あなたはまだあなたが実際にやりたいことを書いていないので、私はまだオフになっているかもしれません。)

一部のオブジェクト指向言語では、オブジェクトはコンストラクターによって作成されます。ただし、Rubyにはコンストラクターがありません。コンストラクターは単なるファクトリメソッドです(愚かな制限があります)。代わりに(より強力な)ファクトリメソッドを使用できるのであれば、適切に設計された言語でそれらを使用する理由はありません。

Rubyでのオブジェクト構築は、次のように機能します。オブジェクト構築は、割り当て初期化の2つのフェーズに分割されます。割り当てはallocate、クラスのインスタンスメソッドとして定義され、Class通常はオーバーライドされない、と呼ばれるパブリッククラスメソッドによって行われます。(実際、実際にオーバーライドできるとは思いません。)オブジェクトにメモリスペースを割り当て、いくつかのポインタを設定するだけですが、現時点ではオブジェクトは実際には使用できません。

initializeそこでイニシャライザが登場します。これは、オブジェクトの内部状態を設定し、他のオブジェクトで使用できる一貫性のある完全に定義された状態にする、と呼ばれるインスタンスメソッドです。

したがって、新しいオブジェクトを完全に作成するには、次のようにする必要があります。

x = X.allocate
x.initialize

[注:Objective-Cプログラマーはこれを認識している可能性があります。]

ただし、呼び出すのを忘れがちでinitializeあり、原則として、オブジェクトは構築後に完全に有効である必要があるため、と呼ばれる便利なファクトリメソッドがありClass#newます。これは、すべての作業を実行し、次のようになります。

class Class
  def new(*args, &block)
    obj = allocate
    obj.initialize(*args, &block)

    return obj
  end
end

[注:実際にinitializeはプライベートであるため、次のようなアクセス制限を回避するには、リフレクションを使用する必要があります:obj.send(:initialize, *args, &block)]

ちなみに、これが、パブリッククラスメソッドを呼び出すオブジェクトを作成するのに、プライベートインスタンスメソッドFoo.new実装Foo#initializeする理由です。これは、多くの新規参入者をつまずかせるようです。

ただし、これはいずれも言語に組み込まれていません。通常、任意のクラスのプライマリファクトリメソッドが呼び出されるという事実newは単なる慣例です(Javaのコンストラクターに似ていますが、完全に異なるため、異なる場合があります)。他の言語では、コンストラクターには特定の名前を付ける必要があります。Javaでは、クラスと同じ名前にする必要があります。つまり、a)コンストラクターは1つしか存在できず、b)匿名クラスは名前がないため、コンストラクターを持つことができません。Pythonでは、ファクトリメソッドを呼び出す必要があります__new__、これも1つしか存在できないことを意味します。(JavaとPythonの両方で、もちろん異なるファクトリメソッドを使用できますが、それらを呼び出すことはデフォルトを呼び出すこととは異なって見えますが、Ruby(およびこのパターンが発生した元のSmalltalk)ではまったく同じように見えます。)

Rubyでは、ファクトリメソッドは好きなだけ名前を付けることができ、ファクトリメソッドにはさまざまな名前を付けることができます。(たとえば、コレクションクラスの場合、ファクトリメソッドは多くの場合、にエイリアスされます。これにより、配列のように見える代わりに[]記述できるため、リストのコレクションっぽい性質が強調されます。)List[1, 2, 3]List.new(1, 2, 3)

要するに:

  • 標準化されたファクトリメソッドはですがFoo.new、何でもかまいません
  • Foo.newallocate空のオブジェクトにメモリを割り当てるための呼び出しfoo
  • Foo.newfoo.initialize次に、Foo#initializeインスタンスメソッドを呼び出します
  • これらの3つはすべて、他のメソッドと同じように、定義を解除、再定義、オーバーライド、ラップ、エイリアスなどを行うことができるメソッドです。
  • ただしallocate、Rubyでは実際にはできないRubyランタイム内にメモリを割り当てる必要があります。

Pythonでは、__new__おおよそとRubyの両方に対応しnewallocateRubyでは__init__ 正確に対応しinitializeます。主な違いは、Rubyではnew呼び出しinitializeが行われるのに対し、Pythonではランタイム__init__が。の後に自動的に呼び出すこと__new__です。

たとえば、これは最大2つのインスタンスの作成のみを許可するクラスです。

class Foo
  def self.new(*args, &block)
    @instances ||= 0
    raise 'Too many instances!' if @instances >= 2

    obj = allocate
    obj.send(:initialize, *args, &block)

    @instances += 1

    return obj
  end

  attr_reader :name

  def initialize(name)
    @name = name
  end
end

one = Foo.new('#1')
two = Foo.new('#2')
puts two.name         # => #2
three = Foo.new('#3') # => RuntimeError: Too many instances!
于 2010-04-20T22:29:16.110 に答える