J'ai rencontré quelques problèmes cette semaine, et puis je me suis rendu compte que si les connaissances de base n'étaient pas consolidées depuis un moment, il y avait encore des parties oubliées qui devaient encore être révisées, je ferai une note ici pour l'enregistrer
Précédemment
Décrivez d'abord brièvement le problème rencontré. L'exigence est d'écrire les résultats de 2 impressions
As. vous pouvez voir, a pointe vers un objet liste, en Python, une telle instruction d'affectation signifie en fait que a pointe vers l'adresse mémoire de la liste, ce qui peut être considéré comme un concept de type pointeur.
Et b, notez qu'il enveloppe l'objet a dans une liste et le multiplie par 5, donc b devrait ressembler à une grande liste avec tous les éléments a
Quand a Après l'objet. est ajouté, la signification implicite est que la liste en mémoire a été modifiée, et tous les objets qui font référence à cet objet vont changer
J'imprime l'identifiant de a, Et, en même temps, j'imprime l'identifiant de l'élément a contenu dans l'objet b, afin que vous puissiez voir que dans la liste b, l'identifiant de chaque élément est le même que a
On peut voir que le L'identifiant (adresse mémoire) de l'objet a est 10892296. Bien que b enveloppe a dans une nouvelle liste, cet élément fait toujours référence à l'objet avec la même adresse. Cela peut être expliqué par la figure suivante
<.>Après cela, nous avons effectué l'opération d'ajout sur a. Puisque la liste est un objet variable, son adresse mémoire n'a pas changé. Cependant, pour cette adresse dans la mémoire, tous les objets référencés seront modifiés ensemble, ce qui peut être vu. à partir de la moitié inférieure de la ligne de démarcation dans le graphique de test ci-dessus
Cela conduit à un examen du mécanisme de référence de Python et de la copie superficielle et de la copie profonde
Mécanisme de référence de Python
Cas 1 du mécanisme de référence
À partir de l'exemple ci-dessus, nous pouvons voir que python En passant la référence, le résultat final est d'avoir deux objets faire référence au contenu de la même zone dans la mémoire
Regardons donc l'exemple suivant
B passe A, et fait également référence à l'identifiant 17446024 Le contenu de l'adresse et l'identifiant (adresse mémoire) des deux sont exactement les mêmes
Ainsi, grâce à l'opération de A A[0]=3 ou A[3].append(6), il modifiera le contenu de cette mémoire (car la liste est un objet variable, l'adresse mémoire ne changera pas, nous en reparlerons plus tard)
C'est le cas de référence le plus basique (en plus, car A et B pointent tous les deux vers la même adresse mémoire, donc le contenu modifié par B peut également se refléter sur A)
Cas du mécanisme de référence 2
Regardons un autre cas
En regardant la question, il semble que l'élément 2 sera remplacé par la liste elle-même. Le résultat peut être A=[1,[1,2,3],3]
Mais c'est le cas. pas! ! Vous pouvez voir qu'il y en a une infinité
imbriquées dans la case rouge. Pourquoi ?
En fait, c'est parce que A pointe vers la liste [1,2,3]. Dans cet exemple, seul le deuxième élément de A pointe vers l'objet A lui-même, ce n'est donc que le deuxième élément de R. La structure a changé ! Cependant, A pointe toujours vers cet objet
On peut voir en imprimant l'identifiant de A que son pointage n'a pas changé ! !
Regardez, la direction de A n'a pas changé
Alors si nous voulons obtenir l'effet de sortie final est [ 1, [1,2,3],3], comment devons-nous le faire fonctionner ?
Ici, nous allons utiliser une copie superficielle. L'utilisation peut être la suivante
Copie superficielle et copie profonde
Copie superficielle
Parlons maintenant de la copie superficielle et de la copie profonde. La méthode ci-dessus n'effectue en fait qu'une copie superficielle, une copie superficielle, ce qui signifie qu'elle copie l'objet référencé d'origine, mais ne fait plus référence à la même adresse d'objet
Regardez l'exemple ci-dessous. B effectue une copie superficielle via l'opération B = A[:] Vous pouvez voir qu'après la copie superficielle, les adresses mémoire référencées par A et B sont déjà différentes
Cependant, le les adresses de référence des éléments à l’intérieur de A et B sont toujours les mêmes, alors soyez très prudent à ce sujet ! Il y a une différence ! ! !
La différence entre les adresses mémoire de référence de A et B signifie que l'opération que vous effectuez sur B n'affectera pas A.
Résumé de la copie superficielle :
La copie superficielle peut donc être résumée comme la copie d'une référence, du nouvel objet et de l'original L'objet les références sont distinguées, mais les références d'adresse des éléments internes sont toujours les mêmes
Mais une copie superficielle posera également des problèmes. C'est lorsqu'il y a imbrication. Par exemple, comme vous pouvez le voir dans la situation suivante, j'ai attribué une copie superficielle de A à B, de sorte que les ID (adresses mémoire) de A et B soient différents.
Donc, quand je modifie A[0]=8, B ne sera pas affecté, car A et B sont des références indépendantes, mais il y a une liste imbriquée au milieu [4 ,5,6]
Nous pouvons comprendre cela [4,5,6] comme : A et B se réfèrent toujours l'un à l'autre, c'est-à-dire que pour les seconds éléments de A et B, ils pointent toujours vers la même adresse mémoire A.
De plus, puisque int est un type immuable, après avoir changé A[0] en 8, son adresse de référence changera ! Elle se distingue de la référence de l'élément B[0].
Copie approfondie
Alors comment faire face à une telle situation ? Vous devez utiliser le module de copie dans le module python
Le module de copie a 2 fonctions
1 : copy.copy (l'objet que vous souhaitez copier) : Il s'agit d'une copie superficielle, et la précédente Les propriétés de l'opération [:] sur la liste sont les mêmes
2 : copy.deepcopy (l'objet que vous souhaitez copier) : Il s'agit d'une copie profonde, sauf qu'elle générera une nouvelle objet tout comme la copie superficielle, et pour les éléments internes, de nouvelles références seront générées pour les séparer indépendamment
Regardez l'exemple ci-dessous. Lorsque vous attribuez une copie complète de A à B, elles peuvent être. dit être complètement indépendant. , que vous modifiiez les éléments immuables dans A ou que vous modifiiez les éléments mutables imbriqués dans A, le résultat n'affectera pas B
Je crois comprendre que la copie profonde peut être appelée copie récursive , il le fera. copiez tous les éléments variables imbriqués, puis référencez-les indépendamment
Résumé de la copie approfondie :
L'effet de la copie approfondie est le même. comme celui de la copie superficielle. En plus de générer une nouvelle référence à partir de la référence de l'objet, cela vous aidera à séparer tous les éléments imbriqués un par un
Dessiné 2 par moi-même Image pour montrer la différence entre la copie superficielle et la copie superficielle. copie profonde
Il convient de noter que bien qu'après une copie superficielle, l'adresse de référence des éléments immuables dans la liste soit toujours la même, mais comme ce sont des éléments immuables, donc après la modification de tout élément immuable, la référence L'adresse sera nouvelle sans affecter l'adresse de référence d'origine.
Résumé
Alors, c'est parti, superficiel copy Et le mécanisme de la copie profonde est fondamentalement compris.
Il y a aussi des circonstances particulières qui doivent être expliquées
Pour les types immuables : int, str, tuple, float et autres éléments, il n'y a pas de copie après leur modification, l'adresse de référence. est directement modifié, comme indiqué ci-dessous
Cependant, s'il y a des types de variables imbriqués à l'intérieur du type immuable, vous pouvez toujours utiliser la copie complète
Rappel également, la méthode la plus couramment utilisée est l'affectation directe (ou passage direct de références), comme l'exemple suivant
Il combine a et b Deux éléments variables pointent vers une adresse mémoire en même temps, donc tout changement affectera a et b
Enfin
type de variable : list , set, dict
types immuables : int, str, float, tuple
méthode de copie superficielle : [:], copy.copy(), utilisez la fonction d'usine (list/dir/set )
Copie approfondie méthode : copy.deepcopy()
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!