程序设计基础(Python)之8

阅读: 评论:0

程序设计基础(Python)之8

程序设计基础(Python)之8

目录

  • 8 类和对象
    • 8.1 引子
      • 8.1.1 面向过程和面向对象编程
      • 8.1.2 类和对象:面向对象编程中的两个重要概念
    • 8.2 类的定义
      • 8.2.1 类名
      • 8.2.2 属性
      • 8.2.3 方法
    • 8.3 实例化及使用
      • 8.3.1 实例的创建
      • 8.3.2 访问属性
      • 8.3.3 调用方法
      • 8.3.4 修改属性
        • 8.3.4.1 直接修改
        • 8.3.4.2 通过方法修改属性
    • 8.4 类成员的私有化
    • 8.5 练习:仿照学生类创建教师类
    • 8.6 类的继承
      • 8.6.1 继承基本概念
        • 8.6.1.1 单继承
        • 8.6.1.2 多层继承
        • 8.6.1.3 多继承
      • 8.6.2 继承实例操作
        • 8.6.2.1 回头看两个类,发现共同点
        • 8.6.2.2 抽象出共同的属性和方法,构成 人类
        • 8.6.2.3 学生类继承自人类
        • 8.6.2.4 教师类继承自人类
        • 8.6.2.5 各类的类属性
    • 8.7 多态
      • 8.7.1 多态的基本概念
      • 8.7.2 多态的具体实例
    • 8.8 类的属性修饰符
      • 8.8.1 @property
      • 8.8.2 @<name>.setter
      • 8.8.3 @<name>.deleter
      • 8.8.4 @abstractmethod
      • 8.8.5 @staticmethod
      • 8.8.6 @classmethod
    • 8.9 类的魔法方法
      • 8.9.1 魔法方法概述
      • 8.9.2 常用魔法方法举例
        • 8.9.2.1 构造与初始化
        • 8.9.2.2 类的表示
        • 8.9.2.3 运算符类
        • 8.9.2.4 容器类操作
        • 8.8.2.5 可调用对象

8 类和对象

8.1 引子

8.1.1 面向过程和面向对象编程

  • 面向过程的编程:是一种以过程为中心的编程思想,常用语言:C,Fortran等
  • 面向对象的编程:是一种对现实世界理解和抽象的方法进行编程的思想。常用语言:C++,Java,Python等。
  • 面向过程的编程思想认为:
    程序=数据+算法,一个工程分解为一个个的功能模块,用函数来实现,函数是面向过程的核心 。
  • 面向对象的编程思想认为:
    程序=对象+消息,一个工程由一个个个的对象组成,这些对象之间相互关联,构成整个项目,类和对象是程序的主体。

8.1.2 类和对象:面向对象编程中的两个重要概念

  • 类:是对事物的抽象,比如:狗
  • 对象:是类的一个实例,比如:旺财、大黄

Why:面向对象更符合人类对客观世界的抽象和理解

  • 一切皆对象
    一只小狗,一把椅子,一张信用卡,一条巧克力。。。

  • 一切对象,都有自己内在的属性
    狗狗的品种、椅子的质地、信用卡的额度、巧克力的口味。。。

  • 一切行为,皆是对象的行为
    狗狗蹲下、椅子移动位置、刷信用卡、巧克力融化了。。。

How:类是对象的载体

不同年龄、肤色、品质的猫,每一只都是一个对象

他们有一个共同的特征:都是猫

我们可以把一类对象的公共特征抽象出来,创建通用的类

# 创建类
class Cat():"""模拟猫"""def __init__(self, name):"""初始化属性"""self.name = namedef jump(self):"""模拟猫跳跃"""print(self.name + " is jumping") 
# 用类创建实例对象
my_cat = Cat("Loser")
your_cat = Cat("Lucky")
# 调用属性
print(my_cat.name)
print(your_cat.name)
# 调用方法
my_cat.jump()
your_cat.jump()

8.2 类的定义

三要素:类名、属性、方法

  • 使用class关键字定义一个类,类名按照标识符的要求
  • 当程序员需要创建的类型不能够用简单类型表示时就需要创建类
  • 类需要的变量和函数组合在一起,这种包含也称之为“封装”
class 类名:"""类说明文档字符串"""#----------定义类主体----------# 定义类属性(成员变量)--作用域在整个类中# 定义类方法(成员方法)

8.2.1 类名

  • 符合标识符命名要求:字母、数字和下划线构成,不能够以数字开头,不能够是保留字…
  • 要有实际意义
  • 驼峰命名法——组成的单词首字母大写
    Dog、 CreditCard、 ElectricCar

8.2.2 属性

  • 类属性:
    存在于类空间,推荐用类名访问,也可以通过实例共享性访问,同一个类中该属性值唯一

  • 成员属性:
    存在于实例对象空间,通过实例访问,不同实例属性值可以不同

