如何访问和修改 Python 对象的属性

PythonBeginner
立即练习

介绍

在本教程中,我们将探讨如何访问和修改 Python 对象的属性。理解如何使用对象属性是 Python 编程的一项基本技能。通过完成这个实验(Lab),你将能够创建对象,使用不同的方法访问它们的属性,并修改这些属性来改变对象的行为。

Python 是一种面向对象的编程语言,这意味着 Python 中的一切都是具有属性和方法的对象。学习与这些对象交互对于构建有效的 Python 应用程序至关重要。

创建一个简单的 Python 类

让我们从理解 Python 类和对象是什么,以及如何创建它们开始。

什么是类和对象?

在 Python 中,类是用于创建对象的蓝图。对象是类的实例,它们包含:

  • 属性(Attributes):存储数据的变量
  • 方法(Methods):定义对象可以执行的操作的函数

可以将类想象成一个模板,而对象是根据该模板创建的东西。

创建你的第一个类

让我们创建一个简单的 Person 类,其中包含一些基本属性。打开代码编辑器,并在 /home/labex/project 目录下创建一个名为 person.py 的新文件:

## 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)}")

这段代码定义了一个 Person 类,它有两个属性(nameage)和一个方法(greet)。让我们理解一下关键组件:

  • class Person: - 这行声明了一个名为 Person 的新类
  • __init__ - 这是一个特殊的方法,称为构造函数(constructor),它在创建新对象时初始化对象
  • self - 这个参数指的是正在创建或操作的对象
  • self.name = name - 这为对象创建了一个名为 name 的属性,并为其分配了传递给构造函数的值

现在让我们运行这段代码,看看会发生什么:

python3 /home/labex/project/person.py

你应该看到类似这样的输出:

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

这个输出告诉我们 johnPerson 类的一个对象。十六进制数字是对象存储的内存地址。

创建多个对象

我们可以从一个类创建多个对象。让我们修改我们的 person.py 文件,以创建另一个 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}")

运行更新后的代码:

python3 /home/labex/project/person.py

你应该看到:

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

现在我们创建了两个不同的 Person 对象,每个对象都有自己的 nameage 属性。

访问对象属性

现在我们已经创建了具有属性的对象,让我们学习访问这些属性的不同方法。

使用点表示法

访问对象属性最常用的方法是使用点表示法:object.attribute

让我们在 /home/labex/project 目录下创建一个名为 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")

## 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}")

运行这段代码:

python3 /home/labex/project/access_attributes.py

你应该看到:

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

使用变量访问属性

有时,你可能需要访问一个属性,而你只有它的名称作为字符串。当你使用动态确定的属性名称时,这会很有用。

让我们修改我们的 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}")

运行代码:

python3 /home/labex/project/access_attributes.py

你应该看到:

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

这有点麻烦。在下一步中,我们将学习一种更优雅的方式来动态访问属性。

使用 getattr() 和 setattr() 函数

Python 提供了内置函数来动态地访问和修改对象属性。当你希望使用直到运行时才知道的属性名称时,这些函数特别有用。

getattr() 函数

getattr() 函数允许你使用属性的名称(作为字符串)来访问它。语法如下:

getattr(object, attribute_name, default_value)
  • object:你想要访问其属性的对象
  • attribute_name:一个包含属性名称的字符串
  • default_value:如果属性不存在,则返回的可选值

让我们在 /home/labex/project 目录下创建一个名为 dynamic_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", "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}")

运行这段代码:

python3 /home/labex/project/dynamic_attributes.py

你应该看到:

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.

请注意,对于不存在的 address 属性,getattr() 返回我们的默认值 "Not available",而不是引发错误。

setattr() 函数

setattr() 函数允许你使用属性的名称(作为字符串)来设置属性。语法如下:

setattr(object, attribute_name, value)
  • object:你想要设置其属性的对象
  • attribute_name:一个包含属性名称的字符串
  • value:要分配给属性的值

让我们修改我们的 dynamic_attributes.py 文件,以包含使用 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}")

运行更新后的代码:

python3 /home/labex/project/dynamic_attributes.py

你应该看到:

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

请注意,我们能够修改现有属性,并且还添加了一个完全新的属性(address),该属性未在类中定义。

实际例子

让我们创建一个更实际的例子,其中动态属性访问和修改很有用。在 /home/labex/project 目录下创建一个名为 data_processor.py 的文件:

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}")

运行这段代码:

python3 /home/labex/project/data_processor.py

你应该看到:

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

这个例子展示了如何将来自字典(可能来自数据库、API 或文件)的数据转换为对象,然后动态地访问或修改其属性。

对象属性的实际应用

现在我们已经了解了如何访问和修改对象属性,让我们探索这些概念的一些实际应用。我们将创建一个简单的库存管理系统,以演示对象属性如何在实际应用中使用。

创建库存系统

让我们在 /home/labex/project 目录下创建一个名为 inventory.py 的文件:

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()

运行这段代码:

python3 /home/labex/project/inventory.py

你应该看到类似如下的输出:

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
============================

使用动态属性

让我们使用 getattr()setattr() 来增强我们的库存系统,以处理动态属性。在 /home/labex/project 目录下创建一个名为 enhanced_inventory.py 的文件:

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()

运行这段代码:

python3 /home/labex/project/enhanced_inventory.py

你应该看到类似如下的输出:

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
============================

在这个增强的库存系统中,我们演示了如何在更复杂的应用程序中使用 getattr()setattr()

  1. Product 类通过 **kwargs 接受额外的属性,并使用 setattr() 动态地设置它们
  2. display_info() 方法使用 getattr() 来显示产品的所有属性
  3. Inventory 类中的 update_attribute() 方法使用 getattr() 检索当前值,并使用 setattr() 更新它

这种方法提供了极大的灵活性,允许我们处理具有不同属性集的产品,而无需修改类定义。

总结

在这个实验中,你已经学习了如何使用 Python 对象属性,这是进行有效 Python 编程的基本技能。以下是你已经完成的总结:

  1. 创建类和对象:你学习了如何创建具有属性和方法的 Python 类,以及如何从这些类实例化对象。

  2. 访问对象属性:你探索了各种访问对象属性的方法,包括:

    • 使用点符号(object.attribute
    • 使用 getattr() 函数进行动态访问
  3. 修改对象属性:你学习了如何使用以下方法更改对象属性:

    • 使用点符号直接赋值(object.attribute = value
    • 使用 setattr() 函数进行动态修改
  4. 实际应用:你将这些概念应用于构建实际应用:

    • 一个简单的库存管理系统
    • 一个具有动态属性处理的增强系统

这些技能将帮助你构建更灵活、更强大的 Python 应用程序。理解如何使用对象属性对于数据处理、配置管理和创建可扩展的软件系统等任务至关重要。

为了进一步练习,请考虑使用以下附加功能扩展库存管理系统,例如:

  • 基于属性过滤产品
  • 按不同条件对产品进行排序
  • 为属性修改添加验证
  • 实现序列化以保存和加载库存数据