Reputation: 14346
In Python, I can get the length of a list
by calling its __len__
method. __len__
is not defined on each individual list
object, but on the list
class. The method can be accessed directly through the class:
>>> [].__len__()
0
>>> type([])
<class 'list'>
>>> list.__len__([])
0
However, the equivalent code does not work in Ruby:
> [].length
=> 0
> [].class
=> Array
> Array.length([])
NoMethodError: undefined method `length' for Array:Class
Why can the length
method not be found when using Array.length
? Does method lookup in Ruby work differently from Python?
Upvotes: 2
Views: 475
Reputation: 1767
Yes, method lookup in Ruby works quite a bit differently than method lookup in Python.
The TL;DR answer is, if you really want to pull the length
method from the Array
Class object, you'd need to write Array.instance_method(:length)
. This would get you an UnboundMethod
object, which you could then bind to particular object and call:
unbound_method = Array.instance_method(:length)
ary = [1,2,3,4]
bound_method = unbound_method.bind(ary)
bound_method.call #=> 4
Unsurprisingly, this is not commonly done in Ruby.
In Ruby, Classes themselves are objects, instances of the class Class
; these classes have their own methods, above and beyond the methods they provide for their instances, methods such as new
, name
, superclass
, etc. Of course, it would be possible to define a length
method on the Array
class object so you could write Array.length
, but this would not be inherently related to the instance method of the same name. There are a few places in core classes and the standard library where this is actually done, but for the most part it is not idiomatic in Ruby.
The distinction between the the methods a Class
object responds to directly, and those it provides for its instances, can be seen pretty clearly in the output of the methods
and instance_methods
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!, ...
One reason that method lookup in Ruby doesn't work the way it does in Python is that there may be some method names that both a class and an instance need to respond to, in different ways. For example, consider a hypothetical Person class:
Person.ancestors # => [Person, Animal, Organism, Object, Kernel, BasicObject]
john = Person.new(...)
john.ancestors # => [John Sr., Sally, Jospeh, Mary, Charles, Jessica]
In Python, of course, the first call would be an error: you can't call Person.ancestors
without passing an instance of Person
to be __self__
, that wouldn't make any sense. But then how do you get the ancestors of the Person class? You'd pass Person
itself to a function: inspect.getmro(Person)
. That's idiomatic in Python, but would be considered extremely poor style in Ruby.
Essentially, as different languages, even though in many ways they are similar, they do still have different ways of solving certain problems, and some things that are considered good practice or style in one are quite bad in the other (and vice versa).
Upvotes: 6