8.2.3 方法

  • 初始化方法
  • 成员方法
# 创建学生类
class Student():"""模拟学生类0. 具有类属性 numOfStudent, 分别记录男学生和女学生人数1. 具有姓名(name)、性别(sex)、年龄(age)、学号(sno)、班级(sclass)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、查看信息(queryInfo)、上某门课程(study)功能"""# 类属性numOfStudent = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, sno, sclass):# 成员属性self.name  = nameself.sex = sexself.age = ageself.sno = snoself.sclass = sclassif sex == '男':Student.numOfStudent['男'] += 1 #self.numOfStudentum['男'] += 1else:Student.numOfStudent['女'] += 1 #self.numOfStudent['女'] += 1 # 成员函数def eat(self):print(f"{self.name}正在吃饭...")def sleep(self):print(f"{self.name}正在睡觉...")        def queryInfo(self):print(f"{self.__sclass}的{self.__name}正在查看信息公告栏") def study(self, classname):print(f"{self.sclass}的{self.name}正在上{classname}这门课程...")    

8.3 实例化及使用

8.3.1 实例的创建

将实例赋值给对象,实例化过程中,传入相应的参数
v = 类名(必要的初始化参数)

# 用类创建实例对象
stu1 = Student("白骨精","女",999,"s001","信息安全1班")

8.3.2 访问属性

  • 成员属性: 实例名.属性名
  • 类属性:
    • 类名.属性名
    • 实例名.属性名
# 访问成员属性
print(stu1.name)
白骨精
stu1.sex,stu1.age,stu1.sclass,stu1.sno
('女', 999, '信息安全1班', 's001')
# 访问类属性 # 只可读,不可更改
stu1.numOfStudent
{'男': 0, '女': 1}
stu1.nums
1
Student.numOfStudent
{'男': 0, '女': 1}
Student.nums
1
stu1.nums +=1 
stu1.nums 
2
Student.nums 
1
Student.nums += 1 
Student.nums
2
stu1.nums
2
stu2 = Student("红孩儿","男",9,"s002","信息安全2班")
stu2.__dict__
{'name': '红孩儿', 'sex': '男', 'age': 9, 'sno': 's002', 'sclass': '信息安全2班'}
stu2.nums
3
stu1.nums
2
Student.nums
3
stu1.numOfStudent
{'男': 1, '女': 1}
Student.numOfStudent
{'男': 1, '女': 1}
a = 1 
print(id(a))
140730165469584
b=a 
print(id(b))
140730165469584
b=2 
print(id(b))
140730165469616
print(id(a))
140730165469584
list1 = [1,2,3]
id(list1)
2085555428104
list2 = list1 
id(list2)
2085555428104
list2.append(4)
print(list2, id(list2))
[1, 2, 3, 4, 4] 2085555428104
print(list1, id(list1))
[1, 2, 3, 4, 4] 2085555428104

8.3.3 调用方法

实例名.方法名(必要的参数)

# 调用方法
stu1.eat()
白骨精正在吃饭...
stu1.sleep()
白骨精正在睡觉...
stu1.study()
---------------------------------------------------------------------------TypeError                                 Traceback (most recent call last)<ipython-input-60-a25e69cf1e1b> in <module>
----> 1 stu1.study()TypeError: study() missing 1 required positional argument: 'classname'

8.3.4 修改属性

8.3.4.1 直接修改
stu1.name
'白骨精'
stu1.name = '蜘蛛精'

8.3.4.2 通过方法修改属性
# 创建类
class Student():"""模拟学生类0. 具有类属性 numOfStudent, 分别记录男学生和女学生人数1. 具有姓名(name)、性别(sex)、年龄(age)、学号(sno)、班级(sclass)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、查看信息(queryInfo)、上某门课程(study)功能"""# 类属性numOfStudent = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, sno, sclass):# 成员属性self.name  = nameself.sex = sexself.age = ageself.sno = snoself.sclass = sclassif sex == '男':Student.numOfStudent['男'] += 1 #self.numOfStudentum['男'] += 1else:Student.numOfStudent['女'] += 1 #self.numOfStudent['女'] += 1 # 成员函数def eat(self):print(f"{self.name}正在吃饭...")def sleep(self):print(f"{self.name}正在睡觉...")        def queryInfo(self):print(f"{self.__sclass}的{self.__name}正在查看信息公告栏") def study(self, classname):print(f"{self.sclass}的{self.name}正在上{classname}这门课程...")  def setName(self, name):self.name = name
# 用类创建实例对象
stu1 = Student("白骨精","女",999,"s001","信息安全1班")
stu1.writeIofo()
信息安全1班的白骨精写下了'我爱重庆工商职业学院!'
stu1.name
'白骨精'
stu1.setName("蜘蛛精")
stu1.name
'蜘蛛精'

8.4 类成员的私有化

