属性访问与绑定方法

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在这个实验中,你将学习 Python 中的属性访问。你将探索如何使用 getattr()setattr() 等函数来有效地操作对象属性。

此外,你还将对绑定方法进行实验。本实验将引导你了解这些概念,并且在这个过程中,你将创建一个名为 tableformat.py 的文件。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/build_in_functions("Build-in Functions") python/ModulesandPackagesGroup -.-> python/using_packages("Using Packages") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") subgraph Lab Skills python/function_definition -.-> lab-132491{{"属性访问与绑定方法"}} python/build_in_functions -.-> lab-132491{{"属性访问与绑定方法"}} python/using_packages -.-> lab-132491{{"属性访问与绑定方法"}} python/classes_objects -.-> lab-132491{{"属性访问与绑定方法"}} python/data_collections -.-> lab-132491{{"属性访问与绑定方法"}} end

理解 Python 中的属性访问

在 Python 中,对象是一个基本概念。对象可以将数据存储在属性中,属性就像是存储值的具名容器。你可以把属性看作是属于对象的变量。有几种方法可以访问这些属性。最直接且常用的方法是点号 (.) 表示法。不过,Python 还提供了一些特定的函数,让你在处理属性时拥有更多的灵活性。

点号表示法

让我们先创建一个 Stock 对象,看看如何使用点号表示法来操作它的属性。点号表示法是一种简单直观的访问和修改对象属性的方式。

首先,打开一个新的终端并启动 Python 交互式 shell。在这里,你可以逐行编写和执行 Python 代码。

## Open a new terminal and run Python interactive shell
python3

## Import the Stock class from the stock module
from stock import Stock

## Create a Stock object
s = Stock('GOOG', 100, 490.1)

## Get an attribute
print(s.name)    ## Output: 'GOOG'

## Set an attribute
s.shares = 50
print(s.shares)  ## Output: 50

## Delete an attribute
del s.shares
## If we try to access s.shares now, we'll get an AttributeError

在上面的代码中,我们首先从 stock 模块导入 Stock 类。然后创建一个名为 sStock 类实例。要获取 name 属性的值,我们使用 s.name。要更改 shares 属性的值,我们只需为 s.shares 赋一个新值。如果我们想删除一个属性,可以使用 del 关键字,后面跟上属性名。

属性访问函数

Python 提供了四个非常有用的内置函数,用于属性操作。这些函数在处理属性时能让你拥有更多的控制权,特别是当你需要动态处理属性时。

  1. getattr() - 此函数用于获取属性的值。
  2. setattr() - 它允许你设置属性的值。
  3. delattr() - 你可以使用此函数删除属性。
  4. hasattr() - 此函数用于检查对象中是否存在某个属性。

让我们看看如何使用这些函数:

## Create a new Stock object
s = Stock('GOOG', 100, 490.1)

## Get an attribute
print(getattr(s, 'name'))       ## Output: 'GOOG'

## Set an attribute
setattr(s, 'shares', 50)
print(s.shares)                 ## Output: 50

## Check if an attribute exists
print(hasattr(s, 'name'))       ## Output: True
print(hasattr(s, 'symbol'))     ## Output: False

## Delete an attribute
delattr(s, 'shares')
print(hasattr(s, 'shares'))     ## Output: False

当你需要动态处理属性时,这些函数特别有用。你可以使用变量名,而不是硬编码的属性名。例如,如果你有一个变量存储了属性名,你可以将该变量传递给这些函数,以对相应的属性执行操作。这让你的代码更具灵活性,特别是在以更动态的方式处理不同的对象和属性时。

使用 getattr() 进行通用对象处理

getattr() 函数是 Python 中的一个强大工具,它允许你以动态方式访问对象的属性。当你想以通用方式处理对象时,这尤其有用。你可以使用 getattr() 处理任何具有所需属性的对象,而不是编写特定于某一对象类型的代码。这种灵活性使你的代码更具可复用性和适应性。

处理多个属性

让我们先学习如何使用 getattr() 函数访问对象的多个属性。当你需要从对象中提取特定信息时,这是一种常见的场景。

首先,如果你关闭了之前的 Python 交互式 shell,请重新打开它。你可以在终端中运行以下命令来实现:

## Open a Python interactive shell if you closed the previous one
python3

接下来,我们将导入 Stock 类并创建一个 Stock 对象。Stock 类表示一支股票,具有 namesharesprice 等属性。

## Import the Stock class and create a stock object
from stock import Stock
s = Stock('GOOG', 100, 490.1)

现在,我们将定义一个要访问的属性名列表。这个列表将帮助我们遍历这些属性并打印它们的值。

## Define a list of attribute names
fields = ['name', 'shares', 'price']

最后,我们将使用 for 循环遍历属性名列表,并使用 getattr() 访问每个属性。每次迭代时,我们将打印属性名及其值。

## Access each attribute using getattr()
for name in fields:
    print(f"{name}: {getattr(s, 'name')}" if name == 'name' else f"{name}: {getattr(s, name)}")

