面向对象编程
面向对象
面向对象(Object Oriented Programming,OOP)的核心是把现实事物映射为程序中的对象(即对象是现实世界事物的模型,可以通过代码创建的与现实世界对应的事物)。在现实世界可以通过状态和行为来定义一个对象,状态描述对象/事物是什么样子,行为描述对象/事物能做什么;在程序中对象是任何具有状态(属性)以及预定义行为(方法)的数据。
中文语境中属性是指狭义上的属性,即描述事物的状态,广义上来说,Python的方法和属性都是属性:
- 方法:方法属性,描述行为的函数(一段代码)
- 属性:数据属性,描述状态的数据
对象创建示例: 例如我想描述现实世界中的鹦鹉,在Python中对象可以通过将类实例化为对象来创建。
先创建一个存放类的文件parrot.py,在其中定义鹦鹉类,类名为Parrot:
class Parrot:
# 类属性,每个对象所拥有的共同状态
# 所有鹦鹉都是2只眼睛1张嘴巴和2条腿
eyes:int = 2
mouth:int = 1
legs:int = 2
# 实例属性,每个对象独立拥有的状态
# 每只鹦鹉都有自己的名字和颜色
def __init__(self, name: str='your_parrot_name', color: str='your_parrot_color'):
self.name = name
self.color = color
然后在主文件(执行Python程序的入口文件)main.py中导入Parrot类,并实例化类为对象:
# 导入Parrot类
from parrot import Parrot
# 将类实例化为对象
naughty = Parrot()
quiet = Parrot(name='quiet', color='red')
# 访问对象中的属性:对象名.属性名
print(naughty.eyes, quiet.eyes) # 2 2
print(naughty.name, quiet.name) # your_parrot_name quiet
print(naughty.color, quiet.color) # your_parrot_color red
# 添加或修改对象中的属性
naughty.name, naughty.color = 'naughty', 'green'
print(naughty.name, naughty.color) # naughty green
quiet.color = 'blue'
print(quiet.color) # blue
核心概念
抽象
抽象的核心要素是隐藏细节和暴露接口。想象你正在使用手机,你可以开关手机、用手机上网浏览网页、安装和使用应用软件……你不需要知道手机内部构造以及硬件和软件是如何结合的,你只需要点击或滑动触摸屏与它交互,就能实现功能,这就是一种抽象。
抽象旨在简化复杂的系统,隐藏不必要的细节,只提供所需的重要信息给使用者。它提示了代码的可重用性、可扩展性。
以计算阶乘为例,若有人编写好了阶乘类 Factorial,其方法 calculate() 接收一个整数并计算阶乘结果,存放在 factorial.py 中(代码见下)。
class Factorial:
def __init__(self, n):
self.n = n
def check(self):
if type(self.n) != int:
raise TypeError("传入的参数n必须是整数!")
if self.n < 0:
raise ValueError("负数没有阶乘!")
def calculate(self):
self.check()
if self.n in [0, 1]:
return 1
else:
result = 1
for i in range(2, self.n + 1):
result *= i
return result
则用户只需要导入该模块并调用 calculate(),不需要知道阶乘的计算过程、实现的具体细节,就能得到计算结果。
from factorial import Factorial
f_3 = Factorial(3)
f_4 = Factorial(4)
f_0 = Factorial(0)
f_minus1 = Factorial(-1)
print(f'3! = {f_3.calculate()}') # 3! = 6
print(f'4! = {f_4.calculate()}') # 4! = 24
print(f'0! = {f_0.calculate()}') # 0! = 1
print(f"-1! = {f_minus1.calculate()}") # 报错:ValueError: 负数没有阶乘!
封装
参考上面的Parrot类的定义,当实例化一只鹦鹉(naughty = Parrot())并初始化了它的名字(naughty.name = 'naughty'),所有人都可以直接访问和修改它的名字。然而当不希望名字被外部直接访问和修改时,只能通过给定方法访问和修改时,可以通过封装来限制访问。
封装就是把属性和操作属性的方法捆绑到类里,并对属性的直接访问加以限制。
封:隐藏属性,不让外部随意访问和修改,只能通过类提供的入口(方法)访问和修改
装:把数据(属性)和操作数据的方法放在同一个类中,对外只提供方法接口
class Parrot:
def __init__(self, name: str='your_parrot_name', color: str='your_parrot_color'):
self.name = name # 没有封装鹦鹉的名字
self.color = color
naughty = Parrot()
# 可以直接访问鹦鹉naughty的名字
print(naughty.name) # your_parrot_name
# 可以直接修改鹦鹉naughty的名字
naughty.name = 'happy'
1. 双下划线
在属性名前加双下划线(如 self.__name),即向 Python 声明这是一个私有属性:类的内部代码可以直接访问和修改它但是外部代码无法直接访问和修改它,直接写 naughty.__name 会抛出 AttributeError。要读写这个私有属性,必须通过类提供的公有方法(getter 负责读取、setter 负责修改,任何人都可以直接调用公有方法)作为受控的访问和修改入口。
定义私有属性
class Parrot:
def __init__(self, name: str, color: str):
self.__name = name # 使用双下划线封装鹦鹉的名字
self.color = color
naughty = Parrot('naughty', 'green')
print(naughty.__name) # 报错,不存在这个属性
# Python解释器会将 __属性名 改名为 _类名__属性名
# 使用双下划线本质是重命名,不是真正私有化属性、对其封装
print(naughty._Parrot__name) # naughty
定义公有访问方法
class Parrot:
def __init__(self, name: str, color: str):
self.__name = name
self.color = color
# 定义getter:读取私有属性的公有方法
def get_name(self):
return self.__name
# 定义setter:修改私有属性的公有方法
def set_name(self, name: str):
self.__name = name
2. property装饰器
继承
继承是指一个类(子类)可以从另一个类(基类)获取方法和属性。它在类与类之间定义了层级关系。
定义基类
# 定义一个动物类,类名为Animal
class Animal:
# 定义Animal类的实例属性
def __init__(self, weight=None, color=None, age=None, type_of_animal=None):
self.weight = weight
self.color = color
self.age = age
self.type_of_animal = type_of_animal
# 定义Animal类的方法
def eat(self):
print('该动物正在吃')
def sleep(self):
print('该动物正在睡')
animal = Animal()
animal.sleep() # 该动物正在睡
定义子类
# 定义一个鸟类,类名为Bird,鸟类继承动物类
class Bird(Animal):
# 会继承Animal类的所有属性,也能定义Bird类自己的属性
def __init__(
self,
weight=None,
color=None,
age=None,
type_of_animal=None,
can_shed=None,
domestic_name=None,
):
super().__init__(weight, color, age, type_of_animal)
self.can_shed = can_shed
self.domestic_name = domestic_name
def bark(self):
print('叽叽喳喳')
# 当子类定义的属性名在基类中存在(同名)时,会覆盖基类的属性
def sleep(self):
print('该鸟正在睡')
# Bird类的实例能继承Animal类的属性
naughty = Bird(color='green', age=1, type_of_animal='bird')
print(naughty.color) # green
# Animal类作为基类无法获取Bird类的属性
animal.bark() # 报错AttributeError:Animal类没有bark方法属性
# 方法覆盖
naughty.sleep() # 该鸟正在睡
多态
多态是指不同对象调用同一个方法可能会有不同的表现,即同一个方法在不同对象上的表现呈现多种形态。
class Animal:
def bark(self):
print('-')
class Bird(Animal):
def bark(self):
print('叽叽喳喳')
class Wolf(Animal):
def bark(self):
print('嗷呜嗷呜')
animals = [Animal(), Bird(), Wolf()]
for animal in animals:
animal.bark()
"""
---------
输出结果为:
-
叽叽喳喳
嗷呜嗷呜
---------
animals列表中的每个实例对象都调用了bark方法,但是它们的叫声(输出结果)有多种形态
---------
"""
组合与聚合
组合与聚合都是HAS-A(有一个)关系,它用于创建包含另一个对象的对象,这个对象本身没有另一个对象的方法和属性,它是去借用,把另一个对象作为整体对象的属性。
- 聚合:弱一点的包含关系,部分可脱离整体、可独立创建、离开整体独立存在
- 组合:强一点的包含关系,整体与部分绑死,部分随整体生灭
而继承则是IS-A(是一种)关系,它通过继承链直接拥有基类的方法和属性,相当于用在自身的东西。
# 武器类
class Weapon:
def __init__(self, name, health_points, attack_points):
self.name = name
self.health_points = health_points
self.attack_points = attack_points
# 英雄类
class Hero:
def __init__(self, name):
self.name = name
self.health_points = 10
self.attack_points = 1
self.weapons = []
def add_weapon(self, *weapons):
for weapon in weapons:
if weapon not in self.weapons:
self.weapons.append(weapon)
# 使用聚合:武器的属性加到英雄的属性上,销毁英雄后武器依然存在
self.health_points += weapon.health_points
self.attack_points += weapon.attack_points
def show_info(self):
print(
f"{self.name}的生命值为:{self.health_points},攻击力为:{self.attack_points}"
)
# 实例化武器(聚合:武器在英雄类外创建)
hei_qie = Weapon("暗影战斧", 5, 20)
bin_xin = Weapon("极寒风暴", 30, 2)
# 实例化英雄
hai_nuo = Hero("海诺")
# 为英雄添加武器
hai_nuo.add_weapon(hei_qie, bin_xin)
# 显示英雄信息
hai_nuo.show_info()

浙公网安备 33010602011771号