Ответ 1
Руби не имеет функций. Он имеет только методы (которые не являются первоклассными) и 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
как методы:
foo('Hello') # method
foo.('Hello') # Proc
Синтаксис foo.(bar)
- это просто синтаксический сахар для foo.call(bar)
(который для Proc
и Method
также имеет псевдоним foo[bar]
). Реализация метода call
на вашем объекте, а затем вызов его с помощью .()
- самая близкая вещь, которую вы получите к __call__
Python __call__
.
Обратите внимание, что важное различие между лямбдами в Ruby Proc
и Python заключается в том, что ограничений нет: в Python лямбда может содержать только один оператор, но в Ruby нет различия между операторами и выражениями (все является выражением), и так что это ограничение просто не существует, поэтому во многих случаях, когда вам нужно передать именованную функцию в качестве аргумента в Python, потому что вы не можете выразить логику в одном выражении, вы бы в Ruby просто передавали Proc
или блок вместо этого, так что проблема уродливого синтаксиса для ссылок на методы даже не возникает.
Вы можете обернуть метод в объект 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
Обратите внимание, что вы можете привязать 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
Обратите внимание, что Method
и Method
UnboundMethod
являются обертками вокруг метода, а не самого метода. Методы не являются объектами в Ruby. (Вопреки тому, что я написал в других ответах, кстати. Мне действительно нужно вернуться и исправить их.) Вы можете обернуть их в объекты, но они не являются объектами, и вы можете видеть это, потому что вы по сути получаете все то же самое проблемы, которые вы всегда получаете с обертками: личность и состояние. Если вы вызываете method
несколько раз для одного и того же метода, вы каждый раз получаете новый объект Method
. Если вы попытаетесь сохранить какое-либо состояние для этого объекта Method
(например, строки __doc__
стиле Python), это состояние будет частным для этого конкретного экземпляра, и если вы попытаетесь снова получить строку документации через method
, вы обнаружите, что это прошло.
Существует также синтаксический сахар в виде оператора ссылки на метод .:
::
bound_method = obj.:foo
Который идентичен
bound_method = obj.method(:foo)