À propos des copies superficielles et profondes de Python / Ruby

À propos des copies superficielles et profondes de Python / Ruby

En Python et Ruby, contrairement à PHP, l'affectation n'est pas une copie et l'affectation signifie «créer une contrainte entre la cible et l'objet». Je l'ai laissé comme mémorandum pour approfondir ma compréhension de la signification de «créer une contrainte entre la cible et l'objet» et la signification de «immuable» et «mutable» basée sur l'échantillon. Ici, Python3.6.2 et Ruby2.4.1 ont été utilisés.

Substitution

Substitution entière

D'après les résultats de Python 3.6 et Ruby 2.4 ci-dessous, lorsque la variable ʻa est manipulée, l''id de ʻa change, et il est immuable (ne peut pas être changé) car il ne peut pas être changé pour l'objet entier 3`. Ici, pour des raisons de commodité, un tel objet est traité comme un objet immuable.

Pour Python 3.6

a = 3
print("id_3:%d, id_a:%d" % (id(3), id(a)))
b = a
print("id_b:%d" % id(b))
a += 1
print("id_3:%d, id_4:%d, id_a:%d, id_b:%d" % (id(3), id(4), id(a), id(b)))

Résultat de sortie


id_3:1623654960, id_a:1623654960
id_b:1623654960
id_3:1623654960, id_4:1623654992, id_a:1623654992, id_b:1623654960

Dans le cas de Python3.6, il y a des cas où «id» est différent même pour les entiers avec la même valeur comme indiqué ci-dessous.

class A:
    def get(self):
        print(id(99999999999999999999999999999))


class B:
    def get(self):
        print(id(99999999999999999999999999999))

A().get()
B().get()

Résultat de sortie


2812564670744
2812564671464

Pour Ruby 2.4

a = 3
puts 'id_3:%d, id_a:%d' % [3.object_id, a.object_id]
b = a
puts 'id_b:%d' % [b.object_id]
a += 1
puts 'id_3:%d, id_4:%d, id_a:%d, id_b:%d' % [3.object_id, 4.object_id, a.object_id, b.object_id]

Résultat de sortie


id_3:7, id_a:7
id_b:7
id_3:7, id_4:9, id_a:9, id_b:7

Même dans le cas de Ruby, il y a des cas où «id» est différent même pour les entiers avec la même valeur comme indiqué ci-dessous.

p 99999999999999999999999.object_id
p 99999999999999999999999.object_id

Résultat de sortie


25133964
25133856

Affectation du tableau (Ruby) et de la liste (Python)

D'après les résultats de Python 3.6 et Ruby 2.4 ci-dessous, même si la valeur est modifiée en opérant ʻa, c'est le même ʻid et peut être changé en une valeur différente. Autrement dit, il est mutable (modifiable). Ici, pour plus de commodité, un tel objet est traité comme un objet mutable. Lorsque la valeur de ʻa est assignée à b, ʻid est le même, donc si la valeur est modifiée en manipulant ʻa, b` change également.

Pour Python 3.6

a = [1,2,3]
b = a
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

a[1] = 4
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

Résultat de sortie


id_a:4332032456, id_b:4332032456
[1, 2, 3] [1, 2, 3]
id_a:4332032456, id_b:4332032456
[1, 4, 3] [1, 4, 3]

Pour Ruby 2.4

a = [1,2,3]
b = a
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

a[1] = 4
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

Résultat de sortie


id_a:70131497645860, id_b:70131497645860
[1, 2, 3]
[1, 2, 3]
id_a:70131497645860, id_b:70131497645860
[1, 4, 3]
[1, 4, 3]

Copie superficielle

Dans le cas d'un objet mutable, comme dans l'exemple ci-dessus, si la valeur de ʻa est assignée à b, l''id est le même, donc si la valeur est modifiée en manipulant ʻa, b ʻAussi change. Vous devez faire une copie pour rendre la valeur de «b» indépendante de «a». Dans le cas de la liste ou du tableau dans cet exemple, il peut être réalisé en faisant une copie superficielle. Dans l'exemple ci-dessous, en faisant une copie superficielle, la valeur de «b» ne change pas même si la valeur est modifiée par l'opération de «a».

