Python-9.1-类

  • 在Python中,首字母大写的名称指的是
    • 这个类定义中的括号是空的,因为我们要从空白创建这个类
  • 面向对象编程是最有效的软件编写方式之一
    • 在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象
    • 编写类时,你定义一大类对象都有通用行为
    • 基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个形
    • 使用面向对象编程可模拟显示情景,其逼真程度令人惊讶
  • 根据类来创建对象被称为实例化,这让你能够使用类的实例
    • 编写一些类并创建其实例
    • 指定可在实例中存储什么信息,定义可对这些实例执行哪些操作
    • 编写一些类来扩展既有类的功能,让相似的类能够高效地共享代码
    • 把自己编写的类存储在模块中,并在自己的程序文件中导入其他程序员编写的类

一:创建和使用类

  • 使用类几乎可以模拟任何东西

1、创建Dog类

  • 它表示的不是特定的小狗,而是任何小狗
  • 根据Dog类创建的每个实例都将存储名字和年龄
  • 赋予了每条小狗蹲下(sit())和打滚(roll_over())的能力
(1) 方法__init__()
  • 类中的函数称为方法
    • 有关函数的一切都适用于方法,唯一的差别是调用方法的方式
  • 方法__init__()是一个特殊的方法,开头和结尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突
  • 形参self必不可少,还必须位于其他形参的前面
    • Python调用这个_int_()方法创建Dog实例时,将自动传入实参self
    • 每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法
  • 可通过实例访问的变量称为属性
# 创建Dog类
class Dog():
    # 文档字符,对类的功能作描述
    """一次模拟小狗的尝试"""
    # Dog类创建新实例时,Python会自动运行它
    def __init__(self, name, age):
        """初始化属性name和age"""
        # 以self为前缀的变量都可供类中的所有方法调用
        # 通过类的任何实例来访问这些变量
        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('willie', 6)

2、根据类创建实例

  • 可将类视为有关如何创建的说明
    • Dog类是一系列说明,让Python知道如何创建表示特定小狗的实例
(1)访问属性
  • 要访问属性,可使用句点表示法
    • 演示了Python如何获悉属性的值
  • my_dog.name.title()
    • Python先找到实例my_dog,再查找与这个实例相关联的属性name
    • 在Dog类中引用这个属性时,使用的是self.name
    • 将my_dog的属性name的值’willie’改为首字母大写
  • my_dog.age
    • Python先找到实例my_dog,再查找与这个实例相关联的属性age
    • 在Dog类中引用这个属性时,使用的是self.age
    • 将my_dog的属性age的值6转换为字符串
# 访问属性
'''class Dog():
    --snip--'''

my_dog = Dog('willie', 6)
print("My dog name is " + \
      my_dog.name.title() + \
      ".")
print("My dog is " + \
      str(my_dog.age) + \
      " years old.")

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

(2) 调用方法
  • 根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法
  • 调用方法
    • 可指定实例的名称(my_dog)和调用的方法,并用句点分隔它们
  • my_dog.sit()
    • Python在类Dog中查找方法sit()并运行其代码
  • my_dog.roll_over()
    • Python在类Dog中查找方法roll_over()并运行其代码
# 调用方法
"""class Dog():
    --snip--"""

"""my_dog = Dog('willie', 6)"""
my_dog.sit()
my_dog.roll_over()

Willie is now sitting.
Willie rolled over!

(3) 创建多个实例
  • 可按需求根据类创建任意数量的实例,条件是将每个实例都存储在不同的变量中,或占用列表或字典的不同的位置
  • 就算给第二条小狗指定同样的名字和年龄,Python依然会根据Dog类创建另一个实例
my_dog = Dog('willie', 6)
print("My dog name is " + \
      my_dog.name.title() + \
      ".")
print("My dog is " + \
      str(my_dog.age) + \
      " years old.")
my_dog.sit()

your_dog = Dog('lucy', 3)
print("\nMy dog name is " + \
      your_dog.name.title() + \
      ".")
