Comment accéder et modifier les attributs d'un objet Python

PythonBeginner
Pratiquer maintenant

Introduction

Dans ce tutoriel, nous allons explorer comment accéder et modifier les attributs des objets Python. Comprendre comment travailler avec les propriétés des objets est une compétence fondamentale pour la programmation Python. À la fin de ce lab, vous serez capable de créer des objets, d'accéder à leurs attributs en utilisant différentes méthodes et de modifier ces attributs pour changer le comportement des objets.

Python est un langage de programmation orienté objet, ce qui signifie que tout en Python est un objet avec des propriétés et des méthodes. Apprendre à interagir avec ces objets est essentiel pour construire des applications Python efficaces.

Création d'une classe Python simple

Commençons par comprendre ce que sont les classes et les objets Python, et comment les créer.

Que sont les classes et les objets ?

En Python, une classe est un plan pour créer des objets. Les objets sont des instances de classes, qui contiennent :

  • Attributs (Attributes) : Des variables qui stockent des données
  • Méthodes (Methods) : Des fonctions qui définissent les actions que l'objet peut effectuer

Considérez une classe comme un modèle et un objet comme quelque chose créé à partir de ce modèle.

Création de votre première classe

Créons une classe Person simple avec quelques attributs de base. Ouvrez l'éditeur de code et créez un nouveau fichier appelé person.py dans le répertoire /home/labex/project :

## Define a Person class with name and age attributes
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30)

## Print the object
print(john)
print(f"Type of john: {type(john)}")

Ce code définit une classe Person avec deux attributs (name et age) et une méthode (greet). Comprenons les composants clés :

  • class Person: - Cette ligne déclare une nouvelle classe nommée Person
  • __init__ - Il s'agit d'une méthode spéciale appelée constructeur (constructor), qui initialise un nouvel objet lorsqu'il est créé
  • self - Ce paramètre fait référence à l'objet en cours de création ou de manipulation
  • self.name = name - Cela crée un attribut appelé name pour l'objet et lui affecte la valeur passée au constructeur

Exécutons maintenant ce code pour voir ce qui se passe :

python3 /home/labex/project/person.py

Vous devriez voir une sortie similaire à :

<__main__.Person object at 0x7f8b2c3d9d90>
Type of john: <class '__main__.Person'>

Cette sortie nous indique que john est un objet de la classe Person. Le nombre hexadécimal est l'adresse mémoire où l'objet est stocké.

Création de plusieurs objets

Nous pouvons créer plusieurs objets à partir d'une seule classe. Modifions notre fichier person.py pour créer un autre objet Person :

## Define a Person class with name and age attributes
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create two Person objects
john = Person("John", 30)
alice = Person("Alice", 25)

## Print information about both objects
print(f"First person: {john.name}, {john.age}")
print(f"Second person: {alice.name}, {alice.age}")

Exécutez le code mis à jour :

python3 /home/labex/project/person.py

Vous devriez voir :

First person: John, 30
Second person: Alice, 25

Nous avons maintenant créé deux objets Person différents, chacun avec ses propres attributs name et age.

Accéder aux attributs d'un objet

Maintenant que nous avons créé des objets avec des attributs, apprenons différentes façons d'accéder à ces attributs.

Utilisation de la notation pointée (Dot Notation)

La méthode la plus courante pour accéder aux attributs d'un objet est d'utiliser la notation pointée : object.attribute.

Créons un nouveau fichier appelé access_attributes.py dans le répertoire /home/labex/project :

class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

## Access attributes using dot notation
print(f"Name: {john.name}")
print(f"Age: {john.age}")
print(f"Job: {john.job}")

## Access and call a method
greeting = john.greet()
print(f"Greeting: {greeting}")

Exécutez ce code :

python3 /home/labex/project/access_attributes.py

Vous devriez voir :

Name: John
Age: 30
Job: Developer
Greeting: Hello, my name is John and I am 30 years old.

Accéder aux attributs en utilisant des variables

