[PYTHON] "Tiefe Kopie" und "flache Kopie", um mit dem kleinsten Beispiel zu verstehen

Tiefe Kopie / flache Kopie ...

Ein Wort, das oft Verwirrung stiftet.

Hier möchte ich anhand des kleinsten Beispiels, in dem der Unterschied zwischen den einzelnen Kopiermethoden erkennbar ist, sehen, wie sich das Verhalten des Programms je nach Kopiermethode unterscheidet.

Haftungsausschluss

Es ist ein Haftungsausschluss. Wenn Sie nichts dagegen haben, fahren Sie mit dem Kapitel "Was machen Sie?" Weiter.

Ich benutze das Wort "Shared Delivery"

In diesem Artikel verwenden wir "Shared Passing", um auf die Methoden "Pass-by-Sharing", "Call-by-Sharing", "Shared Passing" und "Reference Passing by Value" zu verweisen.

Der Autor empfiehlt diesen Begriff nicht. Zum leichteren Vergleich mehrsprachiger Spezifikationen haben wir nur die in diesem Artikel verwendeten Namen angegeben.

Ich werde auch das Wort "○○ pass" im Falle einer Substitution verwenden

In diesem Artikel werden die Begriffe "Referenzübergabe" und "Weitergabe über Freigabe" auch zur Beschreibung von "Methoden zur Variablenzuweisung" verwendet.

Normalerweise ist es ein Wort, das verwendet wird, wenn ein Argument an eine Funktion übergeben wird. Da jedoch dasselbe Konzept für die Zuweisung gilt, verwenden wir das Wort so, wie es ist.

(Dies ist meine persönliche Meinung, aber ich bin mir nicht sicher, ob es sinnvoll ist, das Wort "pass XX" nur zu verwenden, wenn Argumente an eine Funktion übergeben werden. Ich denke, es ist in Ordnung, es für die Zuweisung zu verwenden, also werde ich das in diesem Artikel tun. Ich werde.)

Diejenigen, die keine neue Entität generieren, werden nicht als "flache Kopien" bezeichnet.

Wenn ich suche, erhalte ich viele Artikel mit der Aufschrift "Flache Kopie ist eine Methode, bei der nur eine Referenz oder ein Zeiger kopiert und keine neue Entität erstellt wird".

In diesem Artikel wird der Fall, in dem "eine neue Entität erstellt wird, sich jedoch irgendwo im Inhalt auf dieselbe Entität wie vor dem Kopieren bezieht", als "flache Kopie" bezeichnet.

Englische Version von Wikipedia und Offizielle Python-Dokumentation Aber das ist der Fall.