print("My dog is " + \
      str(your_dog.age) + \
      " years old.")
your_dog.sit()

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

My dog name is Lucy.
My dog is 3 years old.
Lucy is now sitting.

二:使用类和实例

  • 可以使用类来模拟现实世界中的很多情景
  • 类编写好后,大部分时间都花在使用根据类创建的实例
  • 修改实例的属性
    • 直接修改实例的属性
    • 编写方法以特定的方式进行修改

1、Car类

  • 编写一个表示汽车的类,它存储了有关汽车的信息
  • 一个汇总这些信息的方法
class Car():
    
    """一次模拟汽车的简单尝试"""
    # 方法__init__()接受这些形参的值
    # 并将它们存储在根据这个类创建的实例的属性中
    def __init__(self, model, make, year):
        """初始化描述汽车的属性"""
        self.model = model
        self.make = make
        self.year = year
        return None
    def get_descriptive_name(self):
        """返回整洁d额描述性信息"""
        long_name = str(self.year) + ' ' + self.make + " " + self.model
        return long_name.title()
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())

2016 Audi A4

2、给属性指定默认值

  • 类中的每个属性都必须有初始值,哪怕这个值是0或空字符串
  • 在有些情况下,如设置默认值时,在方法__init__()内指定这种初始值是可行的
    • 对某个属性指定了默认值,就无需包含为它提供初始值的形参
class Car():
    
    """一次模拟汽车的简单尝试"""
    def __init__(self, model, make, year):
        """初始化描述汽车的属性"""
        self.model = model
        self.make = make
        self.year = year
        # 添加属性,其初始值总是为0
        self.odometer_reading = 0
        return None
    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = str(self.year) + ' ' + self.make + " " + self.model
        return long_name.title()
    # 添加方法,用于读取汽车的里程表
    def read_odometer(self):
        """打印一条指出汽车里程的消息"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
new_car.read_odometer()

2016 Audi A4
This car has 0 miles on it.

3、修改属性的值

  • 三种不同的方式修改属性值
    • 直接通过实例进行修改
    • 通过方法设置进行修改
    • 通过方法进行递增(增加特定的值)
(1)直接修改属性的值
  • 要修改属性的值,最简单的方式是通过实例直接访问它
    • 使用句点表示法来直接访问设置汽车的属性 odometer_reading
    • 这行代码让 Python 在实例 my_car 中找到属性 odometer_reading,并将该属性设置为10
class Car():
    
    """一次模拟汽车的简单尝试"""
    def __init__(self, model, make, year):
        """初始化描述汽车的属性"""
        self.model = model
        self.make = make
        self.year = year
        self.odometer_reading = 0
        return None
    
    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = str(self.year) + ' ' + self.make + " " + self.model
        return long_name.title()
    
    def read_odometer(self):
        """打印一条指出汽车里程的消息"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")
        return None
    
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
# 1、直接访问属性并修改属性的值
new_car.odometer_reading = 10
new_car.read_odometer()

2016 Audi A4
This car has 10 miles on it.

(2)通过方法修改属性的值
  • 无需直接访问属性
  • 可将值传递给一个方法,由它在内部进行更新
    • 这个方法接受一个里程值,并将其存储到self.odometer_reading中
    • 调用了方法update_odometer(),并向它提供了实参(该实参对应于方法定义中的实参mileage)
class Car():
    
    """一次模拟汽车的简单尝试"""
    def __init__(self, model, make, year):
        """初始化描述汽车的属性"""
        self.model = model
        self.make = make
        self.year = year
        self.odometer_reading = 10
        return None
    
    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = str(self.year) + ' ' + self.make + " " + self.model
        return long_name.title()
    
    # 2、通过方法修改属性的值
    def update_odometer(self, mileage):
        """将里程表读数设置为指定的值
        禁止将里程表读书往回调
        """
        # 禁止任何人将里程表往回调
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
            print("This car has " + str(self.odometer_reading) +  " miles on it.")
        else:
            print("You can't roll back an odometer.")
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
new_car.update_odometer(25)
new_car.update_odometer(5)