Parfois, vous devrez peut-être accéder à un attribut lorsque vous n'avez que son nom sous forme de chaîne de caractères. Cela peut être utile lorsque vous travaillez avec des noms d'attributs déterminés dynamiquement.

Modifions notre fichier access_attributes.py :

class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

## List of attributes to access
attributes = ["name", "age", "job"]

## Access attributes using a loop and dot notation
print("Accessing attributes using dot notation:")
for attr in attributes:
    if attr == "name":
        print(f"Name: {john.name}")
    elif attr == "age":
        print(f"Age: {john.age}")
    elif attr == "job":
        print(f"Job: {john.job}")

## Call a method
greeting = john.greet()
print(f"Greeting: {greeting}")

Exécutez le code :

python3 /home/labex/project/access_attributes.py

Vous devriez voir :

Accessing attributes using dot notation:
Name: John
Age: 30
Job: Developer
Greeting: Hello, my name is John and I am 30 years old.

C'est un peu lourd. Dans l'étape suivante, nous allons apprendre une manière plus élégante d'accéder aux attributs dynamiquement.

Utilisation des fonctions getattr() et setattr()

Python fournit des fonctions intégrées pour accéder et modifier dynamiquement les attributs d'un objet. Celles-ci sont particulièrement utiles lorsque vous souhaitez travailler avec des noms d'attributs qui ne sont pas connus avant l'exécution.

La fonction getattr()

La fonction getattr() vous permet d'accéder à un attribut en utilisant son nom sous forme de chaîne de caractères. La syntaxe est :

getattr(object, attribute_name, default_value)
  • object : L'objet dont vous souhaitez accéder à l'attribut
  • attribute_name : Une chaîne de caractères contenant le nom de l'attribut
  • default_value : Une valeur optionnelle à retourner si l'attribut n'existe pas

Créons un nouveau fichier appelé dynamic_attributes.py dans le répertoire /home/labex/project :

class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

## List of attributes to access
attributes = ["name", "age", "job", "address"]

## Access attributes using getattr()
print("Accessing attributes using getattr():")
for attr in attributes:
    ## The third parameter is the default value if the attribute doesn't exist
    value = getattr(john, attr, "Not available")
    print(f"{attr.capitalize()}: {value}")

## Call a method using getattr
greet_method = getattr(john, "greet")
greeting = greet_method()
print(f"Greeting: {greeting}")

Exécutez ce code :

python3 /home/labex/project/dynamic_attributes.py

Vous devriez voir :

Accessing attributes using getattr():
Name: John
Age: 30
Job: Developer
Address: Not available
Greeting: Hello, my name is John and I am 30 years old.

Notez que pour l'attribut address, qui n'existe pas, getattr() renvoie notre valeur par défaut "Not available" au lieu de générer une erreur.

La fonction setattr()

La fonction setattr() vous permet de définir un attribut en utilisant son nom sous forme de chaîne de caractères. La syntaxe est :

setattr(object, attribute_name, value)
  • object : L'objet dont vous souhaitez définir l'attribut
  • attribute_name : Une chaîne de caractères contenant le nom de l'attribut
  • value : La valeur à affecter à l'attribut

Modifions notre fichier dynamic_attributes.py pour inclure des exemples d'utilisation de setattr() :

class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

print("Original attributes:")
print(f"Name: {john.name}")
print(f"Age: {john.age}")
print(f"Job: {john.job}")

## Modify attributes using setattr()
print("\nModifying attributes using setattr():")
setattr(john, "name", "John Smith")
setattr(john, "age", 31)
setattr(john, "job", "Senior Developer")

## Add a new attribute using setattr()
setattr(john, "address", "123 Main St")

## Print the modified attributes
print("\nModified attributes:")
print(f"Name: {john.name}")
print(f"Age: {john.age}")
print(f"Job: {john.job}")
print(f"Address: {john.address}")

## Access attributes using getattr()
print("\nAccessing attributes using getattr():")
for attr in ["name", "age", "job", "address"]:
    value = getattr(john, attr, "Not available")
    print(f"{attr.capitalize()}: {value}")

Exécutez le code mis à jour :

python3 /home/labex/project/dynamic_attributes.py

