文章目录
第九章.类
9.1 面向过程与面向对象编程的区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为
例:一次选举大会有很多的步骤要进行,甲可能需要先为自己发言;然后乙为自己发言;接着甲为别人投票;乙选择弃票;乙提出质疑…
上面一共列举了5个步骤,我们不关心具体某个步骤,而是关心每个对象(甲、乙)在这次选举大会上的整体行为
9.2 创建和使用Dog类
根据类来创建对象被称为实例化
类相对于把一系列的函数和变量封装在一起,用来表示一种事物
属性 = 以self为前缀与这个实例关联起来的变量
- 属性必须在__init__方法内定义
- 通过self将变量t和age与整个实例关联起来,成为属性
- 以self为前缀的变量(属性)都可供类中所有的方法使用
- 变量tmp不是在__init__方法内以self定义的,所以不是属性,不能被其他方法使用
- 类内访问属性要使用self.属性名,如
self.age
- 类外访问实例中的属性(t, age)要使用实例名.属性名,但不需self,如
my_dog.age
- 类外调用方法实例名.方法名(参数)调用类方法时,self会自动传递
my_dog.roll_over('up')
示例:使用Dog类和实例
根据Dog类创建的每个实例都将存储名字和年龄,我们赋予了每条小狗蹲下和打滚的能力
class Dog():
"""一次模拟小狗的简单尝试"""
def __init__(self, name, age): # 这里name和age是形参
"""初始化属性t和age"""
# 属性必须在__init__方法内定义
self.t = name # 通过self将变量t和age与整个实例关联起来,成为属性
self.age = age # 以self为前缀的变量(属性)都可供类中所有的方法使用
def sit(self):
"""模拟小狗被命令时蹲下"""
tmp = " probably" # 这个变量tmp不是在__init__方法内以self定义的,所以不是属性,不能被其他方法使用
print(self.t.title() + tmp + " is now sitting") # 类内访问属性要使用self.t
def roll_over(self, command):
"""模拟小狗被命令时打滚"""
print("The command is " + command)
print(self.t.title() + " rolled over!")
# 每个小狗都是一个独立的实例,有自己的一组属性
my_dog = Dog('willie', 7) # 根据Dog类来创建新实例
your_dog = Dog('lucy', 3) # self会自动传递
# 类外访问实例中的属性t, age要使用句点表达式,但不需加self
print("My dog, " + my_dog.t.title() + ", is " + str(my_dog.age) + " years old.")
my_dog.sit()
my_dog.roll_over('up')
print("\nYour dog, " + your_dog.t.title() + ", is " + str(your_dog.age) + " years old.")
your_dog.sit()
your_dog.roll_over('down')
输出
My dog, Willie, is 7 years old.
Willie probably is now sitting
The command is up
Willie rolled over!
Your dog, Lucy, is 3 years old.
Lucy probably is now sitting
The command is down
Lucy rolled over!
方法__init__()
类中的函数称为方法,__init()是一个特殊的方法,每次你根据Dog类创建新实例时,Python都会自动运行它,而且虽然没有显式的return,但是Python会自动返回一个表示这条小狗的实例,一般我们把它存到一个变量中,如my_dog = Dog('willie', 7)
为什么类方法定义中包含self ?
因为Python在调用类方法时自动传入实参self,它是一个指向实例本身的引用(类似C++ this指针),让实例能够访问类中的属性和方法
self.t = name获取存储在形参name中的值,并将其存储在变量t中,然后该变量被关联到当前创建的实例。这样的变量t被称为属性
约定:首字母大写的名称(如:Dog)指的是类,小写的名称(如my_dog)指的是根据类创建的实例
在Python2.7中创建类有细微区别,需在括号内包含单词object
class Classname(object)
--snip--
9.3 修改属性的值Car类
说明:类中的每个属性(只能在__init__方法中定义)都必须有初始值,哪个这个值是0或空字符串
有三种不同的方法修改属性的值
- 直接通过实例进行修改
my_car.odometer_reading = 23 - 直接通过实例进行修改
my_car.update_odometer(23) - 通过方法进行递增(增加特定的值)
my_car.increment_odometer(100)
Car.py
# 2020年1月23日 星期四 14:10:40
class Car():
def __init__(self, make, model, year):
"""初始化描述汽车的属性"""
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0 # 每个属性必须初始化
def read_odometer(self):
"""打印汽车的里程信息"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, odometer_reading):
"""更新里程信息"""
self.odometer_reading = odometer_reading
def increment_odometer(self, increment):
"""
增加特定里程
禁止里程表往回调
"""
if increment < 0:
print("You can't roll back an odometer!")
else:
self.odometer_reading += increment
my_car = Car('Audi', 'A4', 2016)
my_car.read_odometer()
my_car.odometer_reading = 1200 # 法一:直接通过实例进行修改
my_car.read_odometer()
my_car.update_odometer(1400) # 法二:直接通过实例进行修改
my_car.read_odometer()
my_car.increment_odometer(100) # 法三:通过方法增加特定值
my_car.read_odometer()
输出
This car has 0 miles on it.
This car has 1200 miles on it.
This car has 1400 miles on it.
This car has 1500 miles on it.
9.4 继承
9.4.1 子类与父类
如果你要编写的类是另一个现成类的特殊版本,可使用继承
一个类继承另一个类后,它将自动获得另一个类的所有属性和方法;原有的类称为父类(superclass),而新类称为子类
子类继承了其父类所有的属性和方法,同时还可以定义自己的属性和方法
创建子类的实例时,首先需要给其父类的所有属性赋值,为此子类的方法__init__()需要父类施以援手
下面创建一个简单的ElectricCar类版本,它具有Car类的所有功能
核心代码:super().__init__(make, model, year)
让子类包含父类Car中的所有属性和方法
super()是一个特殊函数,帮助Python把子类和父类关联起来
class Car():
def __init__(self, make, model, year):
"""初始化描述汽车的属性"""
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0 # 每个属性必须初始化
def read_odometer(self):
"""打印汽车的里程信息"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, odometer_reading):
"""更新里程信息"""
self.odometer_reading = odometer_reading
def increment_odometer(self, increment):
"""
增加特定里程
禁止里程表往回调
"""
if increment < 0:
print("You can't roll back an odometer!")
else:
self.odometer_reading += increment
# 继承
class ElectricCar(Car): # 父类名写在括号内
"""电动汽车的独特之处"""
def __init__(self, make, model, year, color):
"""初始化父类(superclass)Car中的属性"""
super().__init__(make, model, year) # 继承核心代码:让子类包含父类Car中的所有属性和方法
self.license_number = 'A2873L' # 子类可以定义自己的属性和方法
self.color = color
my_tesla = ElectricCar('tesla', 'model s', 2016, 'red')
my_tesla.odometer_reading = 22 # 可以使用父类的属性或方法
my_tesla.read_odometer() # 输出 This car has 22 miles on it.
print("The license number is " + my_tesla.license_number + ", and the color is " + my_tesla.color)
# 输出 The license number is A2873L, and the color is red
继承的一点说明:如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的,就应将其加入到Car类而不是ElectricCar类中。这样,使用Car类的人可以获得相应的功能,而使用ElectricCar类只包含处理电动汽车特有的属性和行为代码
所以上面的代码其实不够好,因为color和license_number所有汽车都有,如果是battery则可以加到子类中
Python2.7中的继承
class Car(object):
def __init__(self, make, model, year):
--snip--
class ElectricCar(Car):
def __init__(self, make, model, year):
super(ElectricCar, self).__init__(make, model, year)
--snip--
函数super()需要两个实参:子类名和对象self
另外在Python2.7使用继承时务必在定义父类时在括号内指定object
9.4.2 重写父类方法—去其槽粕
对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。只需在子类中定义一个同名的方法,Python只会关心子类中定义的方法,不会考虑同名的父类方法
假如Car类有一个名为fill_gas_tank()的方法,它对电动汽车是毫无意义的,因此子类需要重写
class ElectricCar(Car)
--snip--
def fill_gas_tank(self):
"""电动汽车没有油箱"""
print("This car doesn't need a gas tank!")
使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕
9.4.3 将实例用作属性
9.5 def greeting(name: str) -> str:含义
函数接受并返回一个字符串,注释像下面这样
def greeting(name: str) -> str:
return 'Hello ' + name
在函数greeting 中,参数name 预期是 str 类型,并且返回 str 类型
9.6 导入模块中的类import
Python允许将类存储在模块中,也是以.py为后缀的文件
导入模块(类或函数)是一种有效的编程方式,它能够让你专注于主程序的高级逻辑
导入类的方式其实和导入函数完全一样,也有三种
car.py存了Car类
则可在my_car.py中
- 使用from car import Car导入,这样可以直接使用Car类,就好像它是在这个文件中定义的一样(因为已经
显式
导入了类名) - 使用import car导入,这种方式会导入car中所有的类,调用时要用
car.类名
- 使用from car import *导入,这种方式不建议使用
当然在一个模块中也可以导入另一个模块
from car import Car # 导入car.py模块中的Car类
class Battery():
--snip--
class ElectricCar(Car): # 定义Car的子类
--snip--
示例
主程序是my_car.py,导入的模块是car.py和electric_car.py
主程序my_car.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author : Wilson79
@Filename : my_car.py
@Date : 2020/1/23 下午06:56
"""
# 主程序my_car.py 导入模块car.py和electric_car.py
from car import Car
from electric_car import ElectricCar, Battery
my_beetle = Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
my_tesla = ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
输出
2016 volkswagen beetle
2016 tesla roadster
This car has a 70-KWh battery.
类模块 car.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author : Wilson79
@Filename : car.py
@Date : 2020/1/23 下午06:53
"""
"""一个可用于表示汽车的类"""
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 = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name
def read_odometer(self):
"""打印汽车的里程信息"""
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, odometer_reading):
"""更新里程信息"""
self.odometer_reading = odometer_reading
def increment_odometer(self, increment):
"""
增加特定里程
禁止里程表往回调
"""
if increment < 0:
print("You can't roll back an odometer!")
else:
self.odometer_reading += increment
类模块 electric_car.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Author : Wilson79
@Filename : electric_car.py
@Date : 2020/1/23 下午06:54
"""
"""一组可用于表示电动汽车的类"""
from car import Car # ElectricCar类要访问其父类Car,所以要导入Car类
class Battery():
"""模拟电动汽车电瓶的简单尝试"""
def __init__(self, battery_size=70):
"""初始化电瓶的属性"""
self.battery_size = battery_size
def describe_battery(self):
"""打印电瓶容量信息"""
print("This car has a " + str(self.battery_size) + "-KWh battery.")
# ElectricCar继承Car
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""初始化父类中的属性"""
super().__init__(make, model, year) # 继承核心代码:让子类包含父类Car中的所有属性和方法
# 每个ElectricCar实例都会包含一个自动创建的Battery实例
self.battery = Battery() # 使用实例作为属性
9.7 Python标准库和类编码风格
Python标准库是一组模块,安装的Python都包含它
我们可以使用标准库中的任何函数和类
只需在程序开头包含一条简单地import语句
以使用模块collections中的一个类——OrderedDict为例
OrderedDict实例会记录你添加时的键-值对的顺序
from collections import OrderedDict
favorite_languages = OrderedDict() # 创建一个空的有序字典
favorite_languages['kevin'] = 'python'
favorite_languages['jeny'] = 'java'
favorite_languages['sarah'] = 'c'
favorite_languages['wilson'] = 'c++'
for name, language in favorite_languages.items():
print(name.title() + "'s favorite language is "
+ language.title() + ".")
类编码风格
-
类名应采用驼峰命名法,如ElectricCar,不能使用下划线
-
每个类定义后面包含一个文档字符串,简要地描述类的功能
-
同时导入标准库中的模块和自己编写的模块时,先编写导入标准库模块的import语句,再用一个空行隔开,再写导入自己的模块的import语句,这样让人更容易明白程序使用了各个模块来自何方
from collections import OrderedDict # 标准库中模块
from car import Car # 自己编写的模块
from electric_car import ElectricCar, Battery
写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)