【问题标题】:equivalent of Python's "with" in Ruby相当于 Ruby 中 Python 的“with”
【发布时间】:2010-10-06 18:14:29
【问题描述】:

在 Python 中,with 语句用于确保始终调用清理代码,而不管抛出异常或返回函数调用。例如:

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

在此处,文件已关闭,即使引发了异常。更好的解释是here

在 Ruby 中是否有这个结构的等价物?或者你可以编写一个代码,因为 Ruby 有延续?

【问题讨论】:

    标签: python ruby language-features with-statement control-flow


    【解决方案1】:

    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 示例几乎没有区别,但它不需要为语言添加新语法。

    【讨论】:

    • 是的,我同意当您的语言需要新功能时,这是一个不好的迹象。在 Python 中需要“with”的唯一原因是匿名函数无缘无故地受到损害。按照这种逻辑,Scheme 是最好的,因为您可以使用宏自己实现新的语言特性 =)
    • +1 能够动态地将它添加到语言中(并且使用如此少量的代码)非常酷。但是,我认为该语言也具有丰富的语法也有一个优势。每个 Python 开发人员都知道 Python 中的“with”是什么意思,并且在 Python 网站/书籍上都有详细记录。
    • @Claudiu,Ruby 可以很好地伪造某些类型的宏,请参阅:def my_if(condition, &block) block.call if condition; end 像这样使用:my_if(x == 5) { puts "x is 5!" }
    • 对不起,但我不同意“在 File 上下文中正确执行代码块的责任应该属于 File 类”。这不是 Python、Java、C# 甚至 Objective-C 的工作方式,我认为这些语言几乎都是面向对象的。不要误会我的意思,我可以看到这种说法的“美”,我确实尊重“Ruby方式”。但这正是它的本质:一种漂亮、优雅、极具活力和表现力的做事方式,但不是“OO”方式。事实上,这更多地与函数式编程有关,而不是 OO。
    • Ruby 比 python 有 很多 多余的语言特性 - with 在块上方的存在使其范围内的所有内容的上下文非常清晰。
    【解决方案2】:

    Ruby 中的等价物是将块传递给 File.open 方法。

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

    这是 Ruby 使用的惯用语,您应该熟悉它。

    【讨论】:

    • 这是 'with' 的确切用法的一个特例,但我对泛型结构很感兴趣
    • 我对 Python 不是很了解,但它看起来像 C# 的 'using' 语句。 Ruby 中没有类似的语法糖,但许多库类实现了我上面展示的方法。
    【解决方案3】:

    您可以在 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  
    

    【讨论】:

    • 忽略你需要ensure 来捕捉特殊情况,我既喜欢又讨厌这个解决方案。它展示了 Ruby 的灵活性(因此它几乎可以接近 Python 语法),另一方面......为什么要在 Ruby 中接近 Python 语法? :)
    • 是的,这无缘无故地扭曲了语言。其他答案更好。这仍然很棒
    • 我不会在我的代码中这样做,但是是的,这样的事情是可能的很酷(而且有点可怕):)
    • 我不认为Ruby有一个名为__enter__的方法
    【解决方案4】:

    我只是为其他人添加更多解释;功劳应该归于他们。

    确实,在 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(以通常的方式调用)和yield(在这种情况下您还需要向块添加参数)添加参数。因此,要实现您上面引用的内容,

    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
    

    【讨论】:

    • 我认为您正在做某事.. 我认为 ruby​​ 有块意味着不需要特殊的“with”语句。我不太了解红宝石,那只是创建一个块,将其传递给受控执行器,然后在达到“产量”时执行?
    • 多写一点 - Ruby 有块意味着它可以用语法做一些非常简单的事情。查看 Sinatra、RSpec 和 Rake 的示例,了解创造性地使用块的一些想法。
    • 您甚至可以(在某种程度上)使用块在 ruby​​ 中重新实现您自己的 if/else:github.com/banister/custom_boolean/blob/master/…
    【解决方案5】:

    可以在 Ruby 中以原子方式写入文件,如下所示:

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

    这样编写代码意味着不可能不小心打开文件。

    【讨论】:

      【解决方案6】:

      您始终可以使用try..catch..finally 块,其中finally 部分包含要清理的代码。

      编辑:对不起,错了:你想要begin..rescue..ensure

      【讨论】:

      • 您也可以在 Python 中执行此操作。 with 是一个很好的方便,请参阅解释链接
      • 抱歉,Python 不太热。 ;) AFAIK,没有使用有效的 try/catch 块的速记。不过,你最好使用积木。
      【解决方案7】:

      我相信你正在寻找ensure

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-16
        • 2019-07-04
        • 2011-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多