在Python中,私有化是一种封装的概念,用于限制对类的属性和方法的访问。私有化的目的是为了保护类的内部实现细节,防止外部代码直接访问和修改类的私有成员。
在Python中,通过在属性或方法的名称前面添加两个下划线(__)来定义私有成员。

# 创建类
class Student():"""模拟学生类1. 具有姓名(name)、性别(sex)、年龄(age)、学号(sno)、班级(sclass)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、查看信息(queryInfo)、上某门课程(study)功能"""# 类属性numOfStudent = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, sno, sclass):# 成员属性self.__name  = nameself.__sex = sexself.__age = ageself.__sno = snoself.__sclass = sclassif sex == '男':Student.numOfStudent['男'] += 1 #self.numOfStudent['男'] += 1else:Student.numOfStudent['女'] += 1 #self.numOfStudent['女'] += 1 # 成员函数def eat(self):print(f"{self.__name}正在吃饭...")def sleep(self):print(f"{self.__name}正在睡觉...")        def queryInfo(self):print(f"{self.__sclass}的{self.__name}正在查看信息公告栏") def study(self, classname):print(f"{self.__sclass}的{self.__name}正在上{classname}这门课程...")  def setName(self, name):self.__name = name
# 用类创建实例对象
stu1 = Student("白骨精","女",999,"s001","信息安全1班")
stu1.eat()
白骨精正在吃饭...
stu1.queryInfo()
信息安全1班的白骨精正在查看信息公告栏
stu1.__name = "蜘蛛精"
stu1.queryInfo()
信息安全1班的白骨精正在查看信息公告栏
del stu1.__name
stu1.setName("蜘蛛精")
stu1.queryInfo()
信息安全1班的蜘蛛精正在查看信息公告栏

8.5 练习:仿照学生类创建教师类

"""
模拟教师类0. 具有类属性 numOfTeacher, 分别记录男教师和女教师人数1. 具有姓名(name)、性别(sex)、年龄(age)、工号(tno)、院系(tdept)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、发布信息(publishInfo)、教授某门课程(teach)功能
"""
# 创建教师类
class Teacher():"""模拟教师类0. 具有类属性 numOfTeacher, 分别记录男教师和女教师人数1. 具有姓名(name)、性别(sex)、年龄(age)、工号(tno)、院系(tdept)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、发布信息(publishInfo)、教授某门课程(teach)功能"""# 类属性numOfTeacher = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, tno, tdept):# 成员属性self.name  = nameself.sex = sexself.age =  = tnoself.tdept = tdeptif sex == '男':Teacher.numOfTeacher['男'] += 1 #self.numOfTeacher['男'] += 1else:Teacher.numOfTeacher['女'] += 1 #self.numOfTeacher['女'] += 1 # 成员函数def eat(self):print(f"{self.name}正在吃饭...")def sleep(self):print(f"{self.name}正在睡觉...")def publisInfo(self):print(f"{self.tdept}的{self.name}写下了'明天考试,同学们好好复习!'") def teach(self, classname):print(f"{self.tdept}的{self.name}正在上{classname}这门课程...")    
# 用类创建实例对象
t1 = Teacher("唐僧","男",29,"t001","东土大唐1班")
t1.tno,t1.name,t1.age,t1.sex,t1.tdept
('t001', '唐僧', 29, '男', '东土大唐1班')
t1.publisInfo()
东土大唐1班的唐僧写下了'明天考试,同学们好好复习!'
t1.teach("《三藏真经》")
东土大唐1班的唐僧正在上《三藏真经》这门课程...

8.6 类的继承

8.6.1 继承基本概念

看一下人在生物界的分支链
生物——动物界——脊索动物门——哺乳动物纲——灵长目——人科——人属——智人种

公共特征逐渐增加的过程
【问题】
假设二元系统: 人属 = {A人种, B人种, C人种。。。。}
为每一个人种构造一个类

方案一:
各自独立,分别构造各自人种的类

方案二:
1、将各人种公共特征提取出来,建立人属的类;
2、各人种继承上一级(人属)的公共特征,然后添加自身特殊特征,构建各自人种的类。

通常,我们选择方案二,因为他避免了过多的重复劳动

所谓继承,就是低层抽象继承高层抽象的过程

  • 继承:描述的类与类之间所属关系。
  • 基本语法:
class 类B(类A)pass  

称为类 B 继承A

  • 特点:B类的对象可以使用 A类的属性和方法
  • 优点:代码复用。重复相同的代码不用多次书写
  • 名词:
    • 类A:父类 基类
    • 类B:子类 派生类

继承分类:

  • 单继承:如果一个类只有一个父类,把这种继承关系称为单继承

  • 多继承:如果一个类有多个父类,把这种继承关系称为多继承

  • 多层继承:C–>B–>A

8.6.1.1 单继承

如果一个类只有一个父类,把这种继承关系称为单继承

