2017-10-03 9 views
2

Pythonでは、__len__メソッドを呼び出すことで、listの長さを取得できます。 __len__は、個々のlistオブジェクトではなく、listクラスに定義されています。この方法は、クラスを介して直接アクセスすることができる。Rubyでは、なぜ `Array.length`がNoMethodErrorを与えていますか?

>>> [].__len__() 
0 
>>> type([]) 
<class 'list'> 
>>> list.__len__([]) 
0 

しかし、同等のコードはRubyで動作しない:Array.lengthを使用する場合

> [].length 
=> 0 
> [].class 
=> Array 
> Array.length([]) 
NoMethodError: undefined method `length' for Array:Class 

なぜlength方法が見つかりませんか? RubyのメソッドルックアップはPythonとは異なる働きをしますか?

答えて

6

はい、Rubyのメソッドルックアップは、Pythonのメソッドルックアップとは少し異なります。

TL:DRの答えは、Arrayクラスオブジェクトから実際にlengthメソッドを取得する場合は、Array.instance_method(:length)と書く必要があります。

unbound_method = Array.instance_method(:length) 
ary = [1,2,3,4] 
bound_method = unbound_method.bind(ary) 
bound_method.call #=> 4 

当然のことながら、これは一般的にRubyで行われていない:これは、あなたが、特定のオブジェクトにバインドし、呼び出すことができUnboundMethodオブジェクトを取得します。

Rubyでは、クラス自体は、クラスClassのインスタンスです。これらのクラスは、上記と彼らはなどもちろんnewnamesuperclass、としてそのインスタンス、メソッドを提供する方法を超えて、自分自身のメソッドを持って、それが可能はあなたのでArrayクラスオブジェクトにlengthメソッドを定義することですArray.lengthと書くことができますが、これは本質的に同じ名前のインスタンスメソッドには関係しません。コアクラスにはいくつかの場所があり、実際にこれが行われる標準ライブラリがありますが、大部分はRubyでは慣用的ではありません。

方法区別Classオブジェクトを直接に応答し、それはそのインスタンスを提供するものは、methodsinstance_methods方法の出力にかなりはっきりと見ることができる。

irb(main):001:0> Array.methods 
=> [:[], :try_convert, :new, :allocate, :superclass, ... 
irb(main):002:0> Array.instance_methods 
=> [:each_index, :join, :rotate, :rotate!, :sort!, ... 

一つの理由Rubyのメソッドルックアップは、Pythonでのやり方では機能しません。クラスとインスタンスの両方が異なる方法で応答する必要のあるメソッド名があるかもしれません。例えば、仮想のPersonクラスを考えてみます。Pythonで

Person.ancestors # => [Person, Animal, Organism, Object, Kernel, BasicObject] 
john = Person.new(...) 
john.ancestors # => [John Sr., Sally, Jospeh, Mary, Charles, Jessica] 

を、当然のことながら、最初の呼び出しはエラーになります:あなたはそれはないだろう、__self__するPersonのインスタンスを渡さずPerson.ancestorsを呼び出すことはできません意味をなさないしかし、Personクラスの祖先はどうやって取得できますか?関数inspect.getmro(Person)にはPerson自体を渡します。これはPythonでは慣用的ですが、Rubyでは非常に貧弱なスタイルとみなされます。

本質的には、多くの点で類似しているにもかかわらず、特定の問題を解決するさまざまな方法がありますが、一方では良い練習やスタイルと見なされるものもあります逆に)。

関連する問題