Python >> Tutoriel Python >  >> Python

[Résolu] AttributeError:impossible de définir l'attribut en python

Formulation du problème

Vous créez un namedtuple objet dans votre code et vous souhaitez écraser l'une des valeurs d'attribut comme vous en avez l'habitude pour les objets normaux :

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car(300, 'gold')
porsche.speed = 400

Mais, de manière inattendue, Python lève un AttributeError: can't set attribute . ?

Traceback (most recent call last):
  File "C:\Users\xcent\Desktop\code.py", line 5, in <module>
    porsche.speed = 400
AttributeError: can't set attribute

Que pouvez-vous faire? ? Comprenons la raison pour laquelle cette erreur se produit - après cela, vous apprendrez les meilleurs correctifs.

Pourquoi cette erreur se produit-elle ?

Tout namedtuple l'objet est aussi un tuple (relation de sous-classe ), il est donc immuable — vous ne pouvez pas le modifier après la création.

Par exemple, vous ne pouvez pas modifier un tuple ('Alice', 'Bob') à ('Ann', 'Bob') sans en créer un nouveau.

Si vous tentez de modifier un namedtuple valeur d'attribut de l'objet, vous essayez de modifier un objet immuable. Par conséquent, Python lève le AttributeError: can't set attribute .

Correctif #1 :utilisez la méthode namedtuple._replace()

Le moyen le plus simple de réparer le AttributeError:can't set attribute est de créer un nouveau namedtuple objet avec le namedtuple._replace() méthode. Le résultat est un nouveau namedtuple objet avec tous les attributs copiés sauf celui qui vient d'être transmis.

Par exemple, si vous souhaitez modifier l'attribut x de l'objet tuple nommé n à la valeur 42 , appelez le n._replace(x=42) .

Voici le correctif appliqué à notre exemple précédent :

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car(300, 'gold')
porsche = porsche._replace(speed=400)

Vous pouvez voir dans la suite print() déclaration indiquant que la valeur d'attribut d'origine pour speed est passé de 300 à 400.

print(porsche)
# Car(speed=300, color='gold')

Notez que cela est généralement inefficace car tous les autres attributs doivent être copiés juste pour mettre à jour un seul attribut !

Pouvons-nous résoudre ce problème ? Bien sûr !

Correctif #2 : n'utilisez pas de tuples nommés mais des classes

Les tuples nommés sont des objets immuables, vous ne pouvez donc pas modifier les valeurs d'attribut. Mais les classes Python standard sont modifiables, elles peuvent donc être modifiées arbitrairement. Plus précisément, vous pouvez modifier la valeur d'un attribut existant et même ajouter dynamiquement un nouvel attribut à un objet existant.

Le code suivant accomplit la même chose que notre exemple de code, mais il le fait plus efficacement en utilisant des classes Python standard plutôt que namedtuples :

class Car:
    def __init__(self, speed, color):
        self.speed = speed
        self.color = color

porsche = Car(300, 'gold')
porsche.speed = 400

Confirmons que la valeur de l'attribut est passée à 400 :

print(porsche.speed)
# 400

Vous pouvez même ajouter un attribut supplémentaire, par exemple price , à l'objet comme ceci :

porsche.price = 100000
print(porsche.price)
# 100000

Correctif #3 :Utiliser des dictionnaires

Pour les applications légères, vous pouvez également utiliser un dictionnaire simple au lieu de tuples nommés ou de classes. Les dictionnaires sont conçus pour contenir clé :valeur au lieu de attribut :valeur paires. Par exemple, vous pouvez créer un dictionnaire {'key_1': 'value_1', 'key_2': 'value_2'} pour stocker deux clés au lieu d'attributs. Au lieu de mettre à jour un attribut, vous mettriez maintenant à jour une clé en utilisant dict['key_1'] = 'new_value' .

Voici cette solution appliquée à notre problème précédent :une certaine simplification s'est produite !

porsche = {'speed': 300, 'color': 'gold'}
porsche['speed'] = 400

Vérifions si l'attribut a changé :

print(porsche['speed'])
# 400

En effet, la sortie a changé et aucun message d'erreur n'existe.

Correctif #4 :si vous devez utiliser des tuples nommés, utilisez un attribut mutable

Si vous devez utiliser namedtuples — par exemple pour mettre à jour un attribut existant avec une nouvelle valeur — et vous ne pouvez pas en créer un nouveau avec _replace() pour des raisons d'efficacité, procédez comme suit :

Créez le nametuple les attributs de l'objet en tant qu'objets modifiables tels que des listes. Vous pouvez toujours modifier le contenu d'une liste car elle est modifiable. Donc, sémantiquement, vous modifiez la valeur d'un namedtuple attribut sans violer réellement le critère d'immuabilité—le namedtuple pointe toujours vers le même objet modifiable (par exemple, une liste) en mémoire.

Voici cette solution appliquée à l'exemple de problème :

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car([300], 'gold')
porsche.speed[0] = 400

Bien que vous ne trouviez peut-être pas cette solution particulièrement sexy, elle fonctionne à merveille :

print(porsche.speed)
# [400]

La valeur d'origine de la vitesse a changé et aucune erreur ne s'est produite.