使用类和实例

您可以使用类来表示许多现实世界的情况。 一旦你编写了一个类,你将花费大部分时间处理从该类创建的实例。 您要做的首要任务之一是修改与特定实例关联的属性。 您可以直接修改实例的属性或编写以特定方式更新属性的方法。

Car类

让我们写一个代表汽车的新类。 我们的类将存储有关我们正在使用的汽车种类的信息,并且它将有一个方法来汇总这些信息:

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year): (1)
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year

    def get_descriptive_name(self): (2)
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

my_new_car = Car('audi', 'a4', 2024) (3)
print(my_new_car.get_descriptive_name())

Car 类中,我们先定义带有 self 参数的 __init__() 方法,就像我们在 Dog 类中所做的那样。 我们还给它三个其他参数:制造商、型号和年份。 __init__() 方法接收这些参数并将它们分配给将与此类实例相关联的属性。 当我们创建一个新的 Car 实例时,我们需要为我们的实例指定品牌、型号和年份。

我们定义了一个名为 get_descriptive_name() 的方法,它将汽车的 yearmakemodel 放入一个字符串中,整齐地描述汽车。 这将使我们不必单独打印每个属性的值。 要在此方法中使用属性值,我们使用 self.makeself.modelself.year。 在类之外,我们从 Car 类创建一个实例并将其分配给变量 my_new_car 。 然后我们调用 get_descriptive_name() 来显示我们有什么样的车:

2024 Audi A4

为了使课程更有趣,让我们添加一个随时间变化的属性。 我们将添加一个属性来存储汽车的总里程。

给属性指定默认值

创建实例时,可以定义属性而无需作为参数传入。 这些属性可以在 __init__() 方法中定义,并在其中为其分配默认值。

让我们添加一个名为 odometer_reading 的属性,它始终以 0 值开头。我们还将添加一个方法 read_odometer() 来帮助我们读取每辆汽车的里程表:

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year):
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0 (1)

    def get_descriptive_name(self):
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self): (2)
        """Print a statement showing the car's mileage."""
        print(f"This car has {self.odometer_reading} miles on it.")

my_new_car = Car('audi', 'a4', 2024)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

这一次,当 Python 调用 __init__() 方法来创建一个新实例时,它将品牌、型号和年份值存储为属性,就像在前面的示例中所做的那样。 然后 Python 创建一个名为 odometer_reading 的新属性并将其初始值设置为 0 。 我们还有一个名为 read_odometer() 的新方法,可以轻松读取汽车的里程数。

我们的汽车从 0 英里开始:

2024 Audi A4
This car has 0 miles on it.

没有多少汽车在里程表上精确显示为 0 英里,因此我们需要一种方法来更改此属性的值。

修改属性的值

您可以通过三种方式更改属性的值:您可以直接通过实例更改值,通过方法设置值,或通过方法增加值(向其添加一定数量)。 让我们看看这些方法中的每一种。

直接修改属性的值

修改属性值的最简单方法是通过实例直接访问属性。 这里我们直接将里程表读数设置为 23 :

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year):
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """Print a statement showing the car's mileage."""
        print(f"This car has {self.odometer_reading} miles on it.")

my_new_car = Car('audi', 'a4', 2024)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

我们使用点符号来访问汽车的 odometer_reading 属性,并直接设置它的值。 此行告诉 Python 获取实例 my_new_car,找到与其关联的属性 odometer_reading,并将该属性的值设置为 23:

2024 Audi A4
This car has 23 miles on it.

有时您会想像这样直接访问属性,但有时您会想编写一个方法来为您更新值。

通过方法修改属性值

拥有为您更新某些属性的方法会很有帮助。 您不是直接访问属性,而是将新值传递给在内部处理更新的方法。

这是一个显示名为 update_odometer() 的方法的示例:

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year):
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """Print a statement showing the car's mileage."""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
          """Set the odometer reading to the given value."""
          self.odometer_reading = mileage

my_new_car = Car('audi', 'a4', 2024)
print(my_new_car.get_descriptive_name())

