在 Ruby 中,为什么 'Array.length' 给出 NoMethodError?



在Python中,我可以通过调用其__len__方法来获取list的长度。__len__不是在每个单独的list对象上定义的,而是在list类上定义的。该方法可以直接通过类访问:

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

但是,等效代码在 Ruby 中不起作用:

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

为什么使用Array.length时找不到length方法?Ruby 中的方法查找与 Python 的工作方式不同吗?

是的,Ruby 中的方法查找与 Python 中的方法查找有很大不同。

TL;DR的答案是,如果你真的想从Array类对象中提取length方法,你需要编写Array.instance_method(:length)。这将为你提供一个UnboundMethod对象,然后你可以将其绑定到特定对象并调用:

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

不出所料,这在 Ruby 中并不常见。

在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 类:

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

当然,在 Python 中,第一次调用将是一个错误:如果不传递要__self__Person实例,就无法调用Person.ancestors,这没有任何意义。但是,如何获得Person类的祖先呢?您将Person自身传递给一个函数:inspect.getmro(Person)。这在 Python 中是惯用语,但在 Ruby 中会被认为是非常糟糕的风格。

从本质上讲,作为不同的语言,尽管它们在许多方面是相似的,但它们仍然有不同的方法来解决某些问题,并且一些在一种语言中被认为是良好实践或风格的东西在另一种语言中却非常糟糕(反之亦然)。

相关内容

  • 没有找到相关文章

最新更新