【问题标题】:How to create a deep copy of an object in Ruby?如何在 Ruby 中创建对象的深层副本?
【发布时间】:2012-01-02 15:21:15
【问题描述】:

我做了一些搜索,发现了一些关于创建深拷贝操作符的不同方法和帖子。

在 Ruby 中是否有一种快速简单(内置)的方法来深度复制对象?这些字段不是数组或哈希。

在 Ruby 1.9.2 中工作。

【问题讨论】:

标签: ruby-on-rails ruby object copy deep-copy


【解决方案1】:

有一个本地实现来执行 ruby​​ 对象的深度克隆:ruby_deep_clone

用 gem 安装它:

gem install ruby_deep_clone

示例用法:

require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)

它比 Marshal 方法和事件处理冻结物体的速度快大约 6 到 7 倍。

注意这个项目不再维护了(上次提交在2017,有报道issues

【讨论】:

  • 不幸的是,这个 gem 不处理继承自 Array 并包含它们自己的复杂嵌套类的自定义 Collection 类。 Ruby_deep_clone 将这些对象转换为数组,它们会失去自定义属性。
  • 根据作者的说法,这个项目不再维护,也没有准备好生产。
  • @bbozo 它声称支持 Ruby 2.4。是否有消息来源说它不再维护?
  • 此答案与之前发布的stackoverflow.com/questions/8206523/… 重复
【解决方案2】:

我创建了一个本地实现来执行 ruby​​ 对象的深度克隆。

它比 Marshal 方法快大约 6 到 7 倍。

https://github.com/balmma/ruby-deepclone

注意这个项目不再维护了(上次提交在2017,有报道issues

【讨论】:

  • 根据作者的说法,这个项目不再维护,也没有准备好生产。
【解决方案3】:

你真的不需要宝石。这再简单不过了,这不值得一个 Gem 的开销!

def deep_clone(obj)
  obj.clone.tap do |new_obj|
    new_obj.each do |key, val|
      new_obj[key] = deep_clone(val) if val.is_a?(Hash)
    end
  end
end

【讨论】:

    【解决方案4】:

    自动深度克隆并不总是您想要的。通常您需要定义一些选定的属性来进行深度克隆。一种灵活的方法是实现initialize_copyinitialize_dupinitialize_clone 方法。

    如果你有课:

    class Foo
      attr_accessor :a, :b
    end
    

    而您只想深度克隆:b,则覆盖initialize_* 方法:

    class Foo
      attr_accessor :a, :b
    
      def initialize_dup(source)
        @b = @b.dup
        super
      end
    end
    

    当然,如果你想让@b 也深度克隆它自己的一些属性,你在 b 的类中也这样做。

    Rails 会这样做(请参阅 https://github.com/rails/rails/blob/0951306ca5edbaec10edf3440d5ba11062a4f2e5/activemodel/lib/active_model/errors.rb#L78

    为了更完整的解释,我从这篇帖子https://aaronlasseigne.com/2014/07/20/know-ruby-clone-and-dup/学到了这里

    【讨论】:

      【解决方案5】:

      您可以为此使用重复的 gem。

      它是一个小的纯红宝石,能够递归复制对象 它也会将它的对象引用复制到新的副本中。

      require 'duplicate'
      duplicate('target object')
      

      https://rubygems.org/gems/duplicate

      https://github.com/adamluzsi/duplicate.rb

      【讨论】:

        【解决方案6】:

        Rails 有一个名为deep_dup 的递归方法,它将返回对象的深层副本,并且与dupclone 相反,它甚至可以在复合对象(数组/哈希的数组/哈希)上工作。 很简单:

        def deep_dup
          map { |it| it.deep_dup }
        end
        

        【讨论】:

        • Rails 的 Object#deep_dup 实际上是一个浅的dup 调用。 Besdie Hash 和 Array,您需要实现自己的 deep_dup 才能使其工作。
        【解决方案7】:

        我建议您使用 ActiveSupport gem,它为您的原生 Ruby 核心添加了很多糖,而不仅仅是 deep clone 方法。

        您可以查看documentation 以获取有关已添加方法的更多信息。

        【讨论】:

          【解决方案8】:

          还可以查看 deep_dive。这允许您对对象图进行受控的深层副本。

          https://rubygems.org/gems/deep_dive

          【讨论】:

            【解决方案9】:

            原始 Ruby 中没有内置深拷贝,但您可以通过编组和解组对象来破解它:

            Marshal.load(Marshal.dump(@object))
            

            但这并不完美,并且不适用于所有对象。更稳健的方法:

            class Object
              def deep_clone
                return @deep_cloning_obj if @deep_cloning
                @deep_cloning_obj = clone
                @deep_cloning_obj.instance_variables.each do |var|
                  val = @deep_cloning_obj.instance_variable_get(var)
                  begin
                    @deep_cloning = true
                    val = val.deep_clone
                  rescue TypeError
                    next
                  ensure
                    @deep_cloning = false
                  end
                  @deep_cloning_obj.instance_variable_set(var, val)
                end
                deep_cloning_obj = @deep_cloning_obj
                @deep_cloning_obj = nil
                deep_cloning_obj
              end
            end

            来源:

            http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/43424

            【讨论】:

            • 好的,谢谢。我找到了一篇关于编组的帖子,但不知道这是否是一个合理的解决方案。 Marshal.load(Marshal.dump(@object)) 效果很好。
            • 是的,它只是相对而言比较慢 - 只有在一次克隆大量对象时才会注意到
            • 显然,它还假设对象首先是可编组的,但并非所有对象都是。
            • 怎么会没有任何 ruby​​ 内置的方法呢?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-12-28
            • 1970-01-01
            • 1970-01-01
            • 2015-01-10
            • 2023-03-05
            相关资源
            最近更新 更多