Ruby 没有函数。它只有方法(不是一流的)和Proc
一流的,但不与任何对象相关联。
所以,这是一种方法:
def foo(bar) puts bar end
foo('Hello')
# Hello
哦,是的,这是一个真正的方法,而不是顶级函数或过程之类的东西。在顶层定义的方法最终成为Object
类中的私有(!)实例方法:
Object.private_instance_methods(false) # => [:foo]
这是一个Proc
:
foo = -> bar { puts bar }
foo.('Hello')
# Hello
请注意,Proc
s 的调用方式与方法不同:
foo('Hello') # method
foo.('Hello') # Proc
foo.(bar)
语法只是语法糖foo.call(bar)
(对于sProc
和Method
s 也别名为foo[bar]
)。在对象上实现一个call
方法然后调用它.()
是最接近 Python 能力的事情__call__
。
请注意,Ruby 和 Python lambda 之间的一个重要区别Proc
是没有限制:在 Python 中,一个 lambda 只能包含一条语句,但 Ruby 没有语句和表达式之间的区别(一切都是表达式),并且所以这个限制根本不存在,因此在很多情况下,您需要在 Python 中将命名函数作为参数传递,因为您无法在单个语句中表达逻辑,您可以在 Ruby 中简单地传递 aProc
或块,因此甚至不会出现引用方法的难看语法问题。
您可以通过调用对象上的方法(这将为您提供绑定到该特定对象的对象)来将方法包装在Method
对象中(本质上是鸭子类型):Proc
Object#method
Method
self
foo_bound = method(:foo)
foo_bound.('Hello')
# Hello
您还可以使用该Module#instance_method
系列中的一种方法UnboundMethod
从模块(或类,显然,因为一个类是一个模块)中获取一个,然后您可以UnboundMethod#bind
对一个特定的对象进行调用。(我认为 Python 具有相同的概念,尽管实现方式不同:未绑定的方法只是显式地接受 self 参数,就像它被声明的方式一样。)
foo_unbound = Object.instance_method(:foo) # this is an UnboundMethod
foo_unbound.('Hello')
# NoMethodError: undefined method `call' for #<UnboundMethod: Object#foo>
foo_rebound = foo_unbound.bind(self) # this is a Method
foo_rebound.('Hello')
# Hello
请注意,您只能将 an 绑定UnboundMethod
到一个对象,该对象是您从中获取方法的模块的实例。您不能用于UnboundMethods
在不相关的模块之间“移植”行为:
bar = module Foo; def bar; puts 'Bye' end; self end.instance_method(:bar)
module Foo; def bar; puts 'Hello' end end
obj = Object.new
bar.bind(obj)
# TypeError: bind argument must be an instance of Foo
obj.extend(Foo)
bar.bind(obj).()
# Bye
obj.bar
# Hello
但是请注意,theMethod
和 theUnboundMethod
都是方法的包装器,而不是方法本身。方法不是Ruby 中的对象。(与我在其他答案中写的相反,顺便说一句。我真的需要回去修复这些。)您可以将它们包装在对象中,但它们不是对象,您可以看到这一点,因为您基本上得到了所有相同的东西使用包装器时经常遇到的问题:身份和状态。如果你method
多次调用同一个方法,你Method
每次都会得到一个不同的对象。如果您尝试在该Method
对象上存储一些状态(例如 Python 风格__doc__
字符串,例如),该状态将是该特定实例的私有状态,如果您尝试再次通过 检索您的文档字符串method
,您会发现它已经消失了。
方法引用运算符.:
的形式还有语法糖:
bound_method = obj.:foo
这与
bound_method = obj.method(:foo)