运行这段代码时,你将看到以下输出:

name: GOOG
shares: 100
price: 490.1

这个输出表明,我们能够使用 getattr() 函数访问并打印 Stock 对象的多个属性的值。

getattr() 的默认值

getattr() 函数还提供了一个有用的特性:当你尝试访问的属性不存在时,你可以指定一个默认值。这可以防止你的代码引发 AttributeError,使代码更加健壮。

让我们看看这是如何工作的。首先,我们将尝试访问 Stock 对象中不存在的属性。我们将使用 getattr() 并提供一个默认值 'N/A'

## Try to access an attribute that doesn't exist
print(getattr(s, 'symbol', 'N/A'))  ## Output: 'N/A'

在这种情况下,由于 Stock 对象中不存在 symbol 属性,getattr() 返回默认值 'N/A'

现在,让我们将其与访问现有属性进行比较。我们将访问 Stock 对象中确实存在的 name 属性。

## Compare with an existing attribute
print(getattr(s, 'name', 'N/A'))    ## Output: 'GOOG'

在这里,getattr() 返回 name 属性的实际值,即 'GOOG'

处理对象集合

当你需要处理对象集合时,getattr() 函数会变得更加强大。让我们看看如何使用它来处理股票投资组合。

首先,我们将从 stock 模块导入 read_portfolio 函数。这个函数从 CSV 文件中读取股票投资组合,并返回一个 Stock 对象列表。

## Import the portfolio reading function
from stock import read_portfolio

接下来,我们将使用 read_portfolio 函数从名为 portfolio.csv 的 CSV 文件中读取投资组合。

## Read the portfolio from CSV file
portfolio = read_portfolio('portfolio.csv')

最后,我们将使用 for 循环遍历投资组合中的 Stock 对象列表。对于每支股票,我们将使用 getattr() 访问 nameshares 属性并打印它们的值。

## Print the name and shares of each stock
for stock in portfolio:
    print(f"Stock: {getattr(stock, 'name')}, Shares: {getattr(stock, 'shares')}")

这种方法使你的代码更具灵活性,因为你可以将属性名作为字符串处理。这些字符串可以作为参数传递或存储在数据结构中,这样你就可以轻松更改要访问的属性,而无需修改代码的核心逻辑。

使用属性访问创建表格格式化器

在编程中,属性访问是一个基本概念,它允许我们与对象的属性进行交互。现在,我们将把所学的属性访问知识付诸实践。我们将创建一个实用工具:表格格式化器。这个格式化器会接收一组对象,并以表格形式显示它们,使数据更易于阅读和理解。

创建 tableformat.py 模块

首先,我们需要创建一个新的 Python 文件。这个文件将包含我们表格格式化器的代码。

要创建该文件,请按照以下步骤操作:

  1. 在 WebIDE 中,点击“文件”菜单。
  2. 从下拉菜单中选择“新建文件”。
  3. 将新创建的文件保存为 /home/labex/project/tableformat.py

现在我们有了文件,让我们在 tableformat.py 中编写 print_table() 函数的代码。这个函数将负责以表格形式格式化并打印我们的对象。

def print_table(objects, fields):
    """
    Print a collection of objects as a formatted table.

    Args:
        objects: A sequence of objects
        fields: A list of attribute names
    """
    ## Print the header
    headers = fields
    for header in headers:
        print(f"{header:>10}", end=' ')
    print()

    ## Print the separator line
    for header in headers:
        print("-" * 10, end=' ')
    print()

    ## Print the data
    for obj in objects:
        for field in fields:
            value = getattr(obj, field)
            print(f"{value:>10}", end=' ')
        print()

让我们来详细分析这个函数的功能:

  1. 它接受两个参数:一个对象序列和一个属性名列表。对象序列是我们要显示的数据,属性名列表告诉函数要显示对象的哪些属性。
  2. 它打印表头行。表头行包含我们感兴趣的属性名称。
  3. 它打印分隔线。这条线有助于在视觉上分隔表头和数据。
  4. 对于序列中的每个对象,它打印每个指定属性的值。它使用 getattr() 函数来访问每个对象的属性值。

现在,让我们测试一下 print_table() 函数,看看它是否按预期工作。

## Open a Python interactive shell
python3

## Import our modules
from stock import read_portfolio
import tableformat

## Read the portfolio data
portfolio = read_portfolio('portfolio.csv')

## Print the portfolio as a table with name, shares, and price columns
tableformat.print_table(portfolio, ['name', 'shares', 'price'])

当你运行上述代码时,你应该会看到以下输出:

      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44

我们的 print_table() 函数的一大优点是它的灵活性。我们只需更改 fields 列表,就可以更改显示的列。

## Just show shares and name
tableformat.print_table(portfolio, ['shares', 'name'])

运行这段代码将得到以下输出:

    shares       name
---------- ----------
       100         AA
        50        IBM
       150        CAT
       200       MSFT
        95         GE
        50       MSFT
       100        IBM