Pour Python 3.6

Une copie superficielle peut être faite en remplaçant la fonction copy.copy ou ʻa [:]`.

import copy as cp

a = [1,2,3]
b = cp.copy(a)
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

a[1] = 4
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

b = a[:]
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

a[1] = 3
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print(a, b)

Résultat de sortie


id_a:4460690760, id_b:4460690888
[1, 2, 3] [1, 2, 3]
id_a:4460690760, id_b:4460690888
[1, 4, 3] [1, 2, 3]
id_a:4460690760, id_b:4460691272
[1, 4, 3] [1, 4, 3]
id_a:4460690760, id_b:4460691272
[1, 3, 3] [1, 4, 3]

Pour Ruby 2.4

Une copie superficielle peut être obtenue en utilisant ʻObject.clone ou ʻObject.dup. En outre, [ici](https://ja.stackoverflow.com/questions/27101/ruby%E3%81%AEclone%E3%81%A8dup%E3%81%A8activerecord%E3%81%AEclone%E3%81% A8dup% E3% 81% AF% E5% 88% A5% E7% 89% A9) Le comportement est différent de ʻActiveModel.clone et ʻActiveModel.dup de Rails.

a = [1,2,3]
b = a.clone
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

a[1] = 4
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

b = a.dup
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

a[1] = 3
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
p a, b

Résultat de sortie


id_a:70245067666580, id_b:70245067666520
[1, 2, 3]
[1, 2, 3]
id_a:70245067666580, id_b:70245067666520
[1, 4, 3]
[1, 2, 3]
id_a:70245067666580, id_b:70245067665580
[1, 4, 3]
[1, 4, 3]
id_a:70245067666580, id_b:70245067665580
[1, 3, 3]
[1, 4, 3]

Cas où la copie superficielle ne suffit pas

Par exemple, dans l'exemple suivant où il y a une liste dans l'élément de la liste et un tableau dans l'élément du tableau, c'est-à-dire qu'il y a un autre objet mutable de l'objet mutable, même si vous faites une copie superficielle, vous pouvez opérer ʻa [0] `. La valeur de «b [0]» est modifiée. Cela se comporte de la même manière pour Python 3.6 et Ruby 2.4.

Pour Python 3.6

import copy as cp

a = [[1,2,3], "abc"]
b = cp.copy(a)
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

a[0][1] = 4
a[1] = "def"
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

b = a[:]
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

a[0][1] = 3
a[1] = "abc"
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

Résultat de sortie


id_a:4393106888, id_b:4393107272
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4391239728, id_b[1]:4391239728
[[1, 2, 3], 'abc'] [[1, 2, 3], 'abc']
id_a:4393106888, id_b:4393107272
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4392739984, id_b[1]:4391239728
[[1, 4, 3], 'def'] [[1, 4, 3], 'abc']
id_a:4393106888, id_b:4393112648
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4392739984, id_b[1]:4392739984
[[1, 4, 3], 'def'] [[1, 4, 3], 'def']
id_a:4393106888, id_b:4393112648
id_a[0]:4393106760, id_b[0]:4393106760
id_a[1]:4391239728, id_b[1]:4392739984
[[1, 3, 3], 'abc'] [[1, 3, 3], 'def']

Pour Ruby 2.4

a = [[1,2,3], "abc"]
b = a.clone
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

a[0][1] = 4
a[1] = "def"
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

b = a.dup
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

a[0][1] = 3
a[1] = "abc"
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

Résultat de sortie


id_a:70199714913920, id_b:70199714913860
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714913940, id_b[1]:70199714913940
[[1, 2, 3], "abc"]
[[1, 2, 3], "abc"]
id_a:70199714913920, id_b:70199714913860
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714912900, id_b[1]:70199714913940
[[1, 4, 3], "def"]
[[1, 4, 3], "abc"]
id_a:70199714913920, id_b:70199714912200
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714912900, id_b[1]:70199714912900
[[1, 4, 3], "def"]
[[1, 4, 3], "def"]
id_a:70199714913920, id_b:70199714912200
id_a[0]:70199714913960, id_b[0]:70199714913960
id_a[1]:70199714911480, id_b[1]:70199714912900
[[1, 3, 3], "abc"]
[[1, 3, 3], "def"]