# 定义是个 动物类 animal类
class Animal(object):# 在animal类书写play方法,输出快乐的玩耍def play(self):print('快乐的玩耍...')# 定义Dog类继承animal类
# 定义Dog类继承自Animal类
class Dog(Animal):pass
# 创建dog类对象,调用父类的方法
dog = Dog()
dog.play()
快乐的玩耍...
8.6.1.2 多层继承

C–>B–>A

# 定义是个 动物类 animal类
class Animal(object):  # 对于Animal类和object类来说,单继承# 在animal类书写play方法,输出快乐的玩耍def play(self):print('快乐的玩耍...')# 定义Dog类继承animal类# 定义Dog类继承animal类
class Dog(Animal):  # Dog-->Animal 也是单继承,Dog-->Animal-->object 这种继承关系称为多层继承def bark(self):print('汪汪叫......')# 定义类XTQ类,继承Dog类
# 多层继承中,子类可以使用所有继承链中的类中的方法和属性
class XTQ(Dog):  # XTQ--->Dog 单继承,XTQ--->Dog-->Animal 类,多层继承pass
xtq = XTQ()
xtq.bark()  # 调用父类Dog中的方法
xtq.play()  # 调用爷爷类 animal类中的方法
汪汪叫......
快乐的玩耍...
8.6.1.3 多继承

如果一个类有多个父类,把这种继承关系称为多继承。

举个例子,我们可以以小米手机为例。如果我们将小米手机看作子类,可以认为它有三个父类。首先,小米手机本身是一款手机,这是毫无疑问的。另外,我们也知道安卓手机可以作为 NFC 读卡器使用,用于门禁卡、公交卡等的复制。此外,小米手机也可以作为红外遥控器使用,用于电视、空调等设备的控制。一个看似简单的小米手机不仅仅是一部手机,还包含了许多额外的功能。

现在我们来用这个例子来描述多继承的概念。假设在我们的程序中,我们定义了三个类:手机、NFC读卡器和红外遥控器。有了这三个类之后,我们可以构建一个小米手机,让它同时继承这三个类,这样小米手机就可以拥有它们三个类的所有属性和方法。这个想法是可行的。

现在让我们来看一下多继承的语法。与单继承类似,我们只需要在子类中的括号里写出所有要继承的父类名字即可。不同之处在于,多继承需要使用逗号来分隔不同的父类名字。如果要继承多个父类,只需在逗号后面继续添加父类名即可。因此,多继承的语法非常简单。Python的类之间也支持多继承,即一个类,可以继承多个父类:

class 类名(父类1,父类2,……,父类N):类内容体
# 手机类
class Phone:IMEI = None # 序列号producer = None # 厂商def call_by_5g(self):print("5g通话")
# NFC读卡器类
class NFCReader:nfc_type="第五代"producer = "HM"def read_card(self):print("读取NFC卡")def write_card(self):print("写入NFC卡")
# 红外遥控器类
class RemoteControl:rc_type="红外遥控"def control(self):print("红外遥控开启")
# 多继承小米手机类
class MiPhone(Phone,NFCReader,RemoteControl):pass

多继承注意事项:

多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。
即:先继承的保留,后继承的被覆盖

my_phone = MiPhone()
print(my_phone.producer) # 结果为None而非"HM"
None
my_phone.call_by_5g()  # 继承自Phone的打5g电话功能
5g通话
print(my_phone.IMEI) #继承自Phone的序列号属性
None
ad_card()  # 继承自NFCReader的读卡器功能
读取NFC卡
l()  #继承自RemoteControl的红外遥控开启功能
红外遥控开启

8.6.2 继承实例操作

8.6.2.1 回头看两个类,发现共同点

针对前面的Student类与Teacher类,再次考虑下

  • 学生类
# 学生类
class Student():"""模拟学生类1. 具有姓名(name)、性别(sex)、年龄(age)、学号(sno)、班级(sclass)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、填写信息(WriteInfo)、上某门课程(study)功能"""# 类属性numOfStudent = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, sno, sclass):# 成员属性self.name  = nameself.sex = sexself.age = ageself.sno = snoself.sclass = sclassif sex == '男':Student.numOfStudent['男'] += 1 #self.numOfStudentum['男'] += 1else:Student.numOfStudent['女'] += 1 #self.numOfStudent['女'] += 1 # 成员函数def eat(self):print(f"{self.name}正在吃饭...")def sleep(self):print(f"{self.name}正在睡觉...")def queryInfo(self):print(f"{self.__sclass}的{self.__name}正在查看信息公告栏") def study(self, classname):print(f"{self.sclass}的{self.name}正在上{classname}这门课程...")    
  • 教师类
