Ruby의 dup과 clone 메소드의 차이점은 무엇입니까?


214

에 대한 루비 문서dup 말 :

일반적으로, clone그리고 dup하위 클래스에서 다른 의미를 가질 수있다. clone내부 상태를 포함하여 객체를 복제하는 데 사용되는 동안 dup일반적으로 하위 객체의 클래스를 사용하여 새 인스턴스를 만듭니다.

그러나 몇 가지 테스트를 할 때 실제로 동일한 것을 발견했습니다.

class Test
   attr_accessor :x
end

x = Test.new
x.x = 7
y = x.dup
z = x.clone
y.x => 7
z.x => 7

두 방법의 차이점은 무엇입니까?


29
나는에 단지 차이를하지 알고 싶어 무엇을 dup 하고 clone않지만, 당신은 다른 것보다 하나를 사용하십시오.
Andrew Grimm

1
여기 또한 좋은 링크입니다 - coderwall.com/p/1zflyg
Arup에 Rakshit

답변:


298

서브 클래스는 다른 의미를 제공하기 위해이 메소드를 대체 할 수 있습니다. 그 Object자체에는 두 가지 주요 차이점이 있습니다.

먼저 clone싱글 톤 클래스를 복사하지만 dup그렇지는 않습니다.

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42

둘째, clone정지 상태를 유지하지만 dup그렇지 않습니다.

class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # raises RuntimeError

이러한 방법에 대한 Rubinius 구현은 꽤 명확하고 공정하게 준수 루비 구현하기 때문에, 종종 이러한 질문에 대한 답변을 내 소스입니다.


15
누군가 이것을 다시 변경하려고 시도하는 경우 : Ruby에서 잘 정의 된 "singleton 클래스"에는 singleton 메소드 뿐만 아니라 singleton 클래스에 정의 된 상수도 포함됩니다. 고려하십시오 : o = Object.new; class << o; A=5; end; puts ( class << o.clone; A; end ); puts ( class << o.dup; A; end ).
Jeremy Roman

3
큰 답변과 그에 대한 큰 논평이 있었지만, 그 구문을 이해하기 위해 야생 거위 추적을 이끌었습니다. 이것은 혼동 될 수도있는 다른 사람들을 도울 것입니다 : devalot.com/articles/2008/09/ruby-singleton
davidpm4

1
"singleton 클래스"에는 extend원본 객체에 포함 된 모든 모듈이 포함되어 있다고 언급 할 가치가 있다고 생각 합니다. 따라서 Object.new.extend(Enumerable).dup.is_a?(Enumerable)false를 반환합니다.
다니엘

이 답변이 질문에 대답하고 차이점을 설명합니다. Object # dup 설명서에 명시된 바와 같이 두 가지 방법 모두 서로 다른 상황을위한 것 입니다. 복제 의 유스 케이스 는 오브젝트를 동일한 인스턴스 (다른 오브젝트 ID를 가짐) 로 사용하려는 의도로 오브젝트를 복제하는 반면, dup 은 오브젝트를 새 인스턴스의 기본으로 복제하기위한 것입니다.
3limin4t0r

189

ActiveRecord를 다룰 때도 큰 차이가 있습니다.

dup ID를 설정하지 않고 새 객체를 생성하므로 타격을 통해 데이터베이스에 새 객체를 저장할 수 있습니다 .save

category2 = category.dup
#=> #<Category id: nil, name: "Favorites"> 

clone 동일한 ID를 가진 새로운 객체를 생성하므로, 그 새로운 객체에 대한 모든 변경 사항은 타격시 원래 레코드를 덮어 씁니다. .save

category2 = category.clone
#=> #<Category id: 1, name: "Favorites">

43
이 답변은 IMO에게 가장 중요한 실질적인 정보를 제공하는 답변입니다. 다른 답변은 esoterica에 있습니다.
jpw

37
위의 내용은 ActiveRecord에만 해당됩니다. 표준 루비에서는 구별이 훨씬 더 미묘합니다.
ahmacleod

1
@Stefan 및 @jvalanen : 내가 객체 에 적용 dup하고 clone메소드를 사용할 ActiveRecord때 대답에서 언급 한 결과의 역 결과가 나타납니다. 즉 ,를 사용 하면 설정중인 dup새 객체가 생성 되고 설정 되지 않은 상태에서 객체가 생성 됩니다. 다시 살펴보고 정리해 주시겠습니까? . Thnxidcloneid
huzefa biyawarwala

Rails 5에서는 api.rubyonrails.org/classes/ActiveRecord/… 중 아무 것도 변경되지 않았습니다 . 여러분의 경우에는 특별한 것이 있다고 생각합니다.
jvalanen

그러나 clone저장되지 않은 새 레코드를 저장하면 꽤 안전해야합니다. 이 방법으로 "템플릿 개체"를 빌드하고 복제하여 특정 인스턴스를 저장할 수 있습니까?
Cyril Duchon-Doris

