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