Python类

it2024-12-19  10

1.类

面向对象编程是最有效的软件编写方法之一。在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。编写类时,你创建的一大类对象都有通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后根据需要赋予每个对象独特的个性。 根据类来创建对象被称为实例化,这让你能够使用类的实例。

1.1 创建和使用类

使用类几乎可以模拟任何东西。下面将编写一个表示小狗的简单类Dog–它表示的不是特定小狗,而是任何小狗。一般小狗都有姓名和年龄两项信息,许多的小狗还会蹲下和打滚,我们的Dog类将包含它们。

1.1.1 创建Dog类

根据Dog类创建的每个实例都将存储名字和年龄。我们赋予每条小狗蹲下(sit())和打滚(roll_over())的能力:

class Dog(): """模拟创建一只小狗🐶""" def __init__(self, name, age): self.name = name self.age = age def sit(self): """模拟小狗蹲下""" print(self.name.title() + " is now sitting.") def roll_over(self): """模拟小狗被命令是打滚""" print(self.name.title + " rolled over.")

根据类模拟创建实例(实例化):

my_dog = Dog('haha',16) print("My dog's name is " + my_dog.name.title() + ".") print("My dog is " + str(my_dog.age) + "years old.")

输出:

My dog's name is Haha. My dog is 16 years old.

对上面的代码解读:

class Dog():

定义一个名为Dog的类。首字母大写的名称指的是类。类定义中的括号是空的,是因为我们要从空白创建这个类。

方法 __init__()

类中的函数称之为方法,前面学到过的有关函数的一切都适用于方法。init()是一个特殊的方法,每当你根据Dog类创建新的实例时,Python都会自动运行它。在这个方法的名称中,开头和结尾各有两个下划线,旨在避免Python默认方法与普通方法发生名称冲突。 将方法__init__()定义成了包含3个形参:self、name、age。在这个定义中,形参self必不可少且必须位于其他形参的前面。 为什么必须在方法定义中包含形参self呢? 因为Python调用这个 init()方法来创建Dog实例时,都将自动传入实参self。每个与类相关联的方法调用都自动传递实参self,它是一个指向本身的引用,让实例能够访问类中的属性和方法。 我们创建Dog实例时,Python将调用Dog类的方法__init__()。我们将通过实参向Dog传递名字和年龄;self会自动传递,因此我们不需要传递它。每当我们根据Dog类创建实例时,都只需要给最后两个形参(name和age)提供值。

self.name = name self.age = age

这两行代码定义的两个变量都有前缀self。以self为前缀的两个变量都可以供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量。self.name = name 获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。self.age = age 的作用类似。像这样可以通过实例访问的变量称之为属性。

def sit(self): """模拟小狗蹲下""" print(self.name.title() + " is now sitting.") def roll_over(self): """模拟小狗被命令是打滚""" print(self.name.title + " rolled over.")

定义了两个方法:sit()和roll_over()。由于这些方法不需要额外的信息,因此它们只有一个形参self。

1.1.2 根据类创建实例

可以将类看做如何创建实例的说明。Dog是一系列说明,让Python知道如何创建表示特定小狗的实例。 下面创建一个表示特定小狗的实例:

my_dog = Dog('haha',16)

我们使用Python创建一条名为’haha’、年龄为16岁的小狗。遇到这行代码时,Python使用实参’haha’和16调用类中的方法 init()。方法__init__()创建一个表示特定小狗的示例,并使用我们提供的值来设置属性name和age。方法__iit__()并未显式地包含return语句,但Python自动返回一个表示这条小狗的实例。我们将这个实例存储在变量my_dog中。 ①.访问属性 要访问属性,可使用句点表示法。我们编写如下代码来访问my_dog的属性的值:

my_dog.name my_dog.age

②调用方法 根据Dog类创建实例后,可以使用句点表示法来调用类中定义的任何方法。下面来让小狗蹲下和打滚:

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