30

한 가지 차이점은 고정 된 개체와 다릅니다. clone(a 반면 고정 된 오브젝트들은 동결 된 dup냉동 개체는 아님).

class Test
  attr_accessor :x
end
x = Test.new
x.x = 7
x.freeze
y = x.dup
z = x.clone
y.x = 5 => 5
z.x = 5 => TypeError: can't modify frozen object

또 다른 차이점은 싱글 톤 방법입니다. 여기 같은 이야기 dup가 있지만 그것들을 복사하지는 clone않습니다.

def x.cool_method
  puts "Goodbye Space!"
end
y = x.dup
z = x.clone
y.cool_method => NoMethodError: undefined method `cool_method'
z.cool_method => Goodbye Space!

이것은 나에게 매우 유용했습니다. 고정 상수 값을 생성하여 github.com/rack/rack/blob/master/lib/rack/utils.rb#L248 (레일 쿠키 처리) 과 같은 값으로 전달하면 쉽게 오류가 발생할 수 있습니다. 그들이 당신에게 잘 모르면 복제하고 복제를 시도합니다. 고정 값을 지우고 그 값을 전달하면 랙을 손상시키지 않으면 서 실수로 상수를 수정하지 않도록 할 수 있습니다.
XP84

4

둘 다 거의 동일하지만 복제는 dup보다 하나 더 많은 일을합니다. 복제시 개체의 고정 상태도 복사됩니다. Dup에서는 항상 해동됩니다.

 f = 'Frozen'.freeze
  => "Frozen"
 f.frozen?
  => true 
 f.clone.frozen?
  => true
 f.dup.frozen?
  => false 

4

새로운 문서가 좋은 예를 포함한다 :

class Klass
  attr_accessor :str
end

module Foo
  def foo; 'foo'; end
end

s1 = Klass.new #=> #<Klass:0x401b3a38>
s1.extend(Foo) #=> #<Klass:0x401b3a38>
s1.foo #=> "foo"

s2 = s1.clone #=> #<Klass:0x401b3a38>
s2.foo #=> "foo"

s3 = s1.dup #=> #<Klass:0x401b3a38>
s3.foo #=> NoMethodError: undefined method `foo' for #<Klass:0x401b3a38>

0

clone을 사용하여 Ruby에서 프로토 타입 기반 프로그래밍을 수행 할 수 있습니다. Ruby의 Object 클래스는 clone 메소드와 dup 메소드를 모두 정의합니다. clone과 dup은 모두 복사중인 객체의 얕은 복사본을 생성합니다. 즉, 객체의 인스턴스 변수는 복사되지만 참조하는 객체는 복사되지 않습니다. 예를 보여 드리겠습니다.

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.color
 => "red"
orange = apple.clone
orange.color 
 => "red"
orange.color << ' orange'
 => "red orange" 
apple.color
 => "red orange"

위의 예에서 주황색 클론은 사과 객체의 상태 (즉, 인스턴스 변수)를 복사하지만 사과 객체가 다른 객체 (예 : 문자열 객체 색상)를 참조하는 경우 해당 참조는 복사되지 않습니다. 대신 사과와 오렌지는 모두 같은 물체를 참조합니다! 이 예에서 참조는 문자열 객체 'red'입니다. orange가 append 메소드 인 <<를 사용하여 기존 String 객체를 수정하면 문자열 객체가 'red orange'로 변경됩니다. 결과적으로 apple.color도 같은 String 객체를 가리 키기 때문에 변경됩니다.

부수적으로, 할당 연산자 =는 새로운 객체를 할당하여 참조를 파괴합니다. 데모는 다음과 같습니다.

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color = 'orange'
orange.color
=> 'orange'
apple.color
=> 'red'

위의 예에서 주황색 클론의 색상 인스턴스 메소드에 새로운 객체를 할당하면 더 이상 apple과 동일한 객체를 참조하지 않습니다. 따라서 사과의 색상 방법에 영향을주지 않고 주황색의 색상 방법을 수정할 수 있지만, 사과에서 다른 객체를 복제하면 해당 객체가 복사 된 인스턴스 변수에서 사과와 동일한 객체를 참조하게됩니다.

dup은 또한 복사중인 객체의 얕은 복사본을 생성하며, dup에 대해 위에서 설명한 것과 동일한 데모를 수행하면 정확히 동일한 방식으로 작동하는 것을 볼 수 있습니다. 그러나 clone과 dup에는 두 가지 주요 차이점이 있습니다. 첫째, 다른 사람들이 언급했듯이 클론은 고정 상태를 복사하고 복제는하지 않습니다. 이것은 무엇을 의미 하는가? 루비에서 '동결'이라는 용어는 불변의 난해한 용어로, 그 자체가 컴퓨터 과학에서 명명 된 용어이므로 무언가를 변경할 수 없습니다. 따라서 Ruby에서 고정 된 객체는 어떤 식 으로든 수정할 수 없습니다. 사실상 불변이다. 고정 된 객체를 수정하려고하면 Ruby는 RuntimeError 예외를 발생시킵니다. clone은 고정 상태를 복사하므로 복제 된 개체를 수정하려고하면 RuntimeError 예외가 발생합니다. 반대로 dup은 고정 상태를 복사하지 않기 때문에

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.frozen?
 => false 
