Nous rencontrons souvent des erreurs inexplicables dans la programmation Python. En fait, ce n'est pas un problème avec le langage lui-même, mais dû au fait que nous ignorons certaines caractéristiques du langage lui-même. Aujourd'hui, nous allons examiner 3 erreurs incroyables causées par. en utilisant des variables Python, veuillez prêter plus d'attention lors de la programmation future.
Cela semble correct ? Vous écrivez une petite fonction qui, par exemple, recherche un lien sur la page actuelle et l'ajoute éventuellement à une autre liste fournie.
def search_for_links(page, add_to=[]): new_links = page.search_for_links() add_to.extend(new_links) return add_to
En apparence, cela ressemble à du code Python parfaitement normal, et en fait c'est le cas, et il peut s'exécuter. Cependant, il y a un problème. Si nous fournissons une liste au paramètre add_to, cela fonctionnera comme prévu. Mais si nous le laissons utiliser la valeur par défaut, quelque chose de magique se produit.
Essayez le code suivant :
def fn(var1, var2=[]): var2.append(var1) print(var2) fn(3) fn(4) fn(5)
Peut-être pensez-vous que nous verrons :
[3] [4] [5]
Mais en réalité, ce que nous voyons est :
[3] [3,4] [3,4,5]
Pourquoi ? Comme vous pouvez le voir, la même liste est utilisée à chaque fois. Pourquoi le résultat est-il ainsi ? En Python, lorsque nous écrivons une telle fonction, cette liste est instanciée dans le cadre de la définition de la fonction. Lorsqu'une fonction s'exécute, elle n'est pas instanciée à chaque fois. Cela signifie que cette fonction utilisera toujours exactement le même objet de liste à moins que nous fournissions un nouvel objet :
fn(3,[4]) [4,3]
La réponse est exactement ce que nous pensions. Pour obtenir ce résultat, la bonne manière est :
def fn(var1, var2=None): ifnot var2: var2 =[] var2.append(var1)
ou dans le premier exemple :
def search_for_links(page, add_to=None): ifnot add_to: add_to =[] new_links = page.search_for_links() add_to.extend(new_links) return add_to
Cela supprimera l'instanciation lorsque le module chargera du contenu afin que l'instanciation de la liste se produise chaque fois que la fonction est exécutée. Veuillez noter que pour les types de données immuables, tels que les tuples, les chaînes et les entiers, cette situation n'a pas besoin d'être prise en compte. Cela signifie qu'un code comme celui-ci est tout à fait réalisable :
def func(message="my message"): print(message)
Ceci est très similaire à la dernière erreur mentionnée ci-dessus. Considérez le code suivant :
class URLCatcher(object): urls =[] def add_url(self, url): self.urls.append(url)
Ce code semble parfaitement normal. Nous avons un objet qui stocke les URL. Lorsque nous appelons la méthode add_url, elle ajoute une URL donnée au stockage. Ça a l'air plutôt bien, non ? Voyons à quoi ça ressemble réellement :
a =URLCatcher() a.add_url('http://www.google.com') b =URLCatcher() b.add_url('http://www.pythontab.com') print(b.urls) print(a.urls)
Résultat :
['http://www.google.com','http://www.pythontab.com'] ['http://www.google.com','http://www.pythontab.com']
Attendez, que se passe-t-il ? ! Ce n'est pas ce que nous pensions. Nous instancions deux objets distincts a et b. Donnez une URL à a et une autre à b. Comment se fait-il que ces deux objets aient ces deux URL ?
C'est le même problème que le premier exemple d'erreur. Lorsque la définition de classe est créée, la liste d'URL est instanciée. Toutes les instances de cette classe utilisent la même liste. Il y a des moments où cela est utile, mais la plupart du temps, vous ne voulez pas le faire. Vous souhaitez un stockage séparé pour chaque objet. Pour ce faire, nous modifions le code en :
class URLCatcher(object): def __init__(self): self.urls =[] def add_url(self, url): self.urls.append(url)
Maintenant, lorsque l'objet est créé, la liste d'URL est instanciée. Lorsque nous instancions deux objets distincts, ils utiliseront chacun deux listes distinctes.
Ce problème me dérange depuis un moment. Apportons quelques modifications et utilisons un autre type de données mutable : un dictionnaire.
a ={'1':"one",'2':'two'}
Maintenant, supposons que nous souhaitions utiliser ce dictionnaire ailleurs tout en gardant intactes ses données d'origine.
b = a b['3']='three'
Facile, non ?
Maintenant, regardons le dictionnaire original a que nous ne voulons pas changer :
{'1':"one",'2':'two','3':'three'}
Wow, attendez, regardons à nouveau b ?
{'1':"one",'2':'two','3':'three'}
Attends, quoi ? Un peu brouillon... Revenons en arrière et voyons ce qui se passe dans ce cas avec d'autres types immuables, comme un tuple :
c =(2,3) d = c d =(4,5)
Maintenant c est (2, 3) et d est (4, 5 ).
Le résultat de cette fonction est comme nous l'espérions. Alors, que s’est-il passé exactement dans l’exemple précédent ? Lorsque vous utilisez un type mutable, il se comporte un peu comme un pointeur en C. Dans le code ci-dessus, nous laissons b = a, ce que nous voulons vraiment dire est : b devient une référence de a. Ils pointent tous vers le même objet dans la mémoire Python. Cela vous semble familier ? C'est parce que cette question est similaire à la précédente.
Est-ce que la même chose se produira avec les listes ? Oui. Alors, comment pouvons-nous le résoudre ? Cela doit être fait avec beaucoup de soin. Si nous avons vraiment besoin de copier une liste pour le traitement, nous pouvons le faire :
b = a[:]
Cela parcourra et copiera la référence de chaque objet de la liste et le placera dans une nouvelle liste. Mais attention : si tous les objets de la liste sont mutables, nous obtiendrons à nouveau une référence à eux, pas une copie complète.
Supposons que vous fassiez une liste sur un morceau de papier. Dans l’exemple original, cela équivaut à ce que A et B regardent le même morceau de papier. Si une personne modifie cette fiche, les deux personnes verront les mêmes changements. Lorsque l’on copie les références, chacun a désormais sa propre liste. Cependant, nous supposons que cette liste comprend des endroits où trouver de la nourriture. Si « réfrigérateur » est en premier dans la liste, même s'il est copié, les entrées des deux listes pointeront vers le même réfrigérateur. Par conséquent, si le réfrigérateur est modifié par A et mange le gros gâteau à l’intérieur, B verra également la disparition du gâteau. Il n’y a pas de solution simple ici. Souvenez-vous-en et écrivez votre code de manière à ne pas poser ce problème.
字典以相同的方式工作,并且你可以通过以下方式创建一个昂贵副本:
b = a.copy()
再次说明,这只会创建一个新的字典,指向原来存在的相同的条目。因此,如果我们有两个相同的列表,并且我们修改字典 a 的一个键指向的可变对象,那么在字典 b 中也将看到这些变化。
可变数据类型的麻烦也是它们强大的地方。以上都不是实际中的问题;它们是一些要注意防止出现的问题。在第三个项目中使用昂贵复制操作作为解决方案在 99% 的时候是没有必要的。
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!