2.使用类和实例

可以使用类来模拟现实世界中的很多情景。类编写好后,大部分时间都将花在使用根据类创建的实例上。需要执行的一个重要任务就是修改实例的属性。可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。

2.1 Car类

下面编写一个表示汽车的类,它存储了有关汽车的信息,还有一个汇总这些信息的方法:

class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper()

根据类创建实例:

my_new_car = Car("bmw", "X7", 2019) print(my_new_car.get_descriptive_name())

输出:

2019 BMW X7

代码解读:

def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year

我们定义了方法__init__()。与前面的一样,这个方法的第一个形参是self;我们还在这个方法中包含了另外三个形参:make、model和year。方法__init__()接受这些形参的值,并将其存储在根据这个类创建的实例的属性中。

def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper()

定义了一个名为get_descriptive_name()的方法,在这个方法中访问属性的值,我们使用了self.name、self.model和self.year。

my_new_car = Car("bmw", "X7", 2019)

我们根据类创建实例,并将其存储到变量my_new_car中。

2.2 给属性指定默认值

类中的每个属性都必须有初始值,哪怕这个值是0或者空字符串。在这些情况下,如设置默认值时,在方法__init__()内指定这种初始值是可行的;如果对某个属性这样做了,就无需包含为它提供初始值的形参。 下面来添加一个名为 odometer_reading(里程数)的属性,其初始值总是为0。我们还添加了一个名为read_odometer()的方法,用于读取汽车的里程表:

class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.")

实例化:

my_new_car = Car("bmw", "A6", 2019) print(my_new_car.get_descriptive_name()) my_new_car.read_odometer()

输出:

2019 BMW A6 The car has 0 on it. 出售里程表读数为0的汽车不多,所以我们还需要一个修改该属性的值的途径。

2.3 修改属性的值

有3种不同的方法来修改属性的值: 1.直接通过实例进行修改 2.通过方法进行设置 3.通过方法进行递增(增加特定的值) 下面依次进行详细解读: 1.直接通过实例进行修改属性的值 要修改属性的值,最简单的方式是通过实例直接访问它。下面的代码直接将里程表度数设置为23:

my_new_car = Car("bmw", "A6", 2019) print(my_new_car.get_descriptive_name()) my_new_car.odometer = 23 my_new_car.read_odometer()

输出:

2019 BMW A6 The car has 23 on it.

2.通过方法修改属性的值 如果有更新属性的方法,就不需要直接访问属性,而是将值传递给方法,由方法在内部进行更新。

