2017-10-03 9 views
2

En Python, je peux obtenir la longueur d'un list en appelant sa méthode __len__. __len__ n'est pas défini sur chaque objet individuel list, mais sur la classe list. La méthode est directement accessible par la classe:Dans Ruby, pourquoi `Array.length` donne NoMethodError?

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

Cependant, le code équivalent ne fonctionne pas dans Ruby:

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

Pourquoi la méthode peut length ne se trouve lors de l'utilisation Array.length? La recherche de méthode dans Ruby fonctionne-t-elle différemment de Python?

Répondre

6

Oui, la méthode de recherche dans Ruby fonctionne un peu différemment de la méthode de recherche dans Python.

Le TL; DR réponse est, si vous voulez vraiment tirer la méthode length du Array objet de classe, vous aurez besoin d'écrire Array.instance_method(:length). Cela vous obtenir un objet UnboundMethod, que vous pouvez ensuite lier à objet particulier et appeler:

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

Sans surprise, ce n'est pas fait couramment dans Ruby.

Dans Ruby, les classes elles-mêmes sont des objets, instances de la classe Class; ces classes ont leurs propres méthodes, au-dessus et au-delà des méthodes qu'ils fournissent à leurs instances, des méthodes telles que new, name, superclass, etc. Bien sûr, il serait possible de définir une méthode length sur l'objet de classe Array de sorte que vous pourrait écrire Array.length, mais cela ne serait pas intrinsèquement lié à la méthode d'instance du même nom. Il y a quelques endroits dans les classes de base et la bibliothèque standard où cela est réellement fait, mais pour la plupart, ce n'est pas idiomatique dans Ruby.

La distinction entre les méthodes d'un objet Class répond directement, et celles qu'il prévoit ses instances, on peut voir assez clairement dans la sortie des méthodes methods et instance_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!, ... 

Une raison pour laquelle La méthode de recherche dans Ruby ne fonctionne pas comme elle le fait dans Python, car il peut y avoir des noms de méthodes auxquels une classe et une instance doivent répondre, de différentes manières. Par exemple, considérons une classe hypothétique Personne:

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

En Python, bien sûr, le premier appel serait une erreur: vous ne pouvez pas appeler Person.ancestors sans passer une instance de Person être __self__, qui ne serait pas ne veut rien dire. Mais alors, comment obtenez-vous les ancêtres de la classe Person? Vous passeriez Person lui-même à une fonction: inspect.getmro(Person). C'est idiomatique en Python, mais serait considéré comme un style extrêmement pauvre en Ruby. Essentiellement, en tant que langues différentes, même si elles sont similaires à bien des égards, elles ont toujours différentes façons de résoudre certains problèmes, et certaines choses qui sont considérées comme une bonne pratique ou un style sont très mauvaises dans l'autre (et vice versa).