Vous devriez voir :

Original attributes:
Name: John
Age: 30
Job: Developer

Modifying attributes using setattr():

Modified attributes:
Name: John Smith
Age: 31
Job: Senior Developer
Address: 123 Main St

Accessing attributes using getattr():
Name: John Smith
Age: 31
Job: Senior Developer
Address: 123 Main St

Notez que nous avons pu modifier les attributs existants et également ajouter un attribut complètement nouveau (address) qui n'était pas défini dans la classe.

Exemple pratique

Créons un exemple plus pratique où l'accès et la modification dynamiques des attributs sont utiles. Créez un fichier appelé data_processor.py dans le répertoire /home/labex/project :

class DataRecord:
    def __init__(self):
        ## Start with no attributes
        pass

## Create a function to process a dictionary into an object
def dict_to_object(data_dict):
    record = DataRecord()
    for key, value in data_dict.items():
        setattr(record, key, value)
    return record

## Sample data (could come from a database, API, etc.)
user_data = {
    "user_id": 12345,
    "username": "johndoe",
    "email": "john@example.com",
    "active": True,
    "login_count": 27
}

## Convert the dictionary to an object
user = dict_to_object(user_data)

## Access the attributes
print(f"User ID: {user.user_id}")
print(f"Username: {user.username}")
print(f"Email: {user.email}")
print(f"Active: {user.active}")
print(f"Login Count: {user.login_count}")

## We can also add or modify attributes
setattr(user, "last_login", "2023-09-15")
setattr(user, "login_count", 28)

print("\nAfter modifications:")
print(f"Login Count: {user.login_count}")
print(f"Last Login: {user.last_login}")

## We can also access all attributes dynamically
print("\nAll attributes:")
for attr in ["user_id", "username", "email", "active", "login_count", "last_login"]:
    value = getattr(user, attr, None)
    print(f"{attr}: {value}")

Exécutez ce code :

python3 /home/labex/project/data_processor.py

Vous devriez voir :

User ID: 12345
Username: johndoe
Email: john@example.com
Active: True
Login Count: 27

After modifications:
Login Count: 28
Last Login: 2023-09-15

All attributes:
user_id: 12345
username: johndoe
email: john@example.com
active: True
login_count: 28
last_login: 2023-09-15

Cet exemple montre comment vous pouvez convertir des données d'un dictionnaire (qui peuvent provenir d'une base de données, d'une API ou d'un fichier) en un objet, puis accéder ou modifier ses attributs dynamiquement.

Applications pratiques des attributs d'objets

Maintenant que nous comprenons comment accéder et modifier les attributs d'objets, explorons quelques applications pratiques de ces concepts. Nous allons créer un système simple de gestion des stocks pour démontrer comment les attributs d'objets peuvent être utilisés dans des applications réelles.

Création d'un système d'inventaire

Créons un fichier appelé inventory.py dans le répertoire /home/labex/project :

class Product:
    def __init__(self, product_id, name, price, quantity):
        self.product_id = product_id
        self.name = name
        self.price = price
        self.quantity = quantity

    def total_value(self):
        return self.price * self.quantity

    def display_info(self):
        return f"Product: {self.name} (ID: {self.product_id})\nPrice: ${self.price:.2f}\nQuantity: {self.quantity}\nTotal Value: ${self.total_value():.2f}"


class Inventory:
    def __init__(self):
        self.products = {}

    def add_product(self, product):
        self.products[product.product_id] = product
        print(f"Added: {product.name}")

    def remove_product(self, product_id):
        if product_id in self.products:
            product = self.products.pop(product_id)
            print(f"Removed: {product.name}")
            return True
        print(f"Product with ID {product_id} not found.")
        return False

    def update_quantity(self, product_id, new_quantity):
        if product_id in self.products:
            product = self.products[product_id]
            old_quantity = product.quantity
            product.quantity = new_quantity
            print(f"Updated quantity of {product.name} from {old_quantity} to {new_quantity}")
            return True
        print(f"Product with ID {product_id} not found.")
        return False

    def display_inventory(self):
        if not self.products:
            print("Inventory is empty.")
            return

        print("\n===== Current Inventory =====")
        total_value = 0
        for product in self.products.values():
            print(f"\n{product.display_info()}")
            total_value += product.total_value()

        print(f"\nTotal Inventory Value: ${total_value:.2f}")
        print("============================")


