it-swarm.com.de

Was ist der Unterschied zwischen Rubys Dup- und Clone-Methoden?

Die Ruby-Dokumente für dup sagen:

Im Allgemeinen können clone und dup in absteigenden Klassen unterschiedliche Semantik haben. Während clone zum Duplizieren eines Objekts einschließlich seines internen Status verwendet wird, verwendet dup normalerweise die Klasse des abgeleiteten Objekts, um die neue Instanz zu erstellen.

Aber wenn ich einige Tests mache, habe ich festgestellt, dass sie tatsächlich gleich sind:

class Test
   attr_accessor :x
end

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

Was sind die Unterschiede zwischen den beiden Methoden?

205
cali-1337500

Unterklassen können diese Methoden überschreiben, um unterschiedliche Semantiken bereitzustellen. In Object selbst gibt es zwei Hauptunterschiede.

Zuerst kopiert clone die Singleton-Klasse, während dup dies nicht tut.

o = Object.new
def o.foo
  42
end

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

Zweitens behält clone den eingefrorenen Zustand bei, dup jedoch nicht.

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

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

Die Rubinius-Implementierung für diese Methoden Ist häufig meine Quelle für Antworten auf diese Fragen, da sie ziemlich klar ist und eine ziemlich konforme Ruby-Implementierung darstellt.

288
Jeremy Roman

Beim Umgang mit ActiveRecord gibt es auch einen wesentlichen Unterschied:

dup erstellt ein neues Objekt, ohne dass dessen ID festgelegt ist. Sie können ein neues Objekt in der Datenbank speichern, indem Sie .save drücken.

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

clone erstellt ein neues Objekt mit derselben ID, sodass alle an diesem neuen Objekt vorgenommenen Änderungen den ursprünglichen Datensatz überschreiben, wenn Sie .save drücken.

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

Ein Unterschied besteht bei gefrorenen Objekten. Die clone eines eingefrorenen Objekts wird ebenfalls eingefroren (wohingegen eine dup eines eingefrorenen Objekts nicht) ist.

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

Ein weiterer Unterschied besteht bei Singleton-Methoden. Dieselbe Geschichte hier, dup kopiert diese nicht, clone jedoch.

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!
30

Beide sind fast identisch, aber Klon macht mehr als dup. Beim Klon wird auch der eingefrorene Zustand des Objekts kopiert. In Dup wird es immer aufgetaut.

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

Das neuere doc enthält ein gutes Beispiel:

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>
4
Xavier Nayrac

Sie können clone verwenden, um in Ruby prototypenbasierte Programmierung durchzuführen. Die Object-Klasse von Ruby definiert sowohl die Klonmethode als auch die Dup-Methode. Sowohl clone als auch dup erzeugen eine flache Kopie des zu kopierenden Objekts. Das heißt, die Instanzvariablen des Objekts werden kopiert, nicht jedoch die Objekte, auf die sie verweisen. Ich werde ein Beispiel zeigen:

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"

Beachten Sie im obigen Beispiel, dass der orangefarbene Klon den Status (dh die Instanzvariablen) des Apple-Objekts kopiert. Wenn das Apple-Objekt jedoch auf andere Objekte verweist (z. B. die String-Objektfarbe), werden diese Verweise nicht kopiert. Stattdessen beziehen sich Apple und Orange auf dasselbe Objekt! In unserem Beispiel ist die Referenz das Zeichenkettenobjekt 'rot'. Wenn Orange die Append-Methode << verwendet, um das vorhandene String-Objekt zu ändern, ändert sich das String-Objekt in 'Rot-Orange'. Dadurch wird auch Apple.color geändert, da beide auf dasselbe String-Objekt zeigen.

Als Nebenbemerkung weist der Zuweisungsoperator = ein neues Objekt zu und zerstört somit eine Referenz. Hier ist eine Demonstration:

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'

