创建和使用类

您几乎可以使用类对任何事物进行建模。 让我们从编写一个简单的类 Dog 开始,它代表一只狗——不是特定的一只狗,而是任何一只狗。 我们对大多数宠物狗了解多少? 嗯,他们都有名字和年龄。 我们也知道大多数狗会坐着翻身。 这两条信息(姓名和年龄)和这两种行为(坐下和翻身)将进入我们的 Dog 类,因为它们对大多数狗来说都很常见。 这个类将告诉 Python 如何制作一个代表狗的对象。 编写完我们的类后,我们用它来创建单独的实例,每个实例都代表一只特定的狗。

创建Dog类

从 Dog 类创建的每个实例都将存储一个名字和一个年龄,我们将赋予每只狗 sit() 和 roll_over() 的能力:

class Dog: (1)
    """A simple attempt to model a dog."""

    def __init__(self, name, age): (2)
        """Initialize name and age attributes."""
        self.name = name (3)
        self.age = age

    def sit(self): (4)
        """Simulate a dog sitting in response to a command."""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        """Simulate rolling over in response to a command."""
        print(f"{self.name} rolled over!")

这里有很多需要注意的地方,但不要担心。 您将在本章中看到这种结构,并且有很多时间来习惯它。 我们首先定义一个类,叫做 Dog。 按照惯例,大写的名称指的是 Python 中的类。 类定义中没有括号,因为我们是从头开始创建这个类。 然后我们写一个文档字符串来描述这个类做了什么。

init() 方法

作为类一部分的函数是方法。 您学到的有关函数的所有内容也适用于方法; 现在唯一实际的区别是我们调用方法的方式。 __init__() 方法是一个特殊的方法,每当我们基于 Dog 类创建一个新实例时,Python 就会自动运行该方法。 此方法有两个前导下划线和两个尾随下划线,这是一种有助于防止 Python 的默认方法名称与您的方法名称冲突的约定。 确保在 __init__() 的每一侧使用两个下划线。 如果你只在每一侧使用一个,当你使用你的类时,该方法将不会被自动调用,这可能会导致难以识别的错误。

我们将 __init__() 方法定义为具有三个参数:selfnameage。 方法定义中需要 self 参数,它必须在其他参数之前。 它必须包含在定义中,因为当 Python 稍后调用此方法(创建 Dog 的实例)时,方法调用将自动传递 self 参数。 每个与实例关联的方法调用都会自动传递 self,这是对实例本身的引用; 它使单个实例可以访问类中的属性和方法。 当我们创建 Dog 的实例时,Python 将调用 Dog 类的 __init__() 方法。 我们将向 Dog() 传递一个名字和一个年龄作为参数; self 是自动传递的,所以我们不需要传递它。 每当我们想要从 Dog 类创建一个实例时,我们将只提供最后两个参数的值,name 和 age。

__init__() 方法体中定义的两个变量都带有前缀 self 。 类中的每个方法都可以使用任何以 self 为前缀的变量,我们也可以通过从类创建的任何实例访问这些变量。 self.name = name 行获取与参数名称关联的值并将其分配给变量名称,然后将其附加到正在创建的实例。 同样的过程发生在 self.age = age 上。 可通过此类实例访问的变量称为属性。

Dog 类定义了另外两个方法:sit()roll_over() 。 因为这些方法不需要额外的信息来运行,我们只是将它们定义为具有一个参数,self。 我们稍后创建的实例将可以访问这些方法。 换句话说,他们将能够坐下并翻身。 目前,sit() 和 roll_over() 做的不多。 他们只是打印一条消息,说狗正坐着或翻身。 但这个概念可以扩展到现实情况:如果这个类是电脑游戏的一部分,这些方法将包含让一只动画狗坐下并翻身的代码。 如果编写此类是为了控制机器人,则这些方法将引导导致机器狗坐下和翻身的动作。

根据类创建实例

将类视为一组关于如何创建实例的指令。 Dog 类是一组指令,告诉 Python 如何使单个实例代表特定的狗。

让我们创建一个代表具体狗的实例:

my_dog = Dog('Willie', 6) (1)

print(f"My dog's name is {my_dog.name}.") (2)
print(f"My dog is {my_dog.age} years old.") (3)
1 我们在这里使用的 Dog 类是我们刚刚在上一个示例中编写的类。 在这里,我们告诉 Python 创建一只狗,它的名字是“Willie”,年龄是 6。 当 Python 读取这一行时,它会使用参数“Willie”和 6 调用 Dog 中的 __init__() 方法。__init__() 方法创建一个代表这只特定狗的实例,并使用我们提供的值设置 name 和 age 属性。 Python 然后返回代表这只狗的实例。 我们将该实例分配给变量 my_dog。 命名约定在这里很有帮助; 我们通常可以假设像 Dog 这样的大写名称是指类,而像 my_dog 这样的小写名称是指从类创建的单个实例。