apple.freeze
apple.frozen?
 => true 
apple.color = 'crimson'
RuntimeError: can't modify frozen Apple
apple.color << ' crimson' 
 => "red crimson" # we cannot modify the state of the object, but we can certainly modify objects it is referencing!
orange = apple.dup
orange.frozen?
 => false 
orange2 = apple.clone
orange2.frozen?
 => true 
orange.color = 'orange'
 => "orange" # we can modify the orange object since we used dup, which did not copy the frozen state
orange2.color = 'orange'
RuntimeError: can't modify frozen Apple # orange2 raises an exception since the frozen state was copied via clone

둘째, 더 흥미롭게도 클론은 싱글 톤 클래스 (및 그 메소드)를 복사합니다! Ruby에서 프로토 타입 기반 프로그래밍을 수행하려는 경우 매우 유용합니다. 먼저, 싱글 톤 메소드가 복제본으로 복사 된 다음 Ruby에서 프로토 타입 기반 프로그래밍의 예에 적용 할 수 있음을 보여 드리겠습니다.

class Fruit
  attr_accessor :origin
  def initialize
    @origin = :plant
  end
end

fruit = Fruit.new
 => #<Fruit:0x007fc9e2a49260 @origin=:plant> 
def fruit.seeded?
  true
end
2.4.1 :013 > fruit.singleton_methods
 => [:seeded?] 
apple = fruit.clone
 => #<Fruit:0x007fc9e2a19a10 @origin=:plant> 
apple.seeded?
 => true 

보다시피, fruit 객체 인스턴스의 싱글 톤 클래스는 클론으로 복사됩니다. 따라서 복제 된 객체는 singleton 방법 : seeded?에 액세스 할 수 있습니다. 그러나 이것은 dup의 경우가 아닙니다.

apple = fruit.dup
 => #<Fruit:0x007fdafe0c6558 @origin=:plant> 
apple.seeded?
=> NoMethodError: undefined method `seeded?'

이제 프로토 타입 기반 프로그래밍에는 다른 클래스를 확장 한 클래스가 없으며, 청사진 역할을하는 상위 클래스에서 메소드가 파생 된 클래스의 인스턴스를 작성합니다. 대신 기본 객체가 있고 메소드와 상태를 복사하여 객체에서 새 객체를 만듭니다 (물론 복제를 통해 얕은 복사를 수행하기 때문에 인스턴스 변수 참조가 JavaScript와 마찬가지로 공유됩니다) 프로토 타입). 그런 다음 복제 된 메소드의 세부 사항을 채워서 오브젝트의 상태를 채우거나 변경할 수 있습니다. 아래 예제에는 기본 과일 오브젝트가 있습니다. 모든 과일에는 씨앗이 있으므로 number_of_seeds 메서드를 만듭니다. 그러나 사과에는 씨앗이 하나 있으므로 복제본을 만들고 세부 사항을 채 웁니다. 이제 애플을 복제 할 때 메소드를 복제 할뿐만 아니라 상태를 복제했습니다! clone은 상태의 얕은 사본 (인스턴스 변수)을 수행합니다. 그 때문에 red_apple을 얻기 위해 apple을 복제하면 red_apple은 자동으로 1 개의 seed를 갖습니다! red_apple은 Apple에서 상속 된 객체로, 과일에서 상속 된 객체로 생각할 수 있습니다. 따라서 과일과 사과를 대문자로 표기 한 것입니다. 우리는 클래스와 객체의 구별을 복제품으로 제공하지 않았습니다.

Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
  @number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
  @number_of_seeds
end
 Apple = Fruit.clone
 => #<Object:0x007fb1d78165d8> 
Apple.number_of_seeds = 1
Apple.number_of_seeds
=> 1
red_apple = Apple.clone
 => #<Object:0x007fb1d892ac20 @number_of_seeds=1> 
red_apple.number_of_seeds
 => 1 

물론 protoype 기반 프로그래밍에서 생성자 메서드를 사용할 수 있습니다.

Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
  @number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
  @number_of_seeds
end
def Fruit.init(number_of_seeds)
  fruit_clone = clone
  fruit_clone.number_of_seeds = number_of_seeds
  fruit_clone
end
Apple = Fruit.init(1)
 => #<Object:0x007fcd2a137f78 @number_of_seeds=1> 
red_apple = Apple.clone
 => #<Object:0x007fcd2a1271c8 @number_of_seeds=1> 
red_apple.number_of_seeds
 => 1 

궁극적으로 clone을 사용하면 JavaScript 프로토 타입 동작과 유사한 것을 얻을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.