Python随堂笔记 面向对象程序设计

154 阅读12分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

🌴 2022.04.20 下午

前言

🎬本文章是 【Python语言基础】 专栏的文章,主要是上课的随堂笔记与练习
🔗Python专栏 传送门
💻提示:本文的环境配置是Python3的开发环境。目录顺序以当堂内容(教材)为准,所以并非“1、2、3”
📽本节主要内容:python与C+和JAVA一样也有类、对象、成员...学习python面向对象程序设计可以对比着学习,更好理解

6 面向对象程序设计

6.1 概述

🚀 程序设计方法

  • 计算机程序设计的语言有很多种,如C/C++、C#、Java和Python等
  • 程序设计语言描述计算机系统的方式一般有两种:面向过程程序设计(Procedure Oriented Programming, POP)和面向对象程序设计(Object Oriented Programming,OOP)
  • POP把计算机程序视为一系列命令的集合,即一组函数按照事先设定的顺序依次执行,函数是程序的基本单元
  • 为了简化程序设计,把函数继续分解为子函数来降低程序的复杂度。使用POP的程序设计语言有C、Python等
  • OOP是一种新的程序设计思想和方法。OOP把计算机程序视为一组对象(Object)的集合,每个对象都可以接收其他对象发送的消息,并处理这些消息。 计算机程序的执行指一系列消息在各个对象之间传递
  • 支持OOP的程序设计语言有C++、C#、Java、Python等
  • OOP的基本思想是,将数据以及对数据的操作封装在一起,组成一个相互依存、不可分割的整体,即对象
  • 对相同类型的对象进行分类、抽象后,得出共同特征而形成
  • 面向对象程序设计的关键是,如何合理地定义和组织这些类及类之间的关系
  • OOP的基本概念包括对象、类、消息、封装、继承和多态等。其中,封装、继承和多态是OOP最重要的三个特征

🚀 Python中的面向对象程序设计

Python不仅支持POP,更是一种面向对象、高级的动态编程语言,完全支持OOP的各项功能,如封装、继承、多态及对类方法的覆盖或重写等

Python中对象的概念很广泛,一切内容都可以看成对象

6.2 类与对象

6.2.1 类的定义

类是一种类型,对象是该类型的一个变量

类是抽象的,一般不占用内存空间;对象是具体的,创建一个对象时要为其特征分配相应的内存空间

定义类的一般格式为

class 类名:
    """类说明"""
    类体
  1. 组成:主要由类头和类体两部分组成
  2. 类头:由关键字class开头,后面是类的名称
  3. 类体:类体中包含类的实现细节,向右缩进对齐。类体中一般包含两部分内容
  4. 数据成员:用来存储特征的值(体现对象的特征),简称为成员
  5. 成员方法:用来对成员进行操作(体现对象的行为),简称为方法
  6. 类说明:类中也可以选择性添加类的文档字符串,对类的功能等进行说明(就是通过注释生成自己的一个API文档)
  • 要使用类中定义的成员和方法,必须对类实例化,即创建类的对象
  • 在Python中,使用赋值的方式创建类的对象,其一般格式为:对象名 = 类名([参数列表])
  • 对象创建后,可以使用“对象.成员”“对象.方法()”调用该类的成员和方法

6.2.2 对象创建和使用

创建一个类的对象,调用类中的方法

#定义类
class Car:
  #定义构造方法,self代表类的对象,而非类本身
  def __init__(self,name):
    self.name = name
  #定义方法
  def getName(self):
    return self.name
#创建对象
c1 = Car("奔驰")
print("这辆汽车的品牌:",c1.getName())
'''
这辆汽车的品牌: 奔驰
'''

6.3 类的成员

6.3.1 成员类型

按照能否在类的外面直接访问,类的成员可分为如下两种

  1. 公有成员:公有成员不以下画线开头,在类的内部可以访问,在类的外面也可以访问
  2. 私有成员:以单下画线或双下画线开头,在类的外面不能直接访问,只能在类的内部访问或在类的外面通过对象的公有方法访问

