Python入门: (7)类

167 阅读8分钟

1. 创建和使用类

创建Dog类

  • 根据约定,Python中首字母大写的名称指的是 。类定义中没有圆括号,因为要从空白创建这个类
  • 使用 文档字符串 对类的功能加以描述
  • 类中的函数称为 方法方法__init__()是一个特殊方法,每当根据类创建新实例时,Python都会自动运行它。在这个方法中,开头和末尾各有两条下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。务必确保__init__()的两边都有两条下划线,否则当你使用类创建实例时,将不会自动调用这个方法,进而引发难以发现的错误
  • 方法__init__()的定义中,形参self必不可少,而且必须位于其他形参的前面。因为Python调用__init__()方法来创建类的实例时,每个与实例相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法
  • self为前缀的变量可供类中的所有方法使用,可以通过类的任何实例来访问。self.attr = attr获取与形参attr相关联的值,并将其赋给变量attr,然后该变量被关联到当前创建的实例。像这样可通过实例访问的变量称为 属性
class Dog:
    """一次模拟小狗的简单尝试"""

    def __init__(self, name, age):  # 我们将通过实参向Dog()传递名字和年龄,self会自动传递,因此不需要传递它。每当根据Dog类创建实例时,都只需给最后两个形参提供值
        """初始化属性name和age"""
        self.name = name
        self.age = age

    def sit(self):  # 这个方法执行时不需要额外信息,因此只有一个形参self
        """模拟小狗收到命令时蹲下"""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        """模拟小狗收到命令时打滚"""
        print(f"{self.name} rolled over!")

根据类创建实例

  • 通常可认为首字母大写的名称指的是类,小写的名称指的是根据类创建的实例
my_dog = Dog('Willie', 6)  # Python使用实参'Willie'和6调用Dog类的方法__init__()。方法__init__()创建一个表示特定小狗的实例,并根据提供的值来设置属性name和age
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"Your dog's name is {your_dog.name}.")
print(f"Your dog is {your_dog.age} years old.")
your_dog.roll_over()

2. 使用类和实例

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 = f"{self.year} {self.make} {self.model}"
        return long_name.title()


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

给属性指定默认值

class Car:
    """一次模拟汽车的简单尝试"""

    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0  # 将属性odometer_reading的初始值设置为0

    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """打印一条指出汽车里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it.")


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

修改属性的值

直接修改属性的值

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

通过方法修改属性的值

def update_odometer(self, mileage):
    """
    将里程表读数设置为指定的值
    禁止将里程碑读数往回调
    """
    if mileage >= self.odometer_reading:
        self.odometer_reading = mileage
    else:
        print("You can't roll back an odometer!")


my_new_car.update_odometer(23)
my_new_car.read_odometer()

通过方法对属性的值进行递增

def increment_odometer(self, miles):
    """将里程表读数增加指定的量"""
    self.odometer_reading += miles

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

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

my_new_car.increment_odometer(100)
my_new_car.read_odometer()

3. 继承

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

子类的方法__init__()

  • 创建子类时,父类必须包含在当前文件中,且位于子类前面
  • 定义子类时,必须在圆括号内指定父类的名称
  • 函数super()让你能够调用父类的方法。父类也称为 超类(superclass) ,名称super由此而来
class Car:
    """一次模拟汽车的简单尝试"""

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

    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """打印一条指出汽车里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        """
        将里程表读数设置为指定的值
        禁止将里程碑读数往回调
        """
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """将里程表读数增加指定的量"""
        self.odometer_reading += miles


class ElectricCar(Car):
    """电动车的独特之处"""

    def __init__(self, make, model, year):
        """
        初始化父类的属性
        再初始化电动汽车特有的属性
        """
        super().__init__(make, model, year)  # 这里让Python调用Car类的方法__init__(),让ElectricCar实例包含这个方法中定义的所有属性
        self.battery_size = 75  # 添加新属性并设置初始值

    def describe_battery(self):  # 添加新方法
        """打印一条描述电瓶容量的消息"""
        print(f"This car has a {self.battery_size}-kwh battery.")


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

重写父类的方法

  • 对于父类的方法,只要它不符合子类模拟的实物的行为,都可以进行重写。为此,可以在子类中定义一个与要重写的父类方法同名的方法。这样,Python将不会考虑这个父类方法,而只关注在子类中定义的相应方法

将实例用作属性

  • 使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分提取出来,作为一个独立的类。可以将大型类拆分成多个协同工作的小类
class Car:
    --snip--  # 这里跳过Car类的定义

class Battery:
    """一次模拟电动汽车电瓶的简单尝试"""

    def __init__(self, battery_size=75):
        """初始化电瓶的属性"""
        self.battery_size = battery_size

    def describe_battery(self):
        """打印一条描述电瓶容量的消息"""
        print(f"This car has a {self.battery_size}-kwh battery.")

class ElectricCar(Car):
    """电动车的独特之处"""

    def __init__(self, make, model, year):
        """
        初始化父类的属性
        再初始化电动汽车特有的属性
        """
        super().__init__(make, model, year)
        self.battery = Battery()

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

4. 导入类

  • Python为了让文件尽可能整洁。允许将类存储在模块中,然后在主程序中导入所需的模块
  • 你应为自己创建的每个模块编写文档字符串,使其对该模块的内容做简要地描述
  • 打开模块并导入其中的类 格式:from module_name import ClassName1, ClassName2, ClassName3
  • 需要从一个模块中导入很多类时,最好导入整个模块 格式:import module_name 访问类格式:module_name.ClassName
  • 导入模块中的所有类 格式:from module_name import * 不推荐这种导入方式:第一,如果只看文件开头的import语句,就能清楚地知道程序使用了哪些类。然后这种导入方式没有明确地指出使用了模块中哪些类;第二,这种方式还可能引发名称方面的迷惑
  • 有时需要将类分散到多个模块中,以免模块太大或在同一个模块中存储不相关的类
  • 导入类时可为其指定别名 格式:from module_name import ClassName as CN
  • 一开始应让代码结构尽可能简单,先尽可能在一个文件中完成所有的工作,确定一切都能正常运行后,再将类移到独立的模块中

5. Python标准库

  • Python标准库是一组模块,我们安装的Python都包含它
  • 模拟随机模块random可用于实现模拟随机过程
  • 创建与安全相关的应用程序时,不要使用模拟随机模块random
from random import randint
from random import choice
randint(1, 6)  # 生成一个1和6(包含1、6)之间的随机整数

players = ['charles', 'martina', 'michael', 'florence', 'eli']
first_up = choice(players)  # 函数choice()将一个列表或元组作为参数,并随机返回其中的一个元素

6. 类编码风格

  • 类名应采用 驼峰命名法 ,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线
  • 对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都应包含一个文档字符串,对其中的类可用于做什么进行描述
  • 可用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;在模块中,可使用两个空行来分隔类
  • 需要同时导入标准库中的模块和自己编写的模块时,先编写导入标准模块的import语句,再添加一个空行,然后编写导入自己编写模块的import语句。在包含多条import语句的程序中,这种做法让人更容易明白程序使用的各个模块都来自何处

文章中的所有代码经测试均可成功编译运行,可直接复制。具有显然结果或简单结论的代码不展示运行结果。如有问题欢迎随时交流~