эквивалент Python "с" в Ruby - PullRequest
       48

эквивалент Python "с" в Ruby

12 голосов
/ 06 октября 2010

В Python оператор with используется, чтобы гарантировать, что код очистки вызывается всегда, независимо от того, какие исключения генерируются или возвращаются вызовы функций. Например:

with open("temp.txt", "w") as f:
    f.write("hi")
    raise ValueError("spitespite")

Здесь файл закрыт, даже если возникла исключительная ситуация. Лучшее объяснение: здесь .

Есть ли эквивалент в этой конструкции в Ruby? Или вы можете закодировать один, поскольку в Ruby есть продолжения?

Ответы [ 7 ]

22 голосов
/ 06 октября 2010

Ruby имеет синтаксически облегченную поддержку буквальных анонимных процедур (в Ruby они называются blocks ).Поэтому для этого не требуется новая языковая функция.

(В общем, это плохой знак, если вам нужно добавить языковые функции. Вы должны иметь возможность реализовать все в библиотеке, иначеэто признак плохого языкового дизайна.)

Итак, вы обычно пишете метод, который принимает блок кода, выделяет ресурс, выполняет блок кода в контексте этого ресурса.и затем закрывает ресурс.

Примерно так:

def with(klass, *args)
  yield r = klass.open(*args)
ensure
  r.close
end

Вы можете использовать его следующим образом:

with File, 'temp.txt', 'w' do |f|
  f.write 'hi'
  raise 'spitespite'
end

Однако это очень процедурный способсделай это.Ruby является объектно-ориентированным языком, что означает, что ответственность за правильное выполнение блока кода в контексте File должна принадлежать классу File:

File.open 'temp.txt', 'w' do |f|
  f.write 'hi'
  raise 'spitespite'
end

Это может быть реализованопримерно так:

def File.open(*args)
  f = new(*args)
  return f unless block_given?
  yield f
ensure
  f.close if block_given?
end

Это общий шаблон, который реализуется множеством классов в базовой библиотеке Ruby, стандартных библиотеках и сторонних библиотеках.


ЕщеТочное соответствие с общим протоколом диспетчера контекста Python:

def with(ctx)
  yield ctx.setup
ensure
  ctx.teardown
end

class File
  def setup; self end
  alias_method :teardown, :close
end

with File.open('temp.txt', 'w') do |f|
  f.write 'hi'
  raise 'spitespite'
end

Обратите внимание, что это практически неотличимо от примера Python, но не требует добавления нового синтаксиса к языку.

9 голосов
/ 06 октября 2010

Эквивалентом в Ruby будет передача блока методу File.open.

File.open(...) do |file|
  #do stuff with file
end  #file is closed

Это идиома, которую использует Ruby, и с которой вам должно быть удобно.

4 голосов
/ 06 октября 2010

Вы можете использовать Block Arguments для этого в Ruby:

class Object  
    def with(obj)  
        obj.__enter__  
        yield  
        obj.__exit__  
    end  
end

Теперь вы можете добавить __enter__ и __exit__ методы в другой класс и использовать его следующим образом:

with GetSomeObject("somefile.text") do |foo|  
    do_something_with(foo)
end  
2 голосов
/ 29 августа 2012

Можно записать в файл атомарно в Ruby, например, так:

File.write("temp.txt", "hi")
raise ValueError("spitespite")

Написание такого кода означает, что невозможно случайно оставить файл открытым.

2 голосов
/ 06 октября 2010

Я просто добавлю еще несколько объяснений для других;им следует отдать должное.

Действительно, в Ruby код очистки, как говорили другие, в пункте ensure;но упаковка вещей в блоки повсеместна в Ruby, и именно так это делается наиболее эффективно и в духе Ruby.При переводе не переводите прямо в слово, вы получите несколько очень странных предложений.Точно так же не ожидайте, что все из Python будет иметь однозначное соответствие с Ruby.

По ссылке, которую вы разместили:

class controlled_execution:
    def __enter__(self):
        set things up
        return thing
    def __exit__(self, type, value, traceback):
        tear things down

with controlled_execution() as thing:
     some code

Ruby, что-то вроде этого (мужчина,Я, наверное, все делаю неправильно: D):

def controlled_executor
  begin
    do_setup
    yield
  ensure
    do_cleanup
  end
end

controlled_executor do ...
  some_code
end

Очевидно, вы можете добавить аргументы как к controlled executor (чтобы вызываться обычным способом), так и к результату (в этом случае вынеобходимо добавить аргументы в блок).Таким образом, чтобы реализовать то, что вы цитировали выше,

class File
  def my_open(file, mode="r")
    handle = open(file, mode)
    begin
      yield handle
    ensure
      handle.close
    end
  end
end

File.my_open("temp.txt", "w") do |f|
  f.write("hi")
  raise Exception.new("spitesprite")
end
0 голосов
/ 06 октября 2010

Я полагаю, вы ищете обеспечить .

0 голосов
/ 06 октября 2010

Вы всегда можете использовать блок try..catch..finally, где раздел finally содержит код для очистки.

Редактировать: извините, опечатка: вам нужно begin..rescue..ensure.

...