# 教师类
class Teacher():"""模拟教师类1. 具有姓名(name)、性别(sex)、年龄(age)、工号(tno)、院系(tdept)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、发布信息(publishInfo)、教授某门课程(teach)功能"""# 类属性numOfTeacher = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, tno, tdept):# 成员属性self.name  = nameself.sex = sexself.age =  = tnoself.tdept = tdeptif sex == '男':Teacher.numOfTeacher['男'] += 1 #self.numOfTeacher['男'] += 1else:Teacher.numOfTeacher['女'] += 1 #self.numOfTeacher['女'] += 1 # 成员函数def eat(self):print(f"{self.name}正在吃饭...")def sleep(self):print(f"{self.name}正在睡觉...")def publisInfo(self):print(f"{self.tdept}的{self.name}写下了'明天考试,同学们好好复习!'") def teach(self, classname):print(f"{self.tdept}的{self.name}正在上{classname}这门课程...")    
8.6.2.2 抽象出共同的属性和方法,构成 人类
# 人类
class Person():"""模拟人类1. 具有姓名(name)、性别(sex)、年龄(age)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、功能"""# 类属性numOfPerson = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age):# 成员属性self.name  = nameself.sex = sexself.age = ageif sex == '男':Person.numOfPerson['男'] += 1 #self.numOfPerson['男'] += 1else:Person.numOfPerson['女'] += 1 #self.numOfPerson['女'] += 1 # 成员函数def eat(self):print(f"{self.name}正在吃饭...")def sleep(self):print(f"{self.name}正在睡觉...")
8.6.2.3 学生类继承自人类
class Student(Person):# 学生类属性numOfStudent = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, sno, sclass):super().__init__(name, sex, age)  # 调用父类初始化方法# 成员属性        
#         self.name  = name  #父类属性,父类种初始化
#         self.sex = sex
#         self.age = age# 子类独有成员属性,子类中初始化self.sno = snoself.sclass = sclassif sex == '男':Student.numOfStudent['男'] += 1 #self.numOfStudentum['男'] += 1else:Student.numOfStudent['女'] += 1 #self.numOfStudent['女'] += 1 # 父类共有成员函数,父类中定义
#     def eat(self):
#         print(f"{self.name}正在吃饭...")#     def sleep(self):
#         print(f"{self.name}正在睡觉...")# 子类独有方法,子类中定义   def queryInfo(self):print(f"{self.sclass}的{self.name}正在查看信息公告栏") def study(self, classname):print(f"{self.sclass}的{self.name}正在上{classname}这门课程...")    
# 用类创建实例对象
stu1 = Student("白骨精","女",999,"s001","信息安全1班")
stu2 = Student("红孩儿","男",9,"s002","信息安全2班")
stu1.eat() # 父类中方法
白骨精正在吃饭...
stu2.eat()
红孩儿正在吃饭...
stu1.study("《炼化白骨》") #子类方法
信息安全1班的白骨精正在上《炼化白骨》这门课程...
stu2.queryInfo()
信息安全2班的红孩儿正在查看信息公告栏
8.6.2.4 教师类继承自人类
# 教师类
class Teacher(Person):"""模拟教师类1. 具有姓名(name)、性别(sex)、年龄(age)、工号(tno)、院系(tdept)这几个属性  2. 具有吃饭(eat)、睡觉(sleep)、发布信息(publishInfo)、教授某门课程(teach)功能"""# 类属性numOfTeacher = {'男':0, '女':0}# 初始化函数def __init__(self, name, sex, age, tno, tdept):super().__init__(name, sex, age)  # 调用父类初始化方法# 成员属性   
#         self.name  = name
#         self.sex = sex
#         self.age =  = tnoself.tdept = tdeptif sex == '男':Teacher.numOfTeacher['男'] += 1 #self.numOfTeacher['男'] += 1else:Teacher.numOfTeacher['女'] += 1 #self.numOfTeacher['女'] += 1 # 成员函数
#     def eat(self):
#         print(f"{self.name}正在吃饭...")#     def sleep(self):
#         print(f"{self.name}正在睡觉...")def publisInfo(self):print(f"{self.tdept}的{self.name}写下了'明天考试,同学们好好复习!'") def teach(self, classname):print(f"{self.tdept}的{self.name}正在上{classname}这门课程...")    
# 用类创建实例对象
t1 = Teacher("唐僧","男",29,"t001","东土大唐1班")
t2 = Teacher("观音","女",666,"t002","南海普陀2班")
t1.eat() #父类方法
唐僧正在吃饭...
t2.sex #父类属性
'女'
t1.publisInfo() #子类方法
东土大唐1班的唐僧写下了'明天考试,同学们好好复习!'
8.6.2.5 各类的类属性
Student.numOfStudent #学生类自己的类属性 
{'男': 1, '女': 1}
Student.numOfPerson #学生类继承的父类的类属性
{'男': 2, '女': 2}
stu1.numOfStudent #通过实例访问的类属性
{'男': 1, '女': 1}
stu1.numOfPerson #通过实例访问的类属性
{'男': 2, '女': 2}
Teacher.numOfTeacher
{'男': 1, '女': 1}
Teacher.numOfPerson
{'男': 2, '女': 2}