Wenn wir im obigen Beispiel ein neues neues Objekt der Farbinstanzmethode des orangefarbenen Klons zugewiesen haben, verweist es nicht mehr auf dasselbe Objekt wie Apple. Daher können wir jetzt die Farbmethode von Orange ändern, ohne die Farbmethode von Apple zu beeinflussen. Wenn Sie jedoch ein anderes Objekt von Apple klonen, verweist dieses neue Objekt auf die gleichen Objekte in kopierten Instanzvariablen wie Apple.

dup erstellt auch eine flache Kopie des Objekts, das kopiert werden soll. Wenn Sie die oben gezeigte Demonstration für dup ausführen, werden Sie feststellen, dass es genauso funktioniert. Es gibt jedoch zwei Hauptunterschiede zwischen Klon und Dup. Wie andere bereits erwähnt, kopiert der Klon zunächst den eingefrorenen Zustand und dup nicht. Was bedeutet das? Der Begriff "eingefroren" in Ruby ist ein esoterischer Begriff für unveränderlich, der selbst eine Nomenklatur in der Informatik ist, was bedeutet, dass etwas nicht geändert werden kann. Ein eingefrorenes Objekt in Ruby kann daher nicht modifiziert werden. es ist tatsächlich unveränderlich. Wenn Sie versuchen, ein eingefrorenes Objekt zu ändern, löst Ruby eine RuntimeError-Ausnahme aus. Da Klon den eingefrorenen Status kopiert, wird beim Versuch, ein geklontes Objekt zu ändern, eine RuntimeError-Ausnahme ausgelöst. Umgekehrt tritt keine solche Ausnahme auf, da dup den eingefrorenen Status nicht kopiert.

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

Zweitens, und noch interessanter, kopiert der Klon die Singleton-Klasse (und damit ihre Methoden)! Dies ist sehr nützlich, wenn Sie in Ruby eine Prototyp-basierte Programmierung durchführen möchten. Lassen Sie uns zunächst zeigen, dass die Singleton-Methoden tatsächlich mit clone kopiert werden. Anschließend können wir sie in einem Beispiel der prototypenbasierten Programmierung in Ruby anwenden. 

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 

Wie Sie sehen, wird die Singleton-Klasse der Fruchtobjektinstanz in den Klon kopiert. Daher hat das geklonte Objekt Zugriff auf die Singleton-Methode: seeded ?. Dies ist bei dup jedoch nicht der Fall:

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

In der prototypenbasierten Programmierung haben Sie jetzt keine Klassen, die andere Klassen erweitern und dann Instanzen von Klassen erstellen, deren Methoden von einer übergeordneten Klasse stammen, die als Blaupause dient. Stattdessen haben Sie ein Basisobjekt und erstellen dann aus dem Objekt ein neues Objekt, dessen Methoden und Status kopiert werden. Da wir nun durch Kopieren nur oberflächliche Kopien erstellen, werden alle Objekte, auf die sich die Instanzvariablen beziehen, genau wie in JavaScript gemeinsam genutzt Prototypen). Sie können dann den Status des Objekts eingeben oder ändern, indem Sie die Details der geklonten Methoden eingeben. Im folgenden Beispiel haben wir ein Basisobstobjekt. Alle Früchte haben Samen, daher erstellen wir eine Methode number_of_seeds. Aber Äpfel haben einen Samen und so erstellen wir einen Klon und geben die Details ein. Wenn wir jetzt Apple klonen, haben wir nicht nur die Methoden geklont, sondern auch den Zustand! Denken Sie daran, dass clone eine flache Kopie des Zustands (Instanzvariablen) ausführt. Wenn wir also Apple klonen, um eine red_Apple zu erhalten, hat red_Apple automatisch 1 Samen! Sie können sich red_Apple als ein Objekt vorstellen, das von Apple erbt, das wiederum von Fruit erbt. Aus diesem Grund habe ich Fruit und Apple groß geschrieben. Dank Klone haben wir die Unterscheidung zwischen Klassen und Objekten aufgehoben.

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 

Natürlich können wir eine Konstruktormethode für die Programmierung von Prototypen verwenden:

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 

Letztendlich können Sie mit clone etwas Ähnliches wie das Verhalten des JavaScript-Prototyps erhalten.

0
Donato