2016 Audi A4
This car has 25 miles on it.
You can’t roll back an odometer.

(3)通过方法对属性的值进行递增
  • 有时候需要将属性值递增特定的量,而不是将其设置为全新的值
class Car():
    
    """一次模拟汽车的简单尝试"""
    def __init__(self, model, make, year):
        """初始化描述汽车的属性"""
        self.model = model
        self.make = make
        self.year = year
        self.odometer_reading = 2500
        return None
    
    def get_descriptive_name(self):
        """返回整洁的描述性信息"""
        long_name = str(self.year) + ' ' + self.make + " " + self.model
        return long_name.title()
    
    # 3、通过方法对属性的值进行递增
    def increment_odometer(self, miles):
        """将里程表读数增加指定的量"""
        # 禁止任何人将里程表往回调
        if miles > 0:
            self.odometer_reading += miles
            print("This car has " + str(self.odometer_reading) + " miles on it.")
        else:
            print("You can't roll back an odometer.")
        return None
    
new_car = Car('a4', 'audi', 2016)
print(new_car.get_descriptive_name())
new_car.increment_odometer(100)
new_car.increment_odometer(-100)

2016 Audi A4
This car has 2600 miles on it.
You can’t roll back an odometer.

三:继承

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

1、子类的方法 init()

  • 创建子类的实例时,Python首先完成的任务是给父类的所有值赋值
    • 子类的方法 init()需要父类施以援手
# 父类也称超类(superclass)
class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
        return None
    
    def get_descriptive_name(self):
        long_name = str(self.year) + ' ' + self.make + ' ' + self.model
        return long_name.title()
    
    def read_odometer(self):
        print("This car has ", str(self.odometer_reading), " miles on it.")
        return None
    
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer.")
        return None
    
    def increment_odometer(self, miles):
        if miles > 0:
            self.odometer += miles
        else:
            print("You can't roll back an odometer.")
        return None

# 定义子类ElectricCar
class ElectricCar(Car):
    
    """电动汽车的独特之处"""
    
    # 接受Car实例所需的信息
    def __init__(self, make, model, year):
        """初始化父类的属性"""
        # 将父类和子类关联起来
        # 调用父类的方法__init__(),让子类实例包含父类的所有属性
        super().__init__(make, model, year)
        return None

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

2016 Tesla Model S

2、在Python 2.7 中的继承

  • 在 Python 2.7 中使用继承时
    • 务必在定义父类时在括号内指定object
  • 函数 super() 需要两个实参
    • 子类名和对象self
    • 为了帮助Python将父类和子类关联起来,这些实参必不可少

3、给子类定义属性和方法

  • 让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法
class Car():
    - - snip - -


class ElectricCar(Car):
    
    """电动汽车的独特之处"""
    
    def __init__(self, make, model, year):
        """
        初始化父类的属性
        初始化电动汽车特有的属性
        """
        super().__init__(make, model, year)
        # 添加电动汽车特有的属性电瓶容量
        self.battery_size = 70
        return None
    
    # 添加子类的新方法
    def describe_battery(self):
        """打印一条描述电瓶容量的信息"""
        print("The car has a " + str(self.battery_size) + "-kWh battery.")
        return None

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.

4、重写父类的方法

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

class ElectricCar(Car):
    - - snip - - 
    # 重写父类的方法
    def fill_gas_tank(self):
        """电动汽车没有油箱"""
        print("The car doesn't need a gas tank.")
        return None

5、将实例用作属性

  • 使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多
    • 属性和方法清单以及文件都越来越长
  • 需要将类的一部分作为一个独立的类提取出来
    • 将大型类拆分成多个协同工作的小类