(Dies ist meine persönliche Meinung, aber zumindest dieser Fall "Erstellen einer neuen Entität, aber Verweisen auf die Kopierquelle" sollte als "flache Kopie" bezeichnet werden, sodass "flache Kopie neu ist". Ich denke, die Erklärung, dass "es keine Entität erstellt", ist falsch. Wenn "ein Objekt, das keine neue Entität erstellt **, auch als" flache Kopie "bezeichnet wird, gibt es keinen Widerspruch, aber ich denke, dass dies auch falsch ist. )

Was machst du?

Ersetzen Sie eine "Kopie in gewissem Sinne" der Variablen "a" durch die Variable "b", spielen Sie mit dem "b" und überprüfen Sie dann den Inhalt des "a".

Zu dieser Zeit gab es nicht nur zwei Arten von Kopiermethoden

Vergleichen wir die vier.

Die Bedeutung jedes Begriffs wird zusammen erläutert, wenn der Code unten erläutert wird.

Was tun mit dem kleinsten Beispiel?

assignment.dart


void main() {
  var a = [
    ["did deep copy"]
  ];

  //Hier ist der Prozess des Ersetzens einer Kopie von a in gewissem Sinne für b

  b[0][0] = "did shallow copy";
  b[0] = ["did pass-by-sharing"];
  b = [
    ["did pass-by-reference"]
  ];
  print(a[0][0]);
}

Ich wollte nur den Verarbeitungsinhalt vorstellen, und ich wollte nicht abhängig von einer bestimmten Sprache darüber sprechen, also schrieb ich ihn in Dart, das anscheinend von wenigen Leuten verwendet wird. (Kürzlich erhöht ...?)

Der Verarbeitungsinhalt ist

  1. Weisen Sie der Variablen a eine Liste von Zeichenfolgen zu.
  2. Ersetzen Sie die Variable "b" in gewissem Sinne durch eine Kopie von "a".
  3. Ersetzen Sie das 0. Element des 0. Elements von b durch eine andere Zeichenfolge.
  4. Ersetzen Sie das 0. Element von b durch eine Liste verschiedener Zeichenfolgen.
  5. Ersetzen Sie "b" durch eine Liste verschiedener Zeichenfolgen.
  6. Geben Sie das 0. Element des 0. Elements von a aus.

Mal sehen, warum das einen Unterschied macht.

Bei der Übergabe als Referenz

Wenn die Zuweisung "b = a" als Referenz übergeben wird, hat "b" eine "Referenz", die auf "a" zeigt, und jede nachfolgende Verarbeitung auf "b" wirkt sich auch auf "a" aus. Ich werde.

Es ist einfacher, die Bewegung zu verstehen, wenn Sie sich vorstellen, dass "a" den Alias "b" erhält.

In diesem Fall funktioniert der Code folgendermaßen.

assignment.dart


void main() {
  var a = [
    ["did deep copy"]
  ];

  //Übergeben Sie nun a unter Bezugnahme auf b

  //Alle folgenden Verarbeitungen entsprechen denen für a
  b[0][0] = "did shallow copy";
  b[0] = ["did pass-by-sharing"];
  b = [
    ["did pass-by-reference"]
  ];
  print(a[0][0]); // did pass-by-reference
}

Letzte

  b = [
    ["did pass-by-reference"]
  ];

Der Inhalt von "a" ist eine Liste von Listen mit dieser Zeichenfolge.

Daher wird die Ausgabe "Referenzübergabe" sein.

Diese Zeichenfolge bedeutet übrigens "als Referenz übergeben".

Bei geteilter Lieferung

Genau genommen ist Shared Delivery nicht nur ein Begriff, der sich auf das Bestehen bezieht.

In einer gemeinsam genutzten Sprache ist der Inhalt einer Variablen nicht der Wert selbst, sondern ein Verweis auf diesen Wert. (Dies gilt für viele Sprachen wie Java, Python, JavaScript.)

Wenn dann "b = a" gesetzt ist, wird die in "a" gespeicherte "Referenz" kopiert und auch in "b" gespeichert.

Aus diesem Grund wird Shared Passing manchmal als "Passing by Reference" bezeichnet.

In diesem Fall verhält sich der obige Code wie folgt.

assignment.dart


void main() {
  //a hat einen Verweis auf dieses Doppelarray
  var a = [
    ["did deep copy"]
  ];

  //Teilen Sie hier a bis b

  //Dieser Prozess wirkt sich auf a aus, da a und b Verweise haben, die auf dieselbe Entität verweisen.
  b[0][0] = "did shallow copy";

  //Dieser Prozess betrifft auch a
  b[0] = ["did pass-by-sharing"];

  //Hier speichert b eine neue Referenz, die auf die neue Entität verweist, sodass dieser Prozess a nicht beeinflusst.
  b = [
    ["did pass-by-reference"]
  ];
  print(a[0][0]); // did pass-by-sharing
}

Letzte

  b = [
    ["did pass-by-reference"]
  ];

Hat "b" "[[" hat Pass-by-Reference "]]" "zugewiesen". Dies ist eine Referenz, die auf eine andere Entität als zuvor verweist, sodass dieser Prozess "a" beeinflusst. Nein.

Daher wird die Ausgabe "Pass-by-Sharing" ausgeführt.

Diese Zeichenfolge bedeutet übrigens "geteilt und übergeben".

Für flache Kopie

Wir sprechen weiterhin über Sprachen, in denen der Inhalt einer Variablen nicht der Wert selbst ist, sondern ein Verweis auf diesen Wert. (Java, Python, JavaScript usw.)

In Sprachen, in denen der Inhalt der Variablen der Wert selbst ist (z. B. C ++), gibt es keine flache Kopie. (Ich denke, es kann mit Zeigern reproduziert und als Referenz übergeben werden)

Eine flache Kopie macht, wie der Name schon sagt, eine Kopie.

Wenn der Inhalt "Referenz" ist, kopieren Sie ihn so wie er ist.

Mal sehen, was das im Code bedeutet.

assignment.dart


void main() {
  //a hat einen Verweis auf dieses Doppelarray
  var a = [
    ["did deep copy"]
  ];

  //Erstellen Sie nun eine flache Kopie von a und weisen Sie sie b zu

  //a und b haben Verweise, die auf verschiedene Entitäten verweisen.
  //Beide Entitäten haben jedoch die gleiche "Referenz" im 0. Element, also
  //Dieser Prozess betrifft a
  b[0][0] = "did shallow copy";

  //Das 0. Element von b wird als Referenz umgeschrieben, die auf etwas anderes verweist.
  //Dieser Vorgang wirkt sich nicht auf a aus.
  b[0] = ["did pass-by-sharing"];

  //Hier speichert b eine neue Referenz, die auf die neue Entität verweist, sodass dieser Prozess auch a nicht beeinflusst.
  b = [
    ["did pass-by-reference"]
  ];
  print(a[0][0]); // did shallow copy
}

a und seine flache Kopie sind tatsächlich unterschiedlich. Wenn Sie "id" in Python oder "Hashcode" in Dart ausgeben und überprüfen, können Sie sehen, dass "a" und "b" auf verschiedene Entitäten verweisen.

Es werden jedoch dieselben Inhalte kopiert.

In diesem Fall wurde eine "Referenz" kopiert, die auf dasselbe verweist.

Wenn Sie also den Inhalt des "Referenzziels des Inhalts" neu schreiben, sind sowohl "a" als auch "b" betroffen.

Es ist das.

  b[0][0] = "did shallow copy";

Wenn Sie dagegen den "Inhalt von" b "selbst neu schreiben, hat dies keine Auswirkungen auf" a ".

das ist

  b[0] = ["did pass-by-sharing"];

ist. Dieser Vorgang wirkt sich nicht auf "a" aus. Das Ergebnis ist eine "flache Kopie".

Zum tiefen Kopieren

Wie eine flache Kopie macht eine tiefe Kopie eine Kopie, aber

Untersuchen Sie den referenzierten Wert im Inhalt und kopieren Sie ihn ebenfalls.

Wenn es sich auch um eine "Referenz" handelt, überprüfen Sie den Wert der Referenz und kopieren Sie sie ebenfalls.

Wiederholen Sie diesen Vorgang, bis Sie alle Referenzen kopiert haben.

a und b teilen nichts mehr.

Operationen, die an einem ausgeführt werden, wirken sich überhaupt nicht auf den anderen aus.

assignment.dart


void main() {
  //a hat einen Verweis auf dieses Doppelarray
  var a = [
    ["did deep copy"]
  ];

  //Übergeben Sie nun eine tiefe Kopie von a an b. Danach wirkt sich die Operation zu b nicht auf a aus.

  b[0][0] = "did shallow copy";
  b[0] = ["did pass-by-sharing"];
  b = [
    ["did pass-by-reference"]
  ];
  print(a[0][0]); // did deep copy
}

Da "a" nichts geändert hat, wird der Anfangswert "did deep copy" ausgegeben.

Mal sehen mit einem Beispiel

Mal sehen, wie sie in einigen Sprachen tatsächlich zugeordnet sind! !! !!

JavaScript

JavaScript wird bei normaler Zuweisung freigegeben.

assignment.js



a = [["did deep copy"]];

b = a;

b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [["did  pass-by-reference"]];
console.log(a[0][0]); // did pass-by-sharing

Wenn Sie die Entität nicht freigeben möchten, wenn es sich um ein Array handelt, können Sie mit "Slice" eine Kopie erstellen und diese haben.

assignment.js


a = [["did deep copy"]];

b = a.slice(0, a.length);

b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [["did pass-by-reference"]];
console.log(a[0][0]); // did shallow copy

Die Kopie ist in diesem Fall eine flache Kopie. Wenn Sie eine tiefe Kopie erstellen möchten, müssen Sie diese erstellen.

Auch wenn es sich um ein Objekt anstelle eines Arrays handelt, wird es freigegeben, wenn Sie es normal zuweisen. Wenn Sie es kopieren möchten, können Sie wie folgt vorgehen.

assignment.js


a = { x: { y: "did deep copy" } };

b = Object.assign({}, a); //Hier b=Wenn a festgelegt ist, wird es freigegeben

b.x.y = "did shallow copy";
b.x = { y: "did pass-by-sharing" };
b = { x: { y: "did pass-by-reference" } };
console.log(a.x.y); // did shallow copy

Python

Als nächstes kommt Python.

assignment.py


import copy

a = [['did deep copy']]

b = a

b[0][0] = 'did shallow copy'
b[0] = ['did pass-by-sharing']
b = [['did pass-by-reference']]
print(a[0][0]) # did pass-by-sharing

Selbst in Python wird es freigegeben, wenn Sie es normal zuweisen.

Python verfügt über ein Modul namens "copy", mit dem Sie explizit flache oder tiefe Kopien erstellen können.

Klicken Sie hier für die Dokumentation Kopieren --- flache und tiefe Kopiervorgänge

Sie können eine flache Kopie mit copy.copy erstellen.

assignment.py


import copy

a = [['did deep copy']]

b = copy.copy(a)

b[0][0] = 'did shallow copy'
b[0] = ['did pass-by-sharing']
b = [['did pass-by-reference']]
print(a[0][0])  # did shallow copy

Sie können eine tiefe Kopie mit copy.deepcopy erstellen.

assignment.py


import copy

a = [['did deep copy']]

b = copy.deepcopy(a)

b[0][0] = 'did shallow copy'
b[0] = ['did pass-by-sharing']
b = [['did pass-by-reference']]
print(a[0][0])  # did deep copy

Objekte können auch in diesem Modul kopiert werden. Es ist bequem.

Dart

assignment.dart


void main() {
  var a = [
    ["did deep copy"]
  ];

  var b = a;

  b[0][0] = "did shallow copy";
  b[0] = ["did pass-by-sharing"];
  b = [
    ["did pass-by-reference"]
  ];
  print(a[0][0]); // did pass-by-sharing
}

Wenn Dart auch normal zugewiesen wird, wird es geteilt.

C++

C ++ verhält sich ganz anders als die bisherigen Sprachen.

C ++ wird bei normaler Zuweisung nicht freigegeben.

In C ++ ist der Inhalt einer Variablen keine "Referenz", sondern ein "Wert selbst".

Da es so kopiert wird, wie es ist, besteht zu diesem Zeitpunkt keine Beziehung zur Kopierquelle.

Unabhängig davon, wie Sie es kopieren und erstellen, hat dies keine Auswirkungen auf die Kopierquelle.

Es verhält sich genau wie ** Deep Copy **.

assignment.cpp


#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<vector<string>> a{vector<string>{"did deep copy"}};

    vector<vector<string>> b = a;

    b[0][0] = "did shallow copy";
    b[0] = vector<string>{"did pass-by-sharing"};
    b = vector<vector<string>>{vector<string>{"did pass-by-reference"}};
    cout << a[0][0] << endl; // did deep copy
}

