Что такое эквивалент Python для Ruby?
Я перехожу из Ruby в Python для проекта. Я ценю тот факт, что Python имеет первоклассные функции и закрытие, поэтому этот вопрос должен быть легким. Я просто не понял, что является идиоматически правильным для Python:
В Ruby я мог бы написать:
def with_quietude(level, &block)
begin
saved_gval = gval
gval = level
yield
ensure
gval = saved_gval
end
end
и назовите его следующим образом:
with_quietude(3) {
razz_the_jazz
begin_the_beguine
}
(Примечание. Я не спрашиваю о обработке Python try/finally
, а также о сохранении и восстановлении переменных. Мне просто нужен нетривиальный пример обертывания блока внутри какого-либо другого кода.)
Обновление
Или, поскольку некоторые ответы навешены на глобальных назначениях в предыдущем примере, когда я действительно спрашиваю о закрытии, что, если вызов был следующим? (Обратите внимание, что это не меняет определение with_quietude):
def frumble(x)
with_quietude {
razz_the_jazz(x)
begin_the_beguine(2 * x)
}
end
Как бы вы реализовали нечто подобное в Python (и не рассмеялись экспертами Python)?
Ответы
Ответ 1
Глядя на доход от рубинов, похоже, что вы хотите что-то вроде contextlib.contextmanager
:
from contextlib import contextmanager
def razz_the_jazz():
print gval
@contextmanager
def quietude(level):
global gval
saved_gval = gval
gval = level
try:
yield
finally:
gval = saved_gval
gval = 1
with quietude(3):
razz_the_jazz()
razz_the_jazz()
Этот script выдает:
3
1
указав, что наш менеджер контекста сделал reset gval
в глобальном пространстве имен. Конечно, я бы не использовал этот менеджер контекста, поскольку он работает только в глобальном пространстве имен. (Например, он не будет работать с местными жителями в функции).
Это в основном ограничение того, как присваивание создает новую ссылку на объект и что вы никогда не сможете напрямую перекрещивать объект путем присвоения ему. (Единственный способ мутировать объект - назначить один из его атрибутов или через __setitem__
(a[x] = whatever
))
Ответ 2
Слово предупреждения, если вы пришли из Ruby: все python 'def в основном такие же, как ruby' proc '.
Python не имеет эквивалента для ruby 'def'
Вы можете получить очень схожее поведение с тем, что вы просите, определив свои собственные функции в области вызывающей функции
def quietude(level, my_func):
saved_gval = gval
gval = level
my_func()
def my_func():
razz_the_jazz()
begin_the_beguine()
quietude(3, my_func)
---- EDIT: Запрос дополнительной информации: -----
Ярлыки Python ограничены одной строкой, поэтому они не так гибки, как рубины.
Чтобы передавать функции с аргументами вокруг, я бы рекомендовал частичные функции увидеть следующий код:
import functools
def run(a, b):
print a
print b
def runner(value, func):
func(value)
def start():
s = functools.partial(run, 'first')
runner('second', s)
---- Редактировать 2 Дополнительная информация ----
Функции Python вызывается только тогда, когда к ним добавляется '()'. Это отличается от ruby, где "()" являются необязательными. В приведенном ниже коде выполняется "b_method" в start() и "a_method" в run()
def a_method():
print 'a_method is running'
return 'a'
def b_method():
print 'b_method is running'
return 'b'
def run(a, b):
print a()
print b
def start():
run(a_method, b_method())
Ответ 3
Мне нравится ответ, который дает mgilson, поэтому он получает чек. Это просто небольшое расширение возможностей @contextmanager для кого-то из мира Ruby.
gval = 0
from contextlib import contextmanager
@contextmanager
def quietude(level):
global gval
saved_gval = gval
gval = level
try:
yield
finally:
gval = saved_gval
def bebop(x):
with quietude(3):
print "first", x*2, "(gval =", gval, ")"
print "second", x*4, "(gval =", gval, ")"
bebop(100)
bebop("xxxx")
Это выдает:
first 200 (gval = 3 )
second 400 (gval = 3 )
first xxxxxxxx (gval = 3 )
second xxxxxxxxxxxxxxxx (gval = 3 )
Это показывает, что все, что входит в область with
, имеет доступ к лексически закрытым переменным и ведет себя более или менее так, как ожидал бы человек, прибывающий из мира Ruby.
Хороший материал.