欢迎来到~ 初始Python 系列文章 “第五回”,大家好呀~ 我是 清汉
不知不觉中已是Python基础系列中的第五篇文章了~
Python基础系列,每篇文章的篇幅都比较长、比较详细、比较啰嗦~
力求让每一个初学Python的人或者没有接触过编程的人,都能看得明白、看的真切。
从而产生对编程的兴趣,步入这个神奇的领域,了解计算机的世界,开拓不同维度的世界观~
注:下图截取于B站,附传送门:计算机科学速成课
有兴趣的小伙伴可以看一下,一个很棒的系列短视频,相信我,会让你爱上计算机的~
接上回书~
本篇文章主角是Python中的类先生
下面我们开始讲,类先生的“人生海海”~
上码🐎,开始走码🐎观花~
DIY 5
声明:未打广告,全文知识梳理来源于《Python编程 从入门到实践(第2版)》豆瓣评分9.3
注:例中所有 >>> 符号后为输出结果
- -snip- - 为代码省略位置
知识框图
第9章
类
- 面向对象编程中编写表现现实世界中的事物和情景的类,基于这些类来创建对象。
- 编写类时,定义的一大类对象都有通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要,赋予每个对象独特的个性。
- 根据类创建对象称为实例化,这让你能够使用类的实例。
是不是已经有点看迷糊了,哈哈~
说实话我只看这几句话,也不能理解透彻。不过文章后面的 栗子 大家吃了应该可以理解了。
创建类及实例
例1
创建类
- 使用关键字class 定义类,根据约定,Python中首字母大写的名称指的是类。
方法
在类中的函数称为方法。
方法__init__是一个特殊的方法,当你根据类创建新实例时,Python都会自动运行它。
注意:__init__开头和结尾都有两个下划线。
方法中的形参self必不可少,必须位于其他形参前面。
Python调用方法创建实例时,将自动传入实参self。每个实例相关联的方法调用都自动传入实参self,它是指向实例本身的引用,让实例能够访问类中的属性和方法。
以self为前缀的变量可供类中的所有方法使用,可通过类的任何实例来访问。
属性
- 可通过实例访问的变量称为 属性。
创建实例
- 例 1中具体演示了如何:
- 创建实例
- 访问属性
- 调用方法
# 例 1
# 创建 Dog 类
#这个类定义中没有圆括号(参数),因为要从空白(首个)创建这个类。
class Dog:
'''一次模拟小狗的简单尝试'''
def __init__(self, name, age): # 形参 name, age 分别接受小狗的名字和年龄
'''初始化属性 name 和 age'''
self.name = name # 将两个形参值分别赋值给以self为前缀的变量,供类中的所有方法使用
self.age = age
def sit(self):
'''模拟小狗收到命令后蹲下'''
print(f"{self.name} is now sitting.")
def roll_over(self):
'''模拟小狗收到命令后打滚'''
print(f"{self.name} rolled over!")
#创建表示一个表示特定小狗的实例
my_dog = Dog('willie', 6) # 变量名 = 类名称(小狗姓名,小狗年龄)
# 访问属性
#Python先找到实例my_dog,再查找到与实例相关联的属性name,在Dog类中引用这个属性时,使用的是self.name
print(f"My dog's name is {my_dog.name}.")
# >>> My dog's name is willie.
print(f"My dog's is {my_dog.age} years old.") # my_dog.age ,同上
# >>> My dog's is 6 years old.
#调用方法,指定实例的名称和调用的方法,用 .句点分隔
my_dog.sit() # Python在类Dog中查找方法sit()并运行其代码。
# >>> willie is now sitting.
my_dog.roll_over()
# >>> willie rolled over!
#创建多个实例
#即使第二条小狗指定同样的名字、年龄,Python依然会根据Dog类创建另一个实例。每个实例存储在不同的变量中。
your_dog = Dog('Lucy', 3)
print(f"\nMy dog's name is {your_dog.name}.")
print(f"My dog's is {your_dog.age} years old.")
your_dog.sit()
your_dog.roll_over()
# >>> My dog's name is Lucy.
# >>> My dog's is 3 years old.
# >>> Lucy is now sitting.
# >>> Lucy rolled over!
使用类及实例
例 2
- 主要加深了对类的应用,分别针对以下几点展开演示:
- 除形参外添加新的属性,并给属性设置默认值。
- 修改属性的值
- 通过实例访问属性,直接修改属性的值。
- 通过实例调用方法,修改属性的值。
# 例 2
# car类
class Car:
'''一次模拟汽车的简单尝试'''
def __init__(self, make, model, year): # 形参 make, model, year 分别是汽车的品牌,型号,年份
'''初始化描述汽车的属性'''
self.make = make # 将三个形参值分别赋值给以self为前缀的变量,供类中的所有方法使用
self.model = model
self.year = year
#添加一个属性存储汽车的里程,默认值指定为 0
self.odometer_reading = 0
def get_descriptive_name(self):
'''返回整洁的描述信息'''
long_name = f"{self.year} {self.make} {self.model}" # 将汽车的所有属性转换为字符串赋值给变量 long_name
return long_name.title() # return 返回变量 long_name 并使用title()将字符串首字母大写
def read_odometer(self):
'''打印一条汽车里程的消息'''
print(f"This car has {self.odometer_reading} miles on it.\n")
# 通过方法修改属性值
def update_odometer(self, mileage): # 形参 mileage 作为要修改的里程
'''
将里程表修改为指定值
禁止将里程回调
'''
if mileage >= self.odometer_reading: # 如果要修改的里程大于当前里程返回True,执行if代码块
self.odometer_reading = mileage # 将形参 mileage的值,赋值给属self.odometer_reading
else: # 否则提示不能修改
print("You can't roll back an odometer!")
#通过方法对属性的值进行递增
def increment_odmeter(self, miles): # 形参 miles 接受一个需要增加的里程数
'''将里程增加指定的值'''
self.odometer_reading += miles # 默认值里程数 + 需增加里程数
# 创建一个实例
my_new_car = Car('audi', 'a4', 2019) # 变量名 = 类名称(品牌,型号,年份)
print(my_new_car.get_descriptive_name()) # 调用类中的方法 get_descriptive_name(),打印汽车的描述信息
# >>> 2019 Audi A4
my_new_car.read_odometer() # 调用类中的方法 read_odometer() ,打印一条汽车里程的消息
# >>> This car has 0 miles on it.
#直接修改属性的值
my_new_car.odometer_reading = 100 # 访问类中的属性并赋值为100
my_new_car.read_odometer() # 再次调用类中的方法 read_odometer() ,打印一条汽车里程的消息
# >>> This car has 100 miles on it.
#通过方法修改属性的值
my_new_car.update_odometer(110) # 调用类中的方法 update_odometer(),并给它一个实参为110,因为需要修改的里程数大于默认值,所以允许修改,里程数变为110
my_new_car.read_odometer() # 再再次调用类中的方法 read_odometer() ,打印一条汽车里程的消息
# >>> This car has 110 miles on it.
#通过方法对属性的值进行递增
my_new_car.increment_odmeter(100) # 调用类中的方法 increment_odmeter(),给它一个实参为100,方法内执行代码里程数+100
my_new_car.read_odometer() # 再再再次调用类中的方法 read_odometer() ,打印一条汽车里程的消息
# >>> This car has 210 miles on it.
继承
例 3
- 如果要编写的类是另一个已经存在类的特殊版本,可使用继承。
- 一个类继承另一个类时,将自动获得另一个类的所有属性和方法,同时可以定义自己的属性和方法。
- 原有的类称为父类(也可以称为超类)
- 新类称为子类
创建子类
- 例3 中演示了如何创建一个子类
- 父类必须包含在当前文件中,且位于子类前面。
- 必须在子类的圆括号内指定父类名称。如:class 子类名称(父类名称):
- 子类中的第一个方法__init__() 接收创建父类实例所需的信息。
- 函数super() 让子类可以调用父类的方法。
- 子类独有属性,父类不能使用。
- 重写父类方法
- 对于父类方法,只要不符合子类模拟的实物的行为,都可以重写。可在子类中定义一个与要重写父类方法同名的方法。
注:例3 前半部分代码为 例2 中的代码,之所重复引用而不是省略注明,是为了让大家方便阅读,也可以直接复制运行代码(不用分别复制)。
# 例 3
# 继承
#父类(超类)
class Car:
'''一次模拟汽车的简单尝试'''
def __init__(self, make, model, year):
'''初始化描述汽车的属性'''
self.make = make
self.model = model
self.year = year
self.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.\n")
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_odmeter(self, miles):
'''将里程增加指定的值'''
self.odometer_reading += miles
def fill_gas_tank(self):
'''往油箱里装满油'''
print('Has been filled with oil')
# 子类
class ElectricCar(Car):
'''电动汽车的独特之处'''
# 方法__init__() 接收创建Car实例所需的信息。
def __init__(self, make, model, year):
'''
初始化父类的属性
再初始化电动汽车特有属性
'''
#函数super() 让子类可以调用父类的方法。
#让Python调用父类的方法__init__(),让子类实例包含这个方法中定义的所有属性。
super().__init__(make, model, year)
# 子类独有属性,父类不能使用。
self.battery_size = 75 # 创建子类属性self.battery_size,存储电池电量
def describe_battery(self):
'''打印一条描述电池电量的信息'''
print(f"This car has a {self.battery_size}-kWh battery.")
#重写父类方法
def fill_gas_tank(self):
'''电动车无油箱'''
print("This car doesn't need a gas thank!")
# 创建父类实例
you_tesla = Car('jipp', 'aodi', 2018)
# 创建子类实例
my_tesla = ElectricCar('tesla', 'model s', 2019)
print(my_tesla.get_descriptive_name()) # 通过子类实例,调用父类方法
# >>> 2019 Tesla Model S
my_tesla.describe_battery() # 通过子类实例调用子类实例,打印电池电量
# >>> This car has a 75-kWh battery.
my_tesla.fill_gas_tank() # 子类实例调用重写的父类方法
# >>> This car doesn't need a gas thank!
you_tesla.fill_gas_tank() # 父类实例调用的原父类的方法
# >>> Has been filled with oil
将实例用作属性
例 4
- 在程序编写的过程中,随着给类中添加的细节越来越多,属性和方法越来越多,代码整体显得就很臃肿。这时可以将类的一部分提取出来,作为一个独立的类。将一个大类拆解成多个协同工作的小类,完成代码的重构。
- 这里的小类并非指的是子类,而是一个新类,它没有继承任何类。
- 例4 中演示了如何在类中创建实例并赋值到属性
注:重复内容太多,不利于阅读。若是需要复制全部代码各位少侠还是辛苦一下,去前面例中找吧~
# 例 4
# 将实例用作属性
class Car:
'''一次模拟汽车的简单尝试'''
def __init__(self, make, model, year):
'''初始化描述汽车的属性'''
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
'''返回整洁的描述信息'''
long_name = f"{self.year} {self.make} {self.model}"
return long_name.title()
# --snip--
def fill_gas_tank(self):
'''往油箱里装满油'''
print('Has been filled with oil')
class ElectricCar(Car):
'''电动汽车的独特之处'''
def __init__(self, make, model, year):
'''
初始化父类的属性
再初始化电动汽车特有属性
'''
super().__init__(make, model, year)
self.battery = Battery() # 这行代码让Python创建一个新的Battery实例,因为没有指定实参默认为75
# 并将该实例赋值给前面属性,因此每个ElectricCar实例都自动创建一个Battery实例
# --snip--
class Battery:
'''一次模拟电瓶的简单尝试'''
def __init__(self, battery_size=75): # 默认形参为 75
'''初始化电瓶属性'''
self.battery_size = battery_size
def describe_battery(self):
'''打印一条描述电池电量的信息'''
print(f"This car has a {self.battery_size}-kWh battery.")
def get_range(self):
'''打印一条消息,指出电瓶的续航里程'''
if self.battery_size == 75:
range = 260
elif self.battery_size == 100:
range = 315
print(f"This car can go about {range} miles on a full charge.")
you_tesla = Car('jipp', 'aodi', 2018)
my_tesla = ElectricCar('tesla', 'model s', 2019)
print(my_tesla.get_descriptive_name())
# >>> 2019 Tesla Model S
# 先查找到属性,并对在该属性中的Battery实例调用方法describe_battery()
my_tesla.battery.describe_battery()
# >>> This car has a 75-kWh battery.
my_tesla.battery.get_range()
# >>> This car can go about 260 miles on a full charge.
导入类
- 随着类中的功能越来越多,代码会变得很长,即便前边讲述了继承、拆解类等方法让代码显得不是那么臃肿,但架不住功能多啊~
- 为遵循 Python之禅的理念,将介绍另一种方法:将不同的类存储在不同的模块中(也就单独的 .py文件中),然后在主程序中导入需要的模块。
- 当然也可以在一个模块中也可以存放多个类。
- 例5 中介绍模块中存储类、导入类的多种方式、方法:
- 导入单个类
- 在一个模块中存储多个类
- 从一个模块中导入多个类
- 导入整个模块
- 导入模块中的所有类
- 在一个模块中导入另一个模块
- 指定类的别名
- 上述这些,对组织大型开发项目很有好处,了解Python中提供的这些不同的方式、方法,可以让你知道那种项目组织方式是最佳的,并能理解别人开发的项目。
例 5
注:下面四个代码块分别是四个模块文件,模块名标注在每个代码块第一行。
若有兴趣想试运行代码,需将这四个代码块存放在四个 .py 文件中,且在同一文件夹下。
# 模块名称:car.py
'''一个可用于表示燃油汽车和电动汽车的类''' #模块级文档字符串,用来描述这个模块是干嘛的有哪些功能。
class Car:
'''一次模拟汽车的简单尝试'''
def __init__(self, make, model, year):
'''初始化描述汽车的属性'''
self.make = make
self.model = model
self.year = year
self.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.\n")
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_odmeter(self, miles):
'''将里程增加指定的值'''
self.odometer_reading += miles
class ElectricCar(Car):
'''电动汽车的独特之处'''
# 方法__init__() 接收创建Car实例所需的信息。
def __init__(self, make, model, year):
'''
初始化父类的属性
再初始化电动汽车特有属性
'''
#函数super() 让子类可以调用父类的方法。
#让Python调用父类的方法__init__(),让子类实例包含这个方法中定义的所有属性。
super().__init__(make, model, year)
self.battery_size = 75 # 子类独有属性,父类不能使用。
def describe_battery(self):
'''打印一条描述电池电量的信息'''
print(f"This car has a {self.battery_size}-kWh battery.")
def fill_gas_tank(self):
'''电动车无油箱'''
print("This car doesn't need a gas thank!")
# 模块名称:electric_car.py
'''一组用于表示电动汽车的类'''
from car import Car # 模块也可嵌套,在一个模块中导入另一个模块
class ElectricCar(Car):
'''电动汽车的独特之处'''
# 方法__init__() 接收创建Car实例所需的信息。
def __init__(self, make, model, year):
'''
初始化父类的属性
再初始化电动汽车特有属性
'''
#函数super() 让子类可以调用父类的方法。
#让Python调用父类的方法__init__(),让子类实例包含这个方法中定义的所有属性。
super().__init__(make, model, year)
self.battery_size = 75 # 子类独有属性,父类不能使用。
def describe_battery(self):
'''打印一条描述电池电量的信息'''
print(f"This car has a {self.battery_size}-kWh battery.")
#重写父类方法,子类中定义一个与要重写父类方法同名的方法。
def fill_gas_tank(self):
'''电动车无油箱'''
print("This car doesn't need a gas thank!")
# 模块名称:my_car.py
#打开模块并导入类
from car import Car
my_new_car = Car('audi', 'a4', 2019)
print(my_new_car.get_descriptive_name())
# >>> 2019 Audi A4
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
# >>> This car has 23 miles on it.
#导入多个类,用逗号分隔各个类
from car import Car, ElectricCar
my_beetle = Car('volkswagen', 'beetle', 2019)
print(my_beetle.get_descriptive_name())
# >>> 2019 Volkswagen Beetle
my_tesla = ElectricCar('tesla', 'roadster', 2019)
print(my_tesla.get_descriptive_name())
# >>> 2019 Tesla Roadster
#导入整个模块
import car
#语法 module_name.ClassName, 模块名.类名
my_beetle = car.Car('volkswagen', 'beetle', 2018)
print(my_beetle.get_descriptive_name())
# >>> 2018 Volkswagen Beetle
my_tesla = car.ElectricCar('tesla', 'roadster', 2018)
print(my_tesla.get_descriptive_name())
# >>> 2018 Tesla Roadster
# 模块名称:my_electric_car.py
#导入模块中所有类
from car import *
my_beetle = Car('volkswagen', 'beetle', 2019)
print(my_beetle.get_descriptive_name())
# >>> 2019 Volkswagen Beetle
my_tesla = ElectricCar('tesla', 'roadster', 2019)
print(my_tesla.get_descriptive_name())
# >>> 2019 Tesla Roadster
# as 关键字指定别名
from electric_car import ElectricCar as EC
my_tesla = EC('tesla', 'roadster', 2011)
print(my_tesla.get_descriptive_name())
# >>> 2011 Tesla Roadster
本篇小结
-
这一篇文章,虽看着多,但实际不多,多是一些重复代码,绕口了哈~,之所以多重复还是为了能够让阅读连贯、顺畅。
-
本篇主要围绕着类进行了多方面的举例演示,希望路过的少侠看过此篇文章后,对类有了一定的了解。
-
当然要想深入了解只看还是不行的,还是需要动手去敲~
-
所以说学编程是个动词,自我感觉敲一遍,比看10遍都顶用。
感谢,每一位认真阅读到这里的少侠!来个手滑三联吧,不迷路~
下期见,等你哟~