8.7 多态

面向对象编程的三大特性:

  • 封装
  • 继承
  • 多态
    详细博文见

8.7.1 多态的基本概念

多态实际上指的是多种状态。
当我们完成某个具体的行为时,使用不同的对象就会得到不同的状态。
使用一个同样的行为(例如函数),但是传入不同的对象可以得到不同的运行状态。

8.7.2 多态的具体实例

那么如何理解这句话呢?我们可以看一下如下示例代码:

  • 首先,我们定义了一个 Animal 类,其中有一个方法 speak 表示动物发出声音。
  • 然后我们定义了两个子类——狗(Dog)和猫(Cat),它们都继承了 animal 类并且复写了 speak 方法。
    比如,狗的叫声输出为“汪汪汪”,猫的叫声输出为“喵喵喵”。
# 父类: Animal
class Animal():def speak():pass# 讲了属性修饰符时再添加@staticmethoddef makeNoise2(animal):animal.speak()
# 子类:Dog
class Dog(Animal):def speak(self):print("小狗汪汪叫")
# 子类:Cat
class Cat(Animal):def speak(self):print("小猫喵喵喵")
# 实例化一只猫,一条狗
cat = Cat()
dog = Dog()
# 定义一个函数
def makeNoise(animal):animal.speak()
makeNoise(dog)
小狗汪汪叫
makeNoise(cat)
小猫喵喵喵

以下内容讲了属性修饰符时再添加

Animal.makeNoise2(dog)
小狗汪汪叫
cat.makeNoise2(dog) # 对象是谁不重要,重要的是参数是谁
小狗汪汪叫
cat.makeNoise2(cat) # 对象是谁不重要,重要的是参数是谁
小猫喵喵喵

8.8 类的属性修饰符

属性修饰符(Property Decorators)是 Python 中一个重要的特性,它可以帮助我们控制类的属性的访问方式。

  • 常见属性修饰符:
    • @property: 用于将方法转换为只读属性。
    • @.setter: 用于将方法转换为可写属性的赋值方法。
    • @.deleter: 用于将方法转换为可删除属性的方法。
    • @abstractmethod: 用于定义抽象方法,必须被子类实现。
    • @staticmethod: 用于定义静态方法,类和实例都可以调用。
    • @classmethod: 用于定义类方法,只能被类调用,不能被实例调用。

8.8.1 @property

@property修饰符将一个方法转换为只读属性。
使用@property修饰符的方法可以像访问属性一样被调用,而无需使用函数调用的方式。

class Circle:def __init__(self, radius):self.radius = radius@propertydef diameter(self):return self.radius * 2
# 实例化
circle = Circle(5)
circle.diameter
10

8.8.2 @<name>.setter

@<name>.setter修饰符用于将一个方法转换为可写属性的赋值方法。其中<name>是属性名。
需要注意的是,使用@<name>.setter修饰符时,需要先定义一个同名的只读属性,否则会抛出AttributeError异常。

class Circle:def __init__(self, radius):self._radius = radius@propertydef radius(self):return self._radius@radius.setterdef radius(self, value):self._radius = value@propertydef diameter(self):return self._radius * 2
circle = Circle(5)
print(circle.diameter)  # 输出10
circle.radius = 10
print(circle.diameter)  # 输出20
10
20

8.8.3 @<name>.deleter

@<name>.deleter修饰符用于在类中定义一个属性的删除方法。这个修饰符是用来定义一个方法,当删除一个实例中的属性时,该方法就会被调用。

class MyClass:def __init__(self):self._name = None@propertydef name(self):return self._name@name.setterdef name(self, value):self._name = value@name.deleterdef name(self):print('deleter is called!')del self._name
obj = MyClass()
obj.name = "John"
print(obj.name)  # Output: Johndel obj.name
print(obj.name)  
John
deleter is called!---------------------------------------------------------------------------AttributeError                            Traceback (most recent call last)<ipython-input-42-8d122c5f1601> in <module>4 5 del obj.name
----> 6 print(obj.name)<ipython-input-41-b4d1f73c186a> in name(self)5     @property6     def name(self):
----> 7         return self._name8 9     @name.setterAttributeError: 'MyClass' object has no attribute '_name'

8.8.4 @abstractmethod

在Python中,抽象基类是一种特殊类型的类,它不能被实例化,只能被继承,并且它定义了一组抽象方法,子类必须实现这些方法。Python提供了abc模块来支持抽象基类的定义和使用。

@abstractmethod是Python中用于定义抽象方法的装饰器。定义一个抽象方法时,需要使用该装饰器标记方法,以指示该方法是抽象的。如果子类没有实现该方法,将会抛出TypeError异常。