Copie profonde

Si un objet mutable contient un autre objet mutable, qui est une "copie superficielle n'est pas assez de cas", vous devez faire une copie profonde au lieu d'une copie superficielle pour copier complètement la valeur de b dans ʻa`. Il y a.

Pour Python 3.6

Une copie complète peut être réalisée avec copy.deepcopy.

import copy as cp

a = [[1,2,3], "abc"]
b = cp.deepcopy(a)
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

a[0][1] = 4
a[1] = "def"
print("id_a:%d, id_b:%d" % (id(a), id(b)))
print("id_a[0]:%d, id_b[0]:%d" % (id(a[0]), id(b[0])))
print("id_a[1]:%d, id_b[1]:%d" % (id(a[1]), id(b[1])))
print(a, b)

Résultat de sortie


id_a:4306767304, id_b:4306767688
id_a[0]:4306767176, id_b[0]:4306773064
id_a[1]:4304900144, id_b[1]:4304900144
[[1, 2, 3], 'abc'] [[1, 2, 3], 'abc']
id_a:4306767304, id_b:4306767688
id_a[0]:4306767176, id_b[0]:4306773064
id_a[1]:4306400400, id_b[1]:4304900144
[[1, 4, 3], 'def'] [[1, 2, 3], 'abc']

Pour Ruby 2.4

Une copie complète peut être réalisée avec «Marshal.load» et «Marshal.dump».

a = [[1,2,3], "abc"]
b = Marshal.load(Marshal.dump(a))
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

a[0][1] = 4
a[1] = "def"
puts 'id_a:%d, id_b:%d' % [a.object_id, b.object_id]
puts 'id_a[0]:%d, id_b[0]:%d' % [a[0].object_id, b[0].object_id]
puts 'id_a[1]:%d, id_b[1]:%d' % [a[1].object_id, b[1].object_id]
p a, b

Résultat de sortie


id_a:70274700700880, id_b:70274700700800
id_a[0]:70274700700940, id_b[0]:70274700700780
id_a[1]:70274700700900, id_b[1]:70274700700760
[[1, 2, 3], "abc"]
[[1, 2, 3], "abc"]
id_a:70274700700880, id_b:70274700700800
id_a[0]:70274700700940, id_b[0]:70274700700780
id_a[1]:70274700699900, id_b[1]:70274700700760
[[1, 4, 3], "def"]
[[1, 2, 3], "abc"]

Supplément

À propos des chaînes de caractères dans Ruby

Dans Ruby 2.4, la chaîne de caractères littérale devient un objet mutable comme indiqué ci-dessous. En ce qui concerne here, la chaîne littérale sera un objet immuable dans Ruby3. En Python3.6, il est nécessaire de convertir en un type de liste avec b = list (a) pour le gérer comme suit.

a = "abc"
puts 'id_a:%d' % [a.object_id]
a[0] = "A"
puts 'id_a:%d' % [a.object_id]

Résultat de sortie


id_a:70186456022160
id_a:70186456022160

À propos de l'affectation par référence PHP

Dans le cas de PHP, l'affectation correspond-elle à une copie profonde? Dans l'exemple ci-dessous, même si $ a = [5];, $ b fait référence à $ a, il s'agit donc de la valeur référencée par $ a. La substitution par référence telle que $ a = & $ b; en PHP ne peut pas être réalisée en Python ou Ruby.

<?php

$a = [3];
$b = &$a;
$a = [5];
print_r($a);
print_r($b);

Résultat de sortie


Array
(
    [0] => 5
)
Array
(
    [0] => 5
)

À propos des copies superficielles et profondes

Dans l'exemple ici, les listes et les tableaux sont pris comme exemples, mais la différence entre copie superficielle et copie profonde peut être discriminée même dans les cas suivants.

Pour Python 3.6

import copy