class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
        return None
    
    def get_descriptive_name(self):
        long_name = str(self.year) + ' ' + self.make + ' ' + self.model
        return long_name.title()
    
    def read_odometer(self):
        print("This car has ", str(self.odometer_reading), " miles on it.")
        return None
    
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer.")
        return None
    
    def increment_odometer(self, miles):
        if miles > 0:
            self.odometer += miles
        else:
            print("You can't roll back an odometer.")
        return None
    
class Battery():
    
    """电动汽车电瓶容量"""
    def __init__(self, battery_size=80):
        self.battery_size = battery_size
        return None
    
    def describe_battery(self):
        """描述电瓶容量"""
        print("The car has a " + str(self.battery_size) + "-kWh battery.")
        return None
    
    def get_range(self):
        """指出电瓶的续航能力"""
        if self.battery_size > 50 and self.battery_size < 80:
            range = 250
        elif self.battery_size > 80 and self.battery_size < 100:
            range = 400
        elif self.battery_size > 100:
            range = 500
        message = "This car can go approximately " + str(range) + " miles on a full charge."
        print(message)
    
class ElectricCar(Car):
    """电动汽车的独特之处"""
    
    def __init__(self, make, model, year):
        """
        初始化父类的属性
        初始化电动汽车特有的属性
        """
        super().__init__(make, model, year)
        # 添加电动汽车特有的属性电瓶容量
        # 给Battery一个初始量
        self.battery_size = Battery()
        return None

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
# 实例用作属性的重新赋值
my_tesla.battery_size = Battery(51000)
my_tesla.battery_size.describe_battery()
my_tesla.battery_size.get_range()

2016 Tesla Model S
The car has a 51000-kWh battery.
This car can go approximately 500 miles on a full charge.

四:导入类

  • 不断地给类添加功能,文件可能变得很长,即便妥善地使用了继承亦如此
  • Python允许将类存储在模块中,然后在主程序中导入所需的模块

1、导入单个类

  • 导入类是一种有效的编程方式
    • 通过将这个类移到一个模块中,并导入该模块,依然可以使用其所有功能
  • 导入单个类,可使用该语法
    • from module_name import class_name

2、在一个模块中存储多个类

  • 虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的类

3、从一个模块中导入多个类

  • 可根据需要在程序文件中导入多各类
  • 导入多个类,可使用该语法
    • from module_name import class_name, class_other_name

4、导入整个模块

  • 可以导入整个模块,再使用句点表示法访问需要的类
  • 导入整个模块,可使用该语法
    • import module_name

5、导入模块中的所有类

  • 这种导入方式没有明确地指出使用了模块中的哪些类,还可能引发名称方面的困惑
  • 导入模块中的所有类,可使用该语法
    • from module_name import *

6、在一个模块中导入另一个模块

  • 有时候,需要将类分散到多个模块中,以免模块太大,或者同一个模块中存储不相关的类
  • 将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类
    • 可在前一个模块中导入必要的类

7、自定义工作流程

  • 先尽可能在一个文件中完成所有工作,确定一切都能正常运行后,再将类移到独立的模块中
  • 如果你喜欢模块和文件的交互方式,可在项目开始时就尝试将类存储到模块中

五:Python标准库

  • Python 标准库是一组模块,安装 Python 都包含它
  • 可使用标准库中的任何函数和类,为此只需在程序开头包含一条简单的 import 语句

六:类编码风格

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

七:小结

  • 如何编写类
  • 如何使用属性在类中存储信息,以及如何编写方法,以让类具备所需的行为
  • 如何编写方法__init__(),以便根据类创建包含所需属性的实例
  • 如何修改实例的属性
    • 直接修改
    • 通过方法修改
  • 使用继承可简化相关类的创建工作
  • 将一个类的实例用作另一个类的属性可让类更简洁
  • 通过将类存储在模块中,并在需要使用这些类的文件中导入它们,可让项目组织有序
  • 学习了 Python 标准库
  • 学习了编写类时应遵循的 Python 约定
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值