访问属性

要访问实例的属性,您可以使用点表示法。 我们访问 my_dog 的属性名的值是这样写的:

my_dog.name

点符号在 Python 中经常使用。 此语法演示了 Python 如何查找属性的值。 在这里,Python 查看实例 my_dog,然后找到与 my_dog 关联的属性名称。 这与 Dog 类中称为 self.name 的属性相同。 我们使用相同的方法来处理属性 age 。

输出是我们对 my_dog 的了解的总结:

My dog's name is Willie.
My dog is 6 years old.

调用方法

从类 Dog 创建实例后,我们可以使用点表示法调用 Dog 中定义的任何方法。 让我们的狗坐下并翻身:

class Dog:
    """A simple attempt to model a dog."""

    def __init__(self, name, age):
        """Initialize name and age attributes."""
        self.name = name
        self.age = age

    def sit(self):
        """Simulate a dog sitting in response to a command."""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        """Simulate rolling over in response to a command."""
        print(f"{self.name} rolled over!")


my_dog = Dog('Willie', 6)
my_dog.sit()
my_dog.roll_over()

要调用方法,请提供实例名称(在本例中为 my_dog)和您要调用的方法,以点分隔。 当 Python 读取 my_dog.sit() 时,它会在类 Dog 中查找方法 sit() 并运行该代码。 Python 以相同的方式解释 my_dog.roll_over() 行。

现在威利按照我们告诉他的去做:

Willie is now sitting.
Willie rolled over!

这个语法非常有用。 当属性和方法被赋予适当的描述性名称,如 name、age、sit() 和 roll_over() 时,我们可以很容易地推断出一段代码(即使是我们以前从未见过的)应该做什么。

创建多个实例

您可以根据需要从一个类中创建任意数量的实例。 让我们创建第二只狗,叫做 your_dog:

class Dog:
    """A simple attempt to model a dog."""

    def __init__(self, name, age):
        """Initialize name and age attributes."""
        self.name = name
        self.age = age

    def sit(self):
        """Simulate a dog sitting in response to a command."""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        """Simulate rolling over in response to a command."""
        print(f"{self.name} rolled over!")


my_dog = Dog('Willie', 6)
your_dog = Dog('Lucy', 3)

print(f"My dog's name is {my_dog.name}.")
print(f"My dog is {my_dog.age} years old.")
my_dog.sit()

print(f"\nYour dog's name is {your_dog.name}.")
print(f"Your dog is {your_dog.age} years old.")
your_dog.sit()

在本例中,我们创建了一只名为 Willie 的狗和一只名为 Lucy 的狗。 每只狗都是一个单独的实例,具有自己的一组属性,能够执行相同的一组操作:

My dog's name is Willie.
My dog is 6 years old.
Willie is now sitting.

Your dog's name is Lucy.
Your dog is 3 years old.
Lucy is now sitting.

即使我们为第二只狗使用相同的名字和年龄,Python 仍会从 Dog 类创建一个单独的实例。 您可以根据需要从一个类中创建尽可能多的实例,只要您为每个实例指定一个唯一的变量名称,或者它在列表或字典中占据一个唯一的位置。

自己试试
9-1.Restaurant

创建一个名为 Restaurant 的类。 Restaurant 的 init() 方法应该存储两个属性:restaurant_name 和 cuisine_type。 创建一个名为 describe_restaurant() 的方法来打印这两条信息,并创建一个名为 open_restaurant() 的方法来打印一条消息,表明餐厅已营业。 从您的班级创建一个名为 restaurant 的实例。 分别打印两个属性,然后调用这两个方法。

9-2.Three Restaurants

从练习 9-1 开始上课。 从该类创建三个不同的实例,并为每个实例调用 describe_restaurant()。

9-3.Users

创建一个名为用户的类。 创建两个名为 first_name 和 last_name 的属性,然后创建几个通常存储在用户配置文件中的其他属性。 创建一个名为 describe_user() 的方法来打印用户信息的摘要。 创建另一个名为 greet_user() 的方法,它向用户打印个性化的问候语。 创建代表不同用户的多个实例,并为每个用户调用这两种方法。