在形式上,以单下画线或双下画线开头的是私有成员

  1. _xxx:一个以下画线开头的成员。类和派生类可以访问这些成员
  2. __xxx:以两个或更多下画线开头但不能以两个或更多下画线结束的成员。对该成员,只有类自己可以访问,派生类也不能访问

按照归属于类还是对象,类的成员可分为两类

  1. 类成员:定义在类体中且在所有方法外的成员为类成员。类成员属于类本身,一般通过类名调用,不建议使用对象名调用
  2. 实例成员:在类的方法中定义的成员为实例成员。实例成员只能被对象调用。实例成员一般在构造方法__init__()中创建,或在其他方法中创建

创建及使用类的公有成员和私有成员

#定义类
class Woman:
  def __init__(self,name,sex,age):
    self.name = name          #定义公有成员
    self._sex = sex           #定义单下画线私有成员
    self.__age = age          #定义双下画线私有成员
  def getAge(self):
    return self.__age
#创建对象
w = Woman("小芳","Female",18)
print("姓名: %s, 性别: %s, 年龄: %d."%(w.name,w._sex,w.getAge()))
'''
姓名: 小芳, 性别: Female, 年龄: 18
'''

类成员和实例成员的创建及使用

#定义类
class Student:
  chinese = 142                         	
  maths = 1                             	
  english = 141                          	
  #定义构造方法
  def __init__(self,name):                          
    self.name = name                    	
#创建对象
s1 = Student("马允")                                                   
print(s1.name + "的语文成绩: " + str(Student.chinese))     
print(s1.name + "的数学成绩: " + str(Student.maths))   
print(s1.name + "的英语成绩: " + str(Student.english))  
'''
马允的语文成绩: 142
马允的数学成绩: 1
马允的英语成绩: 141
'''

动态添加类的成员

#定义类
class Book:
  def __init__(self,n,p):
    self.name = n
    self.price = p
#创建对象
book1 = Book("堂吉诃德",32)
print("书名:",book1.name,"价格:",book1.price)
book1.author = '塞万提斯'                           
print("书名:",book1.name,"作者:",book1.author,"价格:",book1.price)
'''
书名: 堂吉诃德 价格: 32
书名: 堂吉诃德 作者: 塞万提斯 价格: 32
'''

6.3.2 内置成员

所有的类(无论是系统内置的类还是自定义类)都有一组特殊的成员,其前后各有两个下画线,是类的内置成员

  1. __name__:类的名字,用字符串表示
  2. __doc__:类的文档字符串
  3. __bases__:由所有父类组成的元组
  4. __dict__:由类的成员组成的字典
  5. __module__:类所属模块

查看异常类Exception的内置成员

print("类的名字:",Exception.__name__)
print("类的父类:",Exception.__bases__)
print("类的文档:",Exception.__doc__)
print("类的成员:",Exception.__dict__)
print("类所属模块:",Exception.__module__)
'''
类的名字: Exception
类的父类: (<class 'BaseException'>,)
类的文档: Common base class for all non-exit exceptions.
类的成员: {'__init__': <slot wrapper '__init__' of 'Exception' objects>, :
类所属模块: builtins
'''

6.4 类的方法

6.4.1 方法类型

  1. 公有方法。公有方法的名字不以下画线开头,可以在类的外面通过类名或对象名调用
  2. 私有方法。私有方法以2个或更多下画线开头,可以在类的方法中通过self调用,不能在类的外面直接调用
  3. 静态方法和类方法。静态方法和类方法成员可以通过类名和对象名调用,但不能直接访问属于对象的成员,只能访问属于类的成员,不属于任何对象。静态方法使用装饰器@staticmethod声明,类方法使用装饰器@classmethod声明
  4. 抽象方法。抽象方法一般定义在抽象类中并要求派生类对抽象方法进行实现