from abc import ABC, abstractmethodclass Shape(ABC):@abstractmethoddef area(self):pass@abstractmethoddef perimeter(self):passclass Square(Shape):def __init__(self, side):self.side = sidedef area(self):return self.side ** 2def perimeter(self):return 4 * self.side
s = Square(5)
print("Area:", s.area())
print("Perimeter:", s.perimeter())
---------------------------------------------------------------------------TypeError                                 Traceback (most recent call last)<ipython-input-46-24cd40342252> in <module>
----> 1 s = Square(5)2 print("Area:", s.area())3 print("Perimeter:", s.perimeter())TypeError: Can't instantiate abstract class Square with abstract methods area

8.8.5 @staticmethod

Python中的@staticmethod属性修饰符用于定义一个静态方法。
静态方法是一个不需要类实例化就可以被类直接调用的方法。
它可以用来执行与类相关的操作,但不需要访问类的实例。
Python里面静态方法和我们平时写的函数def是一样的。唯一不同之处就是它定义于类控件,可以存在于类中。
静态方法可以通过类名.静态方法名()调用,以恶可以通过实例对象.静态方法名()调用

# 定义静态方法
class MyClass():@staticmethoddef my_static_method():print("This is a static method.")
# 调用静态方法
# 通过类名调用(推荐)
_static_method()
This is a static method.
# 调用静态方法
# 通过实例对象调用(不推荐)
myclass1 = MyClass()
_static_method()
This is a static method.

8.8.6 @classmethod

@classmethod是一个属性修饰符,用于定义类方法。
类方法是在类级别上运行的方法,可以通过类或类的实例调用。
类方法不需要访问实例变量,也不需要创建实例。
在类方法中,第一个参数通常是cls(代表类本身),而不是self(代表实例)。

@classmethod属性修饰符用于将普通的方法转换为类方法。
它接受一个cls参数,这个参数是类对象本身,并且可以用于访问类变量和调用其他类方法。

class Person:total_people = 0def __init__(self, name):self.name = al_people += 1@classmethoddef show_total_people(cls):print("Total people:", al_people)
person1 = Person("Alice")
person2 = Person("Bob")Person.show_total_people()# 通过类名访问类方法(不推荐)
Total people: 4
person1.show_total_people() # 通过实例访问类方法(不推荐)
Total people: 4

补充区分:实例方法、类方法 & 静态方法:

在开发的时候, 可以使用类对方法进行封装。

  • 如果某一个方法需要访问到对象的实例属性,可以把这个方法封装成一个实例方法。
  • 如果某一个方法不需要访问对象的实例属性,但是需要访问到类的类属性,这个时候就可以考虑把这个方法封装成一个类方法。
  • 如果要封装的某一个方法,既不需要访问到对象的实例属性,也不需要访问类的类属性,这个时候就可以考虑把这个方法封装成一个静态方法。

8.9 类的魔法方法

8.9.1 魔法方法概述

Python中的魔法方法(Magic Methods),也称为双下划线方法(Dunder Methods),是特殊方法,其名称以双下划线开头和结尾,例如__init__、str、__add__等。这些方法提供了一种使Python对象能够使用内置函数和语言结构的方式。
详细介绍见:

8.9.2 常用魔法方法举例

8.9.2.1 构造与初始化
  • __init__() : 当一个实例被创建的时候调用的初始化方法
  • __new__(): 对象实例化的时候所调用的第一个方法.在对象初始化时,首先执行__new__()方法,再执行__init__()方法。
  • __del__():当一个实例被销毁时自动执行,无需调用。
class Test():def __new__(cls, *args, **kwargs):obj = object.__new__(cls)print('__new__()方法被调用')print(obj)return objdef __init__(self):print('__init__()方法被调用')print(self)
t = Test()
__new__()方法被调用
<__main__.Test object at 0x0000017FE9A4E348>
__init__()方法被调用
<__main__.Test object at 0x0000017FE9A4E348>
class Test():def __new__(cls, *args, **kwargs):obj = object.__new__(cls)print('__new__()方法被调用')print(obj)return objdef __init__(self):print('__init__()方法被调用')print(self)def __del__(self):print('对象被销毁')
t = Test()  # 需要到pycharm中运行
__new__()方法被调用
<__main__.Test object at 0x0000017FEA56E248>
__init__()方法被调用
<__main__.Test object at 0x0000017FEA56E248>
8.9.2.2 类的表示
  • __str__() / __repr__() :
    这两个方法都是用来描述类或对象信息的,比如你直接实例化了一个对象,打印出来的是这个对象的地址。而要是重新在类中定义了这两个方法,那打印对象的结果就是方法返回的信息。

  • __bool__():
    当调用 bool(obj) 时,会调用 bool() 方法,返回 True 或 False:

  • __str__() / __repr__()