## Create an inventory
inventory = Inventory()

## Create some products
laptop = Product(1, "Laptop", 999.99, 5)
phone = Product(2, "Smartphone", 499.95, 10)
headphones = Product(3, "Wireless Headphones", 149.99, 15)

## Add products to inventory
inventory.add_product(laptop)
inventory.add_product(phone)
inventory.add_product(headphones)

## Display the inventory
inventory.display_inventory()

## Update quantities
inventory.update_quantity(1, 7)  ## Increase laptop quantity
inventory.update_quantity(3, 10)  ## Decrease headphones quantity

## Display the updated inventory
inventory.display_inventory()

## Remove a product
inventory.remove_product(2)  ## Remove smartphones

## Display the final inventory
inventory.display_inventory()

Exécutez ce code :

python3 /home/labex/project/inventory.py

Vous devriez voir une sortie similaire à :

Added: Laptop
Added: Smartphone
Added: Wireless Headphones

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 5
Total Value: $4999.95

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 15
Total Value: $2249.85

Total Inventory Value: $12249.30
============================
Updated quantity of Laptop from 5 to 7
Updated quantity of Wireless Headphones from 15 to 10

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 7
Total Value: $6999.93

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 10
Total Value: $1499.90

Total Inventory Value: $13499.33
============================
Removed: Smartphone

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 7
Total Value: $6999.93

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 10
Total Value: $1499.90

Total Inventory Value: $8499.83
============================

Travailler avec des attributs dynamiques

Améliorons notre système d'inventaire pour gérer les attributs dynamiques en utilisant getattr() et setattr(). Créez un fichier appelé enhanced_inventory.py dans le répertoire /home/labex/project :

class Product:
    def __init__(self, product_id, name, price, quantity, **kwargs):
        self.product_id = product_id
        self.name = name
        self.price = price
        self.quantity = quantity

        ## Add any additional attributes provided
        for key, value in kwargs.items():
            setattr(self, key, value)

    def total_value(self):
        return self.price * self.quantity

    def display_info(self):
        result = [f"Product: {self.name} (ID: {self.product_id})",
                  f"Price: ${self.price:.2f}",
                  f"Quantity: {self.quantity}",
                  f"Total Value: ${self.total_value():.2f}"]

        ## Display additional attributes
        standard_attrs = {'product_id', 'name', 'price', 'quantity'}
        for attr in dir(self):
            if not attr.startswith('__') and not callable(getattr(self, attr)) and attr not in standard_attrs:
                value = getattr(self, attr)
                result.append(f"{attr.replace('_', ' ').title()}: {value}")

        return '\n'.join(result)


class Inventory:
    def __init__(self):
        self.products = {}

    def add_product(self, product):
        self.products[product.product_id] = product
        print(f"Added: {product.name}")

    def update_attribute(self, product_id, attribute, value):
        if product_id in self.products:
            product = self.products[product_id]
            old_value = getattr(product, attribute, None)
            setattr(product, attribute, value)
            print(f"Updated {attribute} of {product.name} from {old_value} to {value}")
            return True
        print(f"Product with ID {product_id} not found.")
        return False

    def display_inventory(self):
        if not self.products:
            print("Inventory is empty.")
            return

        print("\n===== Current Inventory =====")
        total_value = 0
        for product in self.products.values():
            print(f"\n{product.display_info()}")
            total_value += product.total_value()

        print(f"\nTotal Inventory Value: ${total_value:.2f}")
        print("============================")


## Create an inventory
inventory = Inventory()

## Create products with additional attributes
laptop = Product(1, "Laptop", 999.99, 5, brand="TechPro", model="X5", color="Silver", warranty_years=2)
phone = Product(2, "Smartphone", 499.95, 10, brand="MobiCom", model="Galaxy", color="Black", has_5g=True)
headphones = Product(3, "Wireless Headphones", 149.99, 15, brand="AudioMax", battery_life_hours=20, is_noise_cancelling=True)