Sie können auch als Referenz in C ++ übergeben.

Bei der Übergabe als Referenz wirken sich alle Vorgänge, die durch Kopieren und Erstellen ausgeführt werden, auf die Kopierquelle aus.

assignment.cpp


#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<vector<string>> a{vector<string>{"did deep copy"}};

    vector<vector<string>> &b = a;

    b[0][0] = "did shallow copy";
    b[0] = vector<string>{"did pass-by-sharing"};
    b = vector<vector<string>>{vector<string>{"did pass-by-reference"}};
    cout << a[0][0] << endl; // did pass-by-reference
}

Ende

Wie wir bisher gesehen haben, können Sie beim Schreiben dieses Prozesses klarstellen, was zum Zeitpunkt der Zuweisung zugewiesen wird.

Wenn Sie das wissen, sollte es weniger wahrscheinlich sein, dass Sie von unerwartetem Verhalten gestört werden.

Obwohl es sich um einen ähnlichen Artikel handelt, gibt es einen Artikel, der das Verständnis fördert, indem er das Verhalten beim Übergeben eines Arguments an eine Funktion und das Verhalten beim Zuweisen vergleicht. Lesen Sie ihn daher bitte, wenn Sie möchten.

Einfaches Verständnis der Übergabe von Werten, Übergeben von Teilen und Übergeben von Verweisen anhand des Mindestbeispiels (5 Zeilen)

