类

类 属于 Python

49 个练习

关于 类

Classes把数据和行为结合在一起。类用于创建捆绑在一起的数据和行为的副本或instances,也就是通常所说的objects。对象可以表示现实世界中的实体(例如汽车或猫),也可以表示更抽象的概念(例如整数、交通工具或哺乳动物)。类是面向对象编程(OOP)方法不可或缺的一部分,这种方法要求程序员把问题建模为一个或多个对象,这些对象彼此交互、维护状态并对数据做出操作。

类

类是新的_对象类型_的定义,对象的新instances正是由它创建的。类通常把数据(称为 fields、attributes、properties、data members 或 variables)与操作这些数据的代码或函数(称为 methods)捆绑在一起。从这个意义上说,类是一份_蓝图_,或者一套指令,可以据此构建并使用许多类型相似的对象。一个复杂的程序可以有许多类,每个类都能构建出许多不同种类的对象。从类构建并定制一个对象的过程称为instantiation(或创建类的实例)。

在 Python 中,类的定义很直接:

class MyClass:
    # Class body goes here

类属性

类字段(也称为属性、特性、数据成员或变量)可以添加到类的主体中。

class MyClass:
    number = 5
    string = "Hello!"

可以创建MyClass的一个实例(对象),并把它绑定到一个名字:

>>> new_object = MyClass()

# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>

Class attributes由从类创建的所有对象(或实例)共享,可以通过dot notation访问,也就是把.放在对象名之后、属性名之前:

>>> new_object = MyClass()

# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5

# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'

# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()

>>> second_new_object
<__main__.MyClass at 0x15ad99970>

# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True

类属性定义在类自身的主体中,位于所有其他方法之前。它们归类所有,因此可以在各个实例之间共享。由于这些属性由类的所有实例共享,可以直接从类访问和修改它们的值。修改类属性的值会改变_从该类实例化出的所有对象_的值:

>>> obj_one = MyClass()
>>> obj_two = MyClass()

# Accessing a class attribute from an object.
>>> obj_two.number
5

# Accessing the class attribute from the class itself.
>>> MyClass.number
5

# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27

# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27

>>> obj_two.number
27

让一堆对象的数据始终保持同步,并没有多大用处。好在从类创建出来的对象可以用自己的instance attributes(或实例属性、变量或字段)来定制。顾名思义,实例属性是每个对象独有的,可以独立修改。

用__init__()定制对象实例化

特殊的“dunder 方法”(是“double underscore method”的简称)__init__()用于定制类的实例,可以为对象初始化实例属性。由于它在初始化实例属性方面的作用,__init__()也被称为class constructor或initializer。__init__()接受一个名为self的必需形参,它指向刚刚初始化或创建的那个对象。实例属性的数据随后可以作为__init__()的实参传入,排在self形参之后。

下面,MyClass现在有了名为location_x和location_y的实例属性。可以看到,这两个属性分别被赋值为传给__init__()的location实参(一个元组)的第一个和第二个下标。现在,当你实例化这个类并创建对象时,类实例的location_x和location_y属性就会被初始化:

class MyClass:
    # These are class attributes, variables, or fields.
    number = 5
    string = "Hello!"

    # This is the class "constructor", with a "location" parameter that is a tuple.
    def __init__(self, location):

        # This is an instance or object property, attribute, or variable.
        # Note that we are unpacking the tuple argument into two separate instance variables.
        self.location_x = location[0]
        self.location_y = location[1]

# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))

# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))

# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1

>>> new_object_two.location_x
-8

注意,初始化上面的MyClass时,你只需传入一个实参,因为类被调用时 Python 会自动传入self。

Advanced

创建实例变量的另一种方法是:通过对象访问类变量,然后把它改成别的值:

>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'

>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'

>>> obj_one.string
'Hello!'

现在,看看类变量改变时会发生什么:

>>> MyClass.string = "World!"
>>> obj_two.string

# obj_two.string has not changed
'Hi!'

>>> obj_one.string

# obj_one.string changed
'World!'

对于obj_two来说,string属性现在是一个_实例变量_,但在obj_one中它仍然是_类变量_。

这也可以在类内部完成:

class Demo:
    new_var = 3

    def add_two(self):
        self.new_var += 2

一旦调用<object>.add_two()并读到self.new_var += 2,new_var就会从类变量变成同名的实例变量。

在初始化时,如果类的所有实例都需要某些属性以相同的值开始,这种做法会很有用。不过,实例变量随后会遮蔽类变量,使类变量在发生遮蔽的那个实例中无法访问。鉴于此,通过__init__()方法以self.<attribute>的形式设置实例属性,可能更安全,也更清晰。

方法

method是绑定到类本身(称为类方法,稍后的练习会深入讨论)或类的_实例_(对象)上的function。作用于对象或实例的方法,需要用self作为第一个形参来定义;其余的形参可以像定义“普通”或非绑定函数那样定义。作用于类的方法需要用@classmethod装饰器定义,并且(按照惯例)用cls作为第一个形参。类方法直接在类上调用,不必先从类创建对象。

class MyClass:
    number = 5
    string = "Hello!"

    # Class constructor.
    def __init__(self, location):

        # Instance properties
        self.location_x = location[0]
        self.location_y = location[1]

    # Class method. Uses the @classmethod decorator, and cls as the first parameter.
    # Can be called without first making an instance of the class.
    @classmethod
    def change_string(cls, new_string):
        #Class attributes are referred to with cls.
        cls.string = new_string
        return cls.string

    # Instance method.  Note "self" as first parameter.
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

和访问属性一样,调用方法只需在对象名之后、方法名之前加一个.。被调用的方法不需要把对象副本作为第一个形参,Python 会自动填入self:

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)

# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)

在实例方法内部,可以像在类外部一样访问类属性:

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    # Alter instance variable location_x and location_y
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

    # Alter class variable number for all instances from within an instance.
    def increment_number(self):
        # Increment the 'number' class variable by 1.
        MyClass.number += 1


>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5

>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6

用pass做占位或桩实现

在之前的概念练习和实践练习的桩中,你会看到函数体内用pass关键字代替实际代码。

pass关键字是一个语法上合法的占位符,它能防止 Python 因为函数或类定义为空而抛出语法错误。本质上,它是在对 Python 解释器说:“别担心!我_会_在这里写代码的,只是现在还没写。”

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    # Alter instance variable location_x and location_y.
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return  self.location_x, self.location_y

    # Alter class variable number for all instances.
    def increment_number(self):
        # Increment the 'number' class variable by 1.
        MyClass.number += 1

    # This will compile and run without error, but has no current functionality.
    def pending_functionality(self):
       # Stubbing or place-holding the body of this method.
       pass
通过 GitHub 编辑 该链接会在新窗口或标签页中打开

学习 类

练习已锁定

再解锁 6 个练习即可练习 类