## Add products to inventory
inventory.add_product(laptop)
inventory.add_product(phone)
inventory.add_product(headphones)

## Display the inventory
inventory.display_inventory()

## Update a standard attribute
inventory.update_attribute(1, "price", 1099.99)

## Update a custom attribute
inventory.update_attribute(3, "battery_life_hours", 25)

## Add a new attribute to an existing product
inventory.update_attribute(2, "water_resistant", True)

## Display the updated inventory
inventory.display_inventory()

Exécutez ce code :

python3 /home/labex/project/enhanced_inventory.py

Vous devriez voir une sortie similaire à :

Added: Laptop
Added: Smartphone
Added: Wireless Headphones

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 5
Total Value: $4999.95
Brand: TechPro
Color: Silver
Model: X5
Warranty Years: 2

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50
Brand: MobiCom
Color: Black
Has 5g: True
Model: Galaxy

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 15
Total Value: $2249.85
Battery Life Hours: 20
Brand: AudioMax
Is Noise Cancelling: True

Total Inventory Value: $12249.30
============================
Updated price of Laptop from 999.99 to 1099.99
Updated battery_life_hours of Wireless Headphones from 20 to 25
Updated water_resistant of Smartphone from None to True

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $1099.99
Quantity: 5
Total Value: $5499.95
Brand: TechPro
Color: Silver
Model: X5
Warranty Years: 2

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50
Brand: MobiCom
Color: Black
Has 5g: True
Model: Galaxy
Water Resistant: True

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 15
Total Value: $2249.85
Battery Life Hours: 25
Brand: AudioMax
Is Noise Cancelling: True

Total Inventory Value: $12749.30
============================

Dans ce système d'inventaire amélioré, nous avons démontré comment utiliser getattr() et setattr() dans une application plus complexe :

  1. La classe Product accepte des attributs supplémentaires via **kwargs et les définit dynamiquement avec setattr()
  2. La méthode display_info() utilise getattr() pour afficher tous les attributs du produit
  3. La méthode update_attribute() dans la classe Inventory utilise getattr() pour récupérer la valeur actuelle et setattr() pour la mettre à jour

Cette approche offre une grande flexibilité, nous permettant de gérer des produits avec différents ensembles d'attributs sans modifier la définition de la classe.

Résumé

Dans ce laboratoire, vous avez appris à travailler avec les attributs d'objets Python, une compétence fondamentale pour une programmation Python efficace. Voici un résumé de ce que vous avez accompli :

  1. Création de classes et d'objets : Vous avez appris à créer des classes Python avec des attributs et des méthodes, et comment instancier des objets à partir de ces classes.

  2. Accès aux attributs d'objets : Vous avez exploré différentes façons d'accéder aux attributs d'objets, notamment :

    • En utilisant la notation pointée (object.attribute)
    • En utilisant la fonction getattr() pour un accès dynamique
  3. Modification des attributs d'objets : Vous avez appris à modifier les attributs d'objets en utilisant :

    • L'affectation directe avec la notation pointée (object.attribute = value)
    • La fonction setattr() pour une modification dynamique
  4. Applications pratiques : Vous avez appliqué ces concepts pour construire des applications pratiques :

    • Un système simple de gestion des stocks
    • Un système amélioré avec la gestion dynamique des attributs

Ces compétences vous aideront à créer des applications Python plus flexibles et plus puissantes. Comprendre comment travailler avec les attributs d'objets est essentiel pour des tâches telles que le traitement des données, la gestion de la configuration et la création de systèmes logiciels extensibles.

Pour vous entraîner davantage, envisagez d'étendre le système de gestion des stocks avec des fonctionnalités supplémentaires, telles que :

  • Filtrer les produits en fonction des attributs
  • Trier les produits selon différents critères
  • Ajouter une validation pour les modifications d'attributs
  • Implémenter la sérialisation pour enregistrer et charger les données d'inventaire