#定义类
class A(object):
  def function_p(self):            	
    print("在公有方法中调用:",self.__function())  
    return "公有方法 'function_p'"
  def __function(self):            
    return "私有方法 '__function'"
  @classmethod
  def function_c(cls):            
    return "类方法 'function_c'"
  @staticmethod
  def function_s():       		   	
    return "静态方法 'function_s'"
#创建对象
a1 = A()                       		          
print("对象调用: " + a1.function_p())
print("对象调用: " + a1.function_c())
print("对象调用: " + a1.function_s())
print("类名调用: " + A.function_p(a1))               
print("类名调用: " + A.function_c())
print("类名调用: " + A.function_s()) 
'''
在公有方法中调用: 私有方法 '__function'
对象调用: 公有方法 'function_p'
对象调用: 类方法 'function_c'
对象调用: 静态方法 'function_s'
在公有方法中调用: 私有方法 '__function'
类名调用: 公有方法 'function_p'
类名调用: 类方法 'function_c'
类名调用: 静态方法 'function_s'
'''

6.4.2 属性

  • 属性:一种特殊形式的方法,结合了成员和方法的各自优点,既可以通过属性访问类中的成员,也可以在访问前对用户为成员提供数据的合法性进行检测,还可以设置成员的访问机制
  • 属性通常包括get()方法和set()方法。前者用于获取成员的值,后者用于设置成员的值
  • 除此之外,属性也可以包含其他方法,如删除方法del()

使用属性访问并检查私有成员值的合法性

#定义类.
class Circle:
  def set(self,radius):
    if radius >  0:
      self.__radius  =  radius
      print("圆的面积为: {0}.".format(3.14 * self.__radius ** 2))
    else:
      print("半径 %f 不在规定范围内(>=0),请重新设置!"% radius)
  def get(self):
    return self.__radius         	
#创建对象
c = Circle()
c.set(2.5)
c.set(-2.5)
'''
圆的面积为: 19.625.
半径 -2.500000 不在规定范围内(>=0),请重新设置!
'''

使用属性访问私有成员

#定义类
class Test():
  def __get(self):                 #读成员方法
    return self.__value
  def __set(self,value):           #写成员方法
    self.__value = value
  def __del(self):                 #删除成员方法
    del self.__value
  value = property(__get,__set,__del)    	
t = Test()
t.value = 100                       #写成员
print("value = %d."%t.value)   		#读成员
del t.value                        #删除成员
print("value = %d."%t.value)
'''
value = 100.
AttributeError: 'Test' object has no attribute '_Test__value'
'''

6.4.3 特殊方法

在Python中,类有大量的特殊方法,其中比较常见的是构造方法__init__()和析构方法__del__()

  • 构造方法__init__()用来为类中的成员设置初始值或进行必要的初始化工作,在类实例化时被自动调用和执行
  • 析构方法__del__()一般用来释放对象占用的资源,在删除对象和回收对象空间时被自动调用和执行

构造方法和析构方法的使用

#定义类
class Rectangle(object):                            
  #定义构造方法
  def __init__(self,w,h):
    self.w = w
    self.h = h
    print('执行构造方法...')
  #定义求面积方法
  def getArea(self):
    return self.w * self.h
  #定义析构方法
  def __del__(self):
    print('执行析构方法...')
if __name__ == '__main__':
  rect = Rectangle(3,4)       #创建对象,调用构造方法__init__()
  print("面积为:",rect.getArea())       
  del rect                    #删除对象,调用析构方法__del__()
'''
执行构造方法...
面积为: 12
执行析构方法...
'''

6.5 类的继承与多态

6.5.1 类的继承

继承类称为派生类或子类,被继承类称为父类或基类

在Python中,派生类可以继承一个父类(单继承)或多个父类(多继承)

当派生类继承多个父类时,多个父类之间用逗号隔开

创建派生类的格式为:

class 派生类(父类1,父类2,…):
    类体

派生类可以继承父类的成员和方法,也可以定义自己的成员和方法

如果父类方法不能满足要求,派生类也可以重写父类的方法

类的单继承