class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.") def update_mileage(self, mileage): """修改汽车里程数""" self.odometer = mileage

实例化:

my_new_car = Car("bmw", "A6", 2019) print(my_new_car.get_descriptive_name()) my_new_car.update_mileage(99) my_new_car.read_odometer()

输出:

2019 BMW A6 The car has 99 on it.

可以对方法 update_odometer()进行扩展,使其禁止任何人将里程表读数往回调:

def update_mileage(self, odometer): """修改汽车里程数""" if odometer < self.odometer: print("You can't rool back an odometer!") else: self.odometer = odometer

3.通过方法对属性值进行递增 有时候需要将属性值递增特定的量,而不是将其设置为全新的值。假设我们购买了一辆二手车,且从购买到登记期间增加了100英里的里程,下面的方法让我们能够传递这个增量,并相应的增加里程表的读数:

class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.") def update_mileage(self, odometer): """修改汽车里程数""" if odometer < self.odometer: print("You can't rool back an odometer!") else: self.odometer = odometer def increment_odometer(self,miles): """将里程数增加指定的量,不可小于0""" if miles >=0: self.odometer += miles else: print("You can't increase less than 0")

实例化:

my_new_car = Car("bmw", "A6", 2019) print(my_new_car.get_descriptive_name()) my_new_car.update_mileage(99) my_new_car.increment_odometer(-10000) my_new_car.read_odometer()

输出:

2019 BMW A6 You can't increase less than 0 The car has 99 on it.

3. 继承

编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可以使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类所有的属性和方法,同时还可以自定义自己的属性和方法。

3.1 子类的方法 init()

下面创建一个简单的ElectricCar类版本,它具备Car类的所有功能:

# 父类 class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.") def update_mileage(self, odometer): """修改汽车里程数""" if odometer < self.odometer: print("You can't rool back an odometer!") else: self.odometer = odometer def increment_odometer(self,miles): """将里程数增加指定的量,不可小于0""" if miles >=0: self.odometer += miles else: print("You can't increase less than 0") # 子类 class ElectricCar(Car): """模拟简单的电动汽车""" def __init__(self, make, model, year): """初始化父类属性""" super().__init__(make, model, year)

实例化:

my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())

输出:

2016 TESLA MODEL S

创建子类时,父类必须包含在当前文件中,且位于子类前面。

# 父类 class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.") def update_mileage(self, odometer): """修改汽车里程数""" if odometer < self.odometer: print("You can't rool back an odometer!") else: self.odometer = odometer def increment_odometer(self,miles): """将里程数增加指定的量,不可小于0""" if miles >=0: self.odometer += miles else: print("You can't increase less than 0") # 子类 class ElectricCar(Car): """模拟简单的电动汽车""" def __init__(self, make, model, year): """初始化父类属性""" super().__init__(make, model, year)

实例化:

my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())

代码解读: 创建子类时,父类必须包含在当前文件中,且位于子类前面。 我们定义了子类ElectricCar。定义子类时,必须在括号内指定父类名称。

def __init__(self, make, model, year):

方法__init__()接受创建Car实例所需的信息。

super().__init__(make, model, year)

super()是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类所有的属性。父类也称为超类(superclass),名称super因此得名。

my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())

创建ElectricCar类的一个实例,并将其存储在变量my_tesla中。这行代码调用ElectricCar类中定义的方法__init__(),后者让Python调用父类Car中定义的方法__init__()。

3.2 Python2.7中的继承

Python2.7中,继承语法稍有不同:

函数super()需要两个实参:子类名和对象self。

3.3 给子类定义属性和方法

让一个类继承另一个类后,可添加区分子类和父类所需要的新属性和方法。 下面来添加一个电动汽车特有的属性(电瓶),以及一个描述该属性的方法。我们将存储电瓶容量,并编写一个打印电瓶描述的方法:

class ElectricCar(Car): """模拟简单的电动汽车""" def __init__(self, make, model, year): """初始化父类属性""" super().__init__(make, model, year) self.battery_size = 70 def describe_battery(self): """打印一条描述电池容量的信息""" print("The car has a " + str(self.battery_size) + "-KWH battery.")

实例化:

my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name()) my_tesla.describe_battery()

输出:

2016 TESLA MODEL S The car has a 70-KWH battery.

我们添加了新属性 self.battery_size,并设置其初始值为70。根据Electric类创建的所有实例都将包含这个属性,但是所有的Car实例都不包含它。 添加了一个名为describe_battery()的方法,它打印有关电瓶的信息。

3.4 重写父类的方法

对于父类的方法,只要它不符合模拟的实物的行为,都可以对其进行重写。为此,可以在子类中定义一个这样的方法,即它要与重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。 假设Car类中有一个名为 fill_gas_tank()的方法,这对电动汽车来说毫无意义,因此你可能想重写它:

class ElectricCar(Car): """模拟简单的电动汽车""" --snip-- def fill_gas_tank(self): """电动车没有油箱""" print("This car doesn't need a gas tank!")

假如有人对电动汽车调用方法 fill_gas_tank(),Pytohn将忽略Car类中的方法 fill_gas_tank(),转而运行上述代码。

3.5 将实例用作属性

使用代码模拟实物时,你可能会发现给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。可以将大型的类拆分成许多协同工作的小类。 例如,不断给ElectricCar类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可以把这些属性和方法提取出来,放到另一个名为Battery的类中,并将一个Battery实例用作ElectricCar类的一个属性:

class ElectricCar(Car): """模拟简单的电动汽车""" --snip-- class Battery(): """模拟电瓶""" def __init__(self, battery_size=80): """初始化电瓶容量""" self.battery_size = battery_size def describe_battery(self): """打印一条描述电池容量的信息""" print("The car has a " + str(self.battery_size) + "-KWH battery.") # 子类 class ElectricCar(Car): """模拟简单的电动汽车""" def __init__(self, make, model, year): """初始化父类属性""" super().__init__(make, model, year) self.battery_size = Battery() def fill_gas_tank(self): """电动车没有油箱""" print("This car doesn't need a gas tank!")

实例化:

my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name()) my_tesla.battery_size.describe_battery()

输出:

2016 TESLA MODEL S The car has a 80-KWH battery.

代码解读:

class Battery(): """模拟电瓶""" def __init__(self, battery_size=80): """初始化电瓶容量""" self.battery_size = battery_size def describe_battery(self): """打印一条描述电池容量的信息""" print("The car has a " + str(self.battery_size) + "-KWH battery.")

定义一个名为Battery的新类,它没有继承任何类。 init()方法除了形参self外,还有一个形参battery_size。这个形参是可选的:如果没有给它提供值,电瓶容量将被设置为70。方法describe_battery()也移到了这个类中。

class ElectricCar(Car): """模拟简单的电动汽车""" def __init__(self, make, model, year): """初始化父类属性""" super().__init__(make, model, year) self.battery_size = Battery()

在ElectricCar类中,我们添加了一个名为self.battery的属性。self.battery = Battery()这行代码让Python创建一个新的Battery实例,并将该实例存储在变量self.battery中。每当方法__init__()被调用时,都将执行该操作;因此现在每个ElectricCar实例都包含一个自动创建的Battery实例。 我们创建一辆电动汽车,并将其存储在变量my_tesla中。要描述电瓶时,需要使用电动汽车的属性battery:

my_tesla = ElectricCar('tesla', 'model s', 2016)

这行代码让Python在实例my_tesla中查找属性battery,并对存储在该属性中的battery实例调用方法describe_battery() 下面再给Battery类添加一个方法,根据电瓶容量报告汽车的续行里程:

class Battery(): """模拟电瓶""" --snip-- def get_range(self): """根据电瓶电量指出续航里程""" if self.battery_size == 70: range = 240 if self.battery_size == 80: range = 285 message = "This car can go approximately " + str(range) message += " iles on a full charge." print(message)

实例化:

my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name()) my_tesla.battery_size.describe_battery() my_tesla.battery_size.get_range()

输出:

2016 TESLA MODEL S The car has a 80-KWH battery. This car can go approximately 285 iles on a full charge.

注:Battery()类不是Car()类的子类,所以要调用Battery类中的方法,只能通过ElectricCar()类来调用,通过这句代码来实现:

self.battery_size = Battery()

4 . 导入类

Python允许将类存储在模块中,然后再主程序中导入所需的模块。

4.1 导入单个类

下面创建一个包含Car类的模块,并将其存储在以car.py命名的模块中。

class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.") def update_mileage(self, odometer): """修改汽车里程数""" if odometer < self.odometer: print("You can't rool back an odometer!") else: self.odometer = odometer def increment_odometer(self,miles): """将里程数增加指定的量,不可小于0""" if miles >=0: self.odometer += miles else: print("You can't increase less than 0")

下面创建一个文件my_car.py,在其中导入Car类并创建其实例:

from car import Car my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name()) my_new_car.odometer_reading = 23 my_new_car.read_odometer()

4.2 在一个模块中存储多个类

同一个模块中的类之间应存在某种相关性,可根据需要在一个模块中存储任意数量的类。类Battery和ElectricCar都可帮助模拟汽车,所以将它们都加入到car.py中:

# 父类 class Car(): """模拟创建一辆汽车""" def __init__(self, make, model, year): """初始化汽车属性""" self.make = make self.model = model self.year = year # 设置里程数的默认值 self.odometer = 0 def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + " " + self.make + " " + self.model return long_name.upper() def read_odometer(self): """读取汽车的里程数""" print("The car has " + str(self.odometer) + " on it.") def update_mileage(self, odometer): """修改汽车里程数""" if odometer < self.odometer: print("You can't rool back an odometer!") else: self.odometer = odometer def increment_odometer(self,miles): """将里程数增加指定的量,不可小于0""" if miles >=0: self.odometer += miles else: print("You can't increase less than 0") class Battery(): """模拟电瓶""" def __init__(self, battery_size=80): """初始化电瓶容量""" self.battery_size = battery_size def describe_battery(self): """打印一条描述电池容量的信息""" print("The car has a " + str(self.battery_size) + "-KWH battery.") def get_range(self): """根据电瓶电量指出续航里程""" if self.battery_size == 70: range = 240 if self.battery_size == 80: range = 285 message = "This car can go approximately " + str(range) message += " iles on a full charge." print(message) # 子类 class ElectricCar(Car): """模拟简单的电动汽车""" def __init__(self, make, model, year): """初始化父类属性""" super().__init__(make, model, year) self.battery_size = Battery()

现在,可以新建一个名为my_electric_car.py的文件,导入ElectricCar类,并创建一辆电动汽车:

from car import ElectricCar my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name()) my_tesla.battery.describe_battery() my_tesla.battery.get_range()

4.3 从一个模块中导入多个类

可根据需要在程序文件中导入任意数量的类。如果我们要在统一程序文件中创建普通汽车和电动汽车,就需要将Car和Electric类导入:

from car import Car,ElectricCar my_beetle = Car('volkswagen', 'beetle', 2016) print(my_beetle.get_descriptive_name()) my_tesla = ElectricCar('tesla', 'roadster', 2016) print(my_tesla.get_descriptive_name())

4.4 导入整个模块

可以导入整个模块,再使用句点表示法访问需要的类。由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称发生冲突:

import car my_beetle = car.Car('volkswagen', 'beetle', 2016) print(my_beetle.get_descriptive_name()) my_tesla = car.ElectricCar('tesla', 'roadster', 2016) print(my_tesla.get_descriptive_name())

导入整个模块时,我们使用 module_name.class_name 访问需要的类。

4.5 导入模块中的所有类

使用下面的语法:

from module_name import *

不推荐使用这种做法,需要从一个模块中导入很多类时,最好导入整个模块,并使用 module_name.class_name语法来访问类。

4.6 在一个模块中导入另一个模块

有时候,我们需要将类分散到多个模块中,以免模块太大,或者在同一个模块中存储不相关的类。将类存储在多个模块时,你可能发现一个模块中的类依赖于另一个模块中的类。这种情况下,可在前一个模块中导入必要的类。 例如,下面将Car类存储在一个模块中,并将ElectricCar和Battery类存储在另一个模块中。我们将第二个模块命名为electric_car.py,并将Battery和ElectricCar类复制到这个模块中,下面在electric_car.py引入Car类:

"""一组可用于表示电动汽车的类""" from car import Car class Battery(): --snip -- class ElectricCar(Car): --snip --

6. 类编码风格

1.类名采用驼峰式命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名使用小写格式,并在单词之间加上下划线。

2.对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要的描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也应该包含一个文档字符串,对其中的类可用于做什么进行描述。

3.使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。

4.需要同时导入标准库中的模块和你自己编写的模块时,先编写导入标准库模块的import语句,再添加一个空行,然后再编写导入你自己编写的模块的import语句。

本博客是博主阅读《Python编程:从入门到实践》一书所做的笔记,博客中代码与理论来源于该书。

最新回复(0)