这种方法的强大之处在于它的通用性。只要我们知道要显示的属性名称,就可以使用同一个 print_table() 函数为任何类型的对象打印表格。这使得我们的表格格式化器成为编程工具包中非常实用的工具。

✨ 查看解决方案并练习

理解 Python 中的绑定方法

在 Python 中,方法是一种特殊类型的属性,你可以调用它们。当你通过对象访问方法时,你得到的是所谓的“绑定方法”。绑定方法本质上是与特定对象绑定的方法。这意味着它可以访问对象的数据并对其进行操作。

将方法作为属性访问

让我们使用 Stock 类开始探索绑定方法。首先,我们将了解如何将方法作为对象的属性进行访问。

## Open a Python interactive shell
python3

## Import the Stock class and create a stock object
from stock import Stock
s = Stock('GOOG', 100, 490.10)

## Access the cost method without calling it
cost_method = s.cost
print(cost_method)  ## Output: <bound method Stock.cost of <stock.Stock object at 0x...>>

## Call the method
result = cost_method()
print(result)  ## Output: 49010.0

## You can also do this in one step
print(s.cost())  ## Output: 49010.0

在上面的代码中,我们首先导入 Stock 类并创建一个实例。然后,我们在不实际调用的情况下访问 s 对象的 cost 方法。这会得到一个绑定方法。当我们调用这个绑定方法时,它会根据对象的数据计算成本。你也可以一步直接在对象上调用该方法。

使用 getattr() 访问方法

另一种访问方法的方式是使用 getattr() 函数。这个函数允许你通过名称获取对象的属性。

## Get the cost method using getattr
cost_method = getattr(s, 'cost')
print(cost_method)  ## Output: <bound method Stock.cost of <stock.Stock object at 0x...>>

## Call the method
result = cost_method()
print(result)  ## Output: 49010.0

## Get and call in one step
result = getattr(s, 'cost')()
print(result)  ## Output: 49010.0

在这里,我们使用 getattr()s 对象获取 cost 方法。和之前一样,我们可以调用绑定方法来获取结果。你甚至可以在一行代码中完成获取和调用方法的操作。

绑定方法及其对象

绑定方法始终会保留对其所属对象的引用。这意味着即使你将方法存储在变量中,它仍然知道自己属于哪个对象,并可以访问该对象的数据。

## Store the cost method in a variable
c = s.cost

## Call the method
print(c())  ## Output: 49010.0

## Change the object's state
s.shares = 75

## Call the method again - it sees the updated state
print(c())  ## Output: 36757.5

在这个例子中,我们将 cost 方法存储在变量 c 中。当我们调用 c() 时,它会根据对象的当前数据计算成本。然后,我们更改 s 对象的 shares 属性。当我们再次调用 c() 时,它会使用更新后的数据计算新的成本。

探索绑定方法的内部结构

绑定方法有两个重要的属性,它们能为我们提供更多关于该方法的信息。

  • __self__:这个属性引用方法所绑定的对象。
  • __func__:这个属性是表示该方法的实际函数对象。
## Get the cost method
c = s.cost

## Examine the bound method attributes
print(c.__self__)  ## Output: <stock.Stock object at 0x...>
print(c.__func__)  ## Output: <function Stock.cost at 0x...>

## You can manually call the function with the object
print(c.__func__(c.__self__))  ## Output: 36757.5 (same as c())

在这里,我们访问绑定方法 c__self____func__ 属性。我们可以看到,__self__s 对象,而 __func__cost 函数。我们甚至可以通过将对象作为参数传递来手动调用该函数,其结果与直接调用绑定方法相同。

带参数方法的示例

让我们看看绑定方法如何与带参数的方法(如 sell() 方法)一起工作。

## Get the sell method
sell_method = s.sell

## Examine the method
print(sell_method)  ## Output: <bound method Stock.sell of <stock.Stock object at 0x...>>

## Call the method with an argument
sell_method(25)
print(s.shares)  ## Output: 50

## Call the method manually using __func__ and __self__
sell_method.__func__(sell_method.__self__, 10)
print(s.shares)  ## Output: 40

在这个例子中,我们将 sell 方法作为绑定方法获取。当我们使用参数调用它时,它会更新 s 对象的 shares 属性。我们也可以使用 __func____self__ 属性手动调用该方法,并传递参数。

理解绑定方法有助于你深入了解 Python 对象系统的工作原理,这对于调试、元编程和创建高级编程模式非常有用。

总结

在本次实验中,你学习了 Python 的属性访问系统及其底层机制。你现在知道如何使用点号表示法以及 getattr()setattr()delattr()hasattr() 等函数来访问对象属性。此外,你还了解了如何使用 getattr() 进行通用且灵活的对象处理,以及如何为任意对象集合创建表格格式化器。

你还掌握了绑定方法的概念,以及它们如何与所属对象保持关联。这些基础概念对于高级 Python 编程技术(如内省、反射和元编程)至关重要。理解属性访问能让你编写更灵活、更强大的代码,以处理各种类型的对象。