如何在Ruby中创建对象的深层副本?

问题描述 投票:47回答:9

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

是否有一种快速简便的(内置)方式来深入复制Ruby中的对象?字段不是数组或散列。

在Ruby 1.9.2中工作。

ruby-on-rails ruby object copy deep-copy
9个回答
65
投票

深层副本不是内置于vanilla 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


16
投票

我已经创建了一个本机实现来执行ruby对象的深层克隆。

它比Marshal方法快约6至7倍。

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

请注意,此项目不再维护(最后提交在2017,有报告issues


7
投票

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

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

6
投票

有一个本机实现来执行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


3
投票

你可以使用重复的宝石。

它是一个小的纯ruby gem,能够递归地复制对象它也会将它的对象引用复制到新的复制中。

require 'duplicate'
duplicate('target object')

https://rubygems.org/gems/duplicate

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


2
投票

我建议你使用ActiveSupport gem,它会为你的原生Ruby核心添加大量的糖,而不仅仅是一个deep clone方法。

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


2
投票

自动深度克隆并不总是你想要的。通常,您需要定义选定的几个属性以进行深度克隆。一种灵活的方法是实现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/中学到了这一点


1
投票

还可以查看deep_dive。这允许您对对象图进行受控深度复制。

https://rubygems.org/gems/deep_dive


0
投票

你真的不需要宝石。这不可能比这更简单,这不值得宝石的开销!

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
© www.soinside.com 2019 - 2024. All rights reserved.