class Test1:
    def __init__(self):
        self.a = 0

    def set(self, n):
        self.a = n

    def get(self):
        print(self.a)


class Test2:
    def __init__(self, t):
        self.t1 = t

    def set(self, n):
        self.t1.set(n)

    def get(self):
        self.t1.get()


a = Test2(Test1())
b = copy.copy(a)
c = copy.deepcopy(a)

a.set(3)
b.set(5)
c.set(7)

a.get()
b.get()
c.get()

Résultat de sortie


5
5
7

Pour Ruby 2.4

class Test1
  def initialize
    @a = 0
  end

  def set(n)
    @a = n
  end

  def get()
    puts @a
  end
end

class Test2
  def initialize(t1)
    @t1 = t1
  end

  def set(n)
    @t1.set(n)
  end

  def get()
    @t1.get
  end
end

a = Test2.new(Test1.new)
b = a.clone
c = Marshal.load(Marshal.dump(a))

a.set(3)
b.set(5)
c.set(7)

a.get
b.get
c.get

Résultat de sortie


5
5
7

Recommended Posts

À propos des copies superficielles et profondes de Python / Ruby
Copie superficielle Python et copie profonde
Copie superficielle Python et copie profonde
Résumé de la correspondance entre les opérations de tableau ruby et python
Spécification de la plage des tableaux ruby et python
Ruby, Python et carte
Python et Ruby se séparent
Comparaison de Python et Ruby (Environment / Grammar / Literal Edition)
[Python] Chapitre 01-02 À propos de Python (Exécution et installation de l'environnement de développement)
Comparaison de CoffeeScript avec la grammaire JavaScript, Python et Ruby
Gestion des versions de Node, Ruby et Python avec anyenv
Python sur Ruby et Ruby en colère sur Python
À propos des objets et des classes Python
À propos des variables et des objets Python
Mémo tranche python et rubis
À propos de divers encodages de Python 3
À propos de Python, len () et randint ()
À propos de Perl, Python, PHP, Ruby
Syntaxe Ruby et Python ~ branch ~
À propos de Python et des expressions régulières
À propos des fonctionnalités de Python
Installation source et installation de Python
À propos des opérations Python et OS
Python # À propos de la référence et de la copie
À propos de Python sort () et reverse ()
Python vs Ruby "Deep Learning from scratch" Chapitre 1 Graphique de la fonction sin et de la fonction cos
Résumé de la prise en charge des opérations de hachage (dictionnaire) pour Ruby et Python
Construction d'environnement de python et opencv
Différence entre Ruby et Python Split
L'histoire de Python et l'histoire de NaN
Installer SciPy et matplotlib (Python)
Scraping avec Node, Ruby et Python
À propos de Python dict et des fonctions triées
À propos de Python et Cython dtype
À propos de Python Pickle (cPickle) et Marshal
[Python] À propos des classes Executor et Future
À propos de Python, à partir et à l'importation, comme
Ceci et cela des propriétés python
Coexistence de Python2 et 3 avec CircleCI (1.0)
À propos de la liste de base des bases de Python
Résumé des index et des tranches Python
Réputation des livres Python et des livres de référence
J'ai comparé la vitesse de Hash avec Topaz, Ruby et Python
Installation du code Visual Studio et installation de python
À propos de la création de l'interface graphique à l'aide de TKinter de Python
Différences entre Ruby et Python dans la portée
Extraction de tweet.js (json.loads et eval) (Python)
Signification des modèles et paramètres d'apprentissage en profondeur
À propos de l'environnement virtuel de Python version 3.7
À propos du sensor_mode et de l'angle de vue de la picamera
Mémorandum des débutants en python
Connectez beaucoup de Python ou et et
Crypter avec Ruby (Rails) et décrypter avec Python
Une histoire sur Python pop and append
Introduction facile de la série python3 et d'OpenCV3
[Python] Diverses combinaisons de chaînes de caractères et de valeurs
Scraping Web facile avec Python et Ruby
Note de problèmes sur la coexistence du système Python 2/3
[Python] Chapitre 02-04 Bases du programme Python (À propos des commentaires)