Wenn Sie in diesem Artikel einen Fehler machen, wäre ich Ihnen sehr dankbar, wenn Sie darauf hinweisen könnten! Vielen Dank.

Recommended Posts

"Tiefe Kopie" und "flache Kopie", um mit dem kleinsten Beispiel zu verstehen
Flache Python-Kopie und tiefe Kopie
Flache Python-Kopie und tiefe Kopie
Künstliche Intelligenz, maschinelles Lernen, tiefes Lernen zu implementieren und zu verstehen
Ich habe das Toho-Projekt mit Deep Learning aufgenommen ... ich wollte.
Wiederholen Sie mit While. Skript zum Twittern oder Suchen vom Terminal aus
[Statistik] Visualisieren und verstehen Sie die Hamiltonsche Monte-Carlo-Methode mit Animation.
[Erforderliches Thema DI] Implementieren und verstehen Sie den Mechanismus von DI mit Go
Die stärkste Möglichkeit, MeCab und CaboCha mit Google Colab zu verwenden
Verbesserung der Wiederverwendbarkeit und Wartbarkeit von mit Luigi erstellten Workflows
Der Versuch, Segmentbäume Schritt für Schritt zu implementieren und zu verstehen (Python)
Setzen Sie Cabocha 0.68 in Windows ein und versuchen Sie, die Abhängigkeit mit Python zu analysieren
Geben Sie den Browser an, der mit Jupyter Notebook verwendet werden soll. Besonders Mac. (Und Vivaldi)
Stellen Sie mit Ihrem Smartphone eine Verbindung zum VPN her und schalten Sie den Server aus / ein
Ich versuchte, Trauer und Freude über das Problem der stabilen Ehe auszudrücken.
So ermitteln Sie mit Python den Unterschied zwischen Datum und Uhrzeit in Sekunden
Suchen Sie mit tweepy nach Twitter-Keywords und schreiben Sie die Ergebnisse in Excel
Konvertieren Sie eine Tabelle in CSV und laden Sie sie mit Cloud-Funktionen in den Cloud-Speicher hoch
Ich habe versucht, den Winkel von Sin und Cos mit Chainer zu lernen
Ich habe versucht, die Netzwerkbandbreite und -verzögerung mit dem Befehl tc zu steuern
Versuchen Sie, den Hintergrund und das sich bewegende Objekt des Videos mit OpenCV zu trennen
Notieren Sie die Schritte zum Verständnis des maschinellen Lernens
Einführung in Deep Learning ~ Falten und Pooling ~
Fraktal zum Erstellen und Spielen mit Python
Beispielskript zum Anzeigen von BoundingBox mit PIL
Verstehen Sie den Entscheidungsbaum und klassifizieren Sie Dokumente
Kopieren Sie den Consumer-Offset mit kafka-python auf einen anderen
Richten Sie die Farbleiste mit matplotlib an der Figur aus
Ich wollte unbedingt mit Selen kopieren
Der Weg zum Kompilieren zu Python 3 mit Thrift
So starten Sie das Python-Projekt im Jahr 2020 (Windows WSL und Mac Common)
So übergeben Sie den Pfad zu der mit pyenv und virtualenv mit PyCharm erstellten Bibliothek
Hinweise zum Implementieren des Schlüssels unter Amazon S3 mit Boto 3, Implementierungsbeispiel, Hinweise
Deep Learning von Grund auf neu Die Theorie und Implementierung des mit Python erlernten Deep Learning Kapitel 3
Verwenden Sie den Befehl [shell], um eine beliebige Datei zu komprimieren, um eine Datei zu erstellen und die Originaldatei zu löschen.
Geben Sie die Start- und Endpositionen der Dateien an, die in qiitap enthalten sein sollen
Erstellen Sie eine Python-Umgebung, um die Theorie und Implementierung von Deep Learning zu erlernen
[Einführung in AWS] Ich habe versucht, eine Konversations-App zu portieren und mit text2speech @ AWS playing zu spielen