class Washer:def __int__(self):passdef __repr__(self):return '我是__repr__()魔法方法!'def __str__(self):"""这个str的作用就是:类的说明或对象状态的说明:return:"""return '我是__str__魔法方法!'
haier = Washer()
# 不定义str方法,直接打印,结果是对象的内存地址,定义了str方法,
# 显示的就是str方法返回的内容
print(haier)  # 我是__str__魔法方法
我是__repr__()魔法方法!
str(haier)
'我是__repr__()魔法方法!'

要是同时写了这两个方法,只会调用__str__方法。都是用来描述类或对象的信息,那为啥要定义两个呢?

设计的目的是不一样的:
1. __repr__的目标是准确性,或者说,__repr__的结果是让解释器用的。
2. __str__的目标是可读性,或者说,__str__的结果是让人看的。更详细的信息参考:link。

  • __bool__()
class Person(object):def __init__(self, uid):self.uid = uiddef __bool__(self):return self.uid > 10
p1 = Person(4)
p2 = Person(14)
print(bool(p1))  # False
print(bool(p2))  # True
False
True
8.9.2.3 运算符类
8.9.2.3.1 比较运算符
  • __eq__():可以判断两个对象是否相等:
  • __ne__():可以判断两个对象是否不箱等
  • __lt__(): <
  • __gt__(): >
class Test:def __init__(self, name, age):self.name = nameself.age = agedef __eq__(self, other):return self.age == other.agedef __gt__(self, other):return self.age > other.agedef __lt__(self, other):return self.age < other.agedef __ne__(self, other):return self.age != other.age
t1 = Test('小王', 20)
t2 = Test('小张', 30)
print(t1 == t2)  # False
print(t1 > t2)   # False
print(t1 < t2)  # True
print(t1 != t2) # True
False
False
True
True
8.9.2.3.2 算术运算符
  • __add__ :加
  • __sub__ :减
  • __mul__ :乘
  • __truediv__ :除
  • __floordiv__ :向下取整
  • __mod__ :取余
  • __pow__ :乘方
class Test:def __init__(self, name, age):self.name = nameself.age = agedef __add__(self, other):return self.age + other.agedef __sub__(self, other):return self.age - other.agedef __mul__(self, other):return self.age * other.agedef __truediv__(self, other):return self.age / other.agedef __floordiv__(self, other):return self.age // other.agedef __mod__(self, other):return self.age % other.agedef __pow__(self, power, modulo=None):return pow(self.age, power)
t1 = Test('小王', 20)
t2 = Test('小张', 30)
print(t1 + t2)  # 50
print(t1 - t2)   # -10
print(t1 * t2)  # 600
print(t1 / t2) # 0.666
print(t1 // t2) # 0
print(t1 % t2) # 20
print(t1 ** 3) # 8000
50
-10
600
0.6666666666666666
0
20
8000
8.9.2.4 容器类操作
  • __setitem__(self, key, value): 赋值操作,a[key]=value
  • __getitem__(self, item): 索引操作,a[item]
  • __delitem__(self, key): 删除元素,del a[key]
  • __len__(self): 容器长度,len(a)
  • __iter__(self):定义当迭代容器中的元素的行为;
  • __contains__(self, item): 是否包含某个元素,in/not in
  • __reversed__(self): 反转容器,reversed() - `
class Mylist:def __init__(self, values):self._index = 0self.values = values or []def __setitem__(self, key, value):self.values[key] = valuedef __getitem__(self, item):return self.values[item]def __delitem__(self, key):del self.values[key]def __len__(self):return len(self.values)def __contains__(self, item):return item in self.valuesdef __reversed__(self):return list(reversed(self.values))def __str__(self):return str(self.values)def __iter__(self):# 可迭代return selfdef __next__(self):# 迭代的具体细节# 如果__iter__返回self 则必须实现此方法if self._index >= len(self.values):raise StopIteration()value = self.values[self._index]self._index += 1return value
a = Mylist([1, 2, 3, 4])
print(a) # [1, 2, 3, 4]print(a[2]) # 3a[2] = 10
print(a) # [1, 2, 10, 4]print(len(a)) # 4print(1 in a) # Trueprint(reversed(a)) # [4, 10, 2, 1]del a[2]
print(a) # [1, 2, 4]
[1, 2, 3, 4]
3
[1, 2, 10, 4]
4
True
[4, 10, 2, 1]
[1, 2, 4]
[d for d in a]
[1, 2, 4]
8.8.2.5 可调用对象
  • __call__: 允许实例当成函数去执行。当在实例后加括号即实例()或者类()()时,会自动调用__call__方法。
class Test:def __init__(self, name, age):self.name = nameself.age = agedef __call__(self, x, y):return x + y
a = Test('小王', 20)
print(a.name, a.age)  # 小王 20t = a(100, 200)
print(t) # 300
print(callable(a)) # True
小王 20
300
True

本文发布于:2024-02-01 04:06:28,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170673158933734.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:程序设计   基础   Python
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23