my_new_car.update_odometer(23) (1)
my_new_car.read_odometer()
1 对 Car 的唯一修改是添加了 update_odometer()。 此方法接收里程值并将其分配给 self.odometer_reading。 使用 my_new_car 实例,我们以 23 作为参数调用 update_odometer() 。 这会将里程表读数设置为 23,并且 read_odometer() 会打印读数:
2024 Audi A4
This car has 23 miles on it.

我们可以扩展方法 update_odometer() 以在每次修改里程表读数时做额外的工作。 让我们添加一点逻辑来确保没有人试图回滚里程表读数:

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year):
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """Print a statement showing the car's mileage."""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        """
        Set the odometer reading to the given value.
        Reject the change if it attempts to roll the odometer back.
        """
        if mileage >= self.odometer_reading: (1)
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!") (2)

my_new_car = Car('audi', 'a4', 2024)
print(my_new_car.get_descriptive_name())

my_new_car.update_odometer(23)
my_new_car.read_odometer()

my_new_car.update_odometer(10)
my_new_car.read_odometer()
1 现在 update_odometer() 在修改属性之前检查新读数是否有意义。 如果 mileage 提供的值大于等于已有的 mileage,self.odometer_reading,可以将里程表读数更新为新的里程数。
2 如果新里程小于现有里程,您将收到无法回滚里程表的警告。

通过方法增加属性的值

有时您希望将属性的值增加一定数量,而不是设置一个全新的值。 假设我们购买了一辆二手车,并在购买和注册之间行驶了 100 英里。 这是一个允许我们传递这个增量并将该值添加到里程表读数的方法:

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year):
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """Print a statement showing the car's mileage."""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        """
        Set the odometer reading to the given value.
        Reject the change if it attempts to roll the odometer back.
        """
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """Add the given amount to the odometer reading."""
        self.odometer_reading += miles


my_used_car = Car('subaru', 'outback', 2019) (1)
print(my_used_car.get_descriptive_name())

my_used_car.update_odometer(23_500) (2)
my_used_car.read_odometer()

my_used_car.increment_odometer(100)
my_used_car.read_odometer()
1 新方法 increment_odometer() 获取英里数,并将此值添加到 self.odometer_reading。 首先,我们创建一辆二手车,my_used_car。
2 我们通过调用 update_odometer() 并将其传递给 23_500 将其里程表设置为 23,500。 最后,我们调用 increment_odometer() 并将其传递给 100 以添加我们在购买汽车和注册汽车之间行驶的 100 英里:
2019 Subaru Outback
This car has 23500 miles on it.
This car has 23600 miles on it.

您可以修改此方法以拒绝负增量,这样就不会有人使用此函数来回滚里程表。

您可以使用这样的方法来控制您的程序的用户如何更新里程表读数等值,但任何有权访问该程序的人都可以通过直接访问该属性来将里程表读数设置为任何值。 除了此处显示的基本检查之外,有效的安全性还需要特别注意细节。

自己试试
9-4.Number Served

从练习 9-1(第 162 页)中的程序开始。 添加一个默认值为 0 的名为 number_served 的属性。从此类创建一个名为 restaurant 的实例。 打印餐厅服务过的顾客数量,然后改变这个值再打印一次。

添加一个名为 set_number_served() 的方法,让您可以设置已服务的客户数量。 使用新数字调用此方法并再次打印该值。

添加一个名为 increment_number_served() 的方法,让您可以增加已服务的客户数量。 使用您喜欢的任何数字调用此方法,该数字可以表示在一天的营业时间内服务了多少客户。

9-5.Login Attempts

将名为 login_attempts 的属性添加到练习 9-3(第 162 页)中的用户类。 编写一个名为 increment_login_attempts() 的方法,将 login_attempts 的值递增 1。编写另一个名为 reset_login_attempts() 的方法,将 login_attempts 的值重置为 0。

创建 User 类的实例并多次调用 increment_login_attempts()。 打印 login_attempts 的值以确保它已正确递增,然后调用 reset_login_attempts()。 再次打印 login_attempts 以确保它已重置为 0。