#定义People类
class People:
  #定义构造方法
  def __init__(self,n,a):
    self.name = n
    self.age = a
  #定义公有方法
  def speak(self):
    print("我是%s,今年%d岁." % (self.name,self.age))
#定义Student类,继承类People
class Student(People):
  def __init__(self,n,a,g):
    People.__init__(self,n,a)                
    self.grade = g
  #重写父类的方法
  def speak(self):
    print("我是%s,今年%d岁,读%d年级." % (self.name,self.age,self.grade))
  def function_a(self)
    #类内调用两种
    super(Student,self).speak();
    self.speak()
if __name__ == '__main__':
  #创建Student类的对象.
  s = Student('孔融',10,4)
  s.speak()       		                         
  super(Student,s).speak()
  s.function_a()
'''
我是孔融,今年10岁,读4年级.
我是孔融,今年10岁.
'''

类的多继承

#定义People类
class People:
  #定义构造方法
  def __init__(self,n,a):
    self.name = n
  #定义公有方法
  def speak(self):
    print("%s说: 我今年%d岁." % (self.name,self.age))
#定义Speaker类
class Speaker():
  #定义构造方法
  def __init__(self,n,t):
    self.name = n
    self.topic = t
 #定义公有方法
  def speak(self):
    print("我叫%s,是一名科学家,今天演讲的主题是%s." % (self.name,self.topic))
#定义Scientist类,继承People类和Speaker类
class Scientist(Speaker,People):
  #定义构造方法
  def __init__(self,n,a,t):
    People.__init__(self,n,a)
    Speaker.__init__(self,n,t)
if __name__ == '__main__':
  #创建Scientist类的对象,传入参数.
  Hawkin = Scientist("霍金",50,"《时间简史》")
  Hawkin.speak()               	
'''
我叫霍金,是一名科学家,今天演讲的主题是《时间简史》.
'''

6.5.2 类的多态

多态(Polymorphism):一般是指父类的一个方法在不同派生类对象中具有不同表现和行为

派生类在继承了父类的行为和属性之后,还可能增加某些特定的行为和属性,也可能会对继承父类的行为进行一定的改变,这些都是多态的表现形式

类的多态实现

#定义类Animal
class Animal():
  #定义方法
  def getInfo(self):
    return "I am an animal."
#定义类,继承类Animal
class Lion(Animal):
  #重写父类方法  getInfo().
  def getInfo(self):
    return "I am a lion."
#定义类,继承类Animal
class Tiger(Animal):
  #重写父类方法getInfo().
  def getInfo(self):
    return "I am a tiger."
#定义类,继承类Animal
class Leopard(Animal):
  #重写父类方法getInfo()
  def getInfo(self):
    return "I am a leopard."
if __name__ == '__main__':
    objectList = [item() for item in (Animal,Lion,Tiger,Leopard)]
  #不同对象调用同一方法
  for object in objectList:
    print(object.getInfo())
'''
I am an animal.
I am a lion.
I am a tiger.
I am a leopard.
'''

6.6 抽象类和抽象方法

抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同但本质上相同的概念抽象

抽象类特点:抽象类中通常包含抽象方法(没有实现功能),该类不能被实例化,只能被继承,且派生类必须实现抽象类中的抽象方法

Python中一般使用抽象基类(Abstract Base Class,ABC)来实现抽象类

ABC主要定义了基本类和最基本的抽象方法,可以为派生类定义公有的API,不需要具体实现,相当于Java中的接口或抽象类

抽象类和抽象方法的使用

import abc                          		                
#定义抽象类
class People(metaclass=abc.ABCMeta):  
  #定义抽象方法
  @abc.abstractmethod
  def working(self):
    pass
#定义抽象类People的派生类
class Chinese(People):
  #实现抽象类的抽象方法working()
  def working(self):
    print("中国人都在勤奋地工作……")
#创建Chinese类的对象
c1 = Chinese()
c1.working()
'''
中国人都在勤奋地工作……
'''