14 面向对象
14.1 面向对象基础
面向对象是一种编程思想,处理面向对象编程以外,还有面向过程编程
三大特征:
1. 封装
2. 继承
3. 多态
面向过程与面向对象的区别
- 面向过程: 自己动手洗衣服,拧衣服、晾衣服
- 面向对象:构造一个洗衣机对象,让洗衣机对象完成
14.2 类和对象
类:相同属性和功能的一类事物。
人是一个类,张三(对象),李四(对象),王五(对象)
狗是一个类,张三的柯基(对象),张三的哈士奇(对象),王五的田园犬(对象)
类的写法
class 类名:
pass
# 一般首字母大写,使用驼峰命名法。
类的三要素
- 类名
- 属性
- 方法
class Human:
hair = 'gold'
print(Human.hair)
Human.hair = 'black'
print(Human.hair)
Human.name = '比尔盖茨'
del Human.hair #删除属性
实例化对象
执行实例方法时,自动将调用该方法的对象赋值给self
# 实例化对象的格式: 对象名 = 类名()
# 实例方法:由对象来调用,至少一个self参数;
# 执行实例方法时,自动将调用该方法的对象赋值给self
class Person:
name = "zhangsan"
def speak(self):
print("self")
print(self) #这里的self就是当前调用函数的对象
print("正在说话")
p1 = Person()
print(p1)
p1.speak()
14.3 构造方法
实例属性: self.属性名
class A:
name = 'zhangsan' #类属性
def work(self): #实例方法
print("实例方法")
print(f"张三的年龄是{self.age}")
a = A()
a.age = 20
a.work()
构造方法
类在实例化的时候,会自动执行init方法
class A:
def __init__(self,name):
self.name = name
a = A("张三")
print(a.name)
class Hero:
def __init__(self,name,hp,at):
self.name = name
self.hp = hp
self.at = at
def move(self):
print(f"{self.name}在移动")
def attack(self):
print(f"{self.name}的生命值是{self.hp}")
# 实例化对象
libai = Hero("李白",2000,120)
libai.move()
libai.attack()
luban = Hero("鲁班",2100,200)
luban.move()
luban.attack()
14.4 类属性和实例属性
总结:
类属性属于类,实例属性属于对象
实例属性属于每个对象,对象不同实例属性不同。实例属性需要添加self
class A:
num = 10 #类属性
def __init__(self,name):
self.name = name #实例属性
def test(self):
print(f"我是{self.name}")
# 第一次实例化对象
a = A("张三")
a.test()
print(A.num) #通过类名查看类属性
print(a.num) #通过对象查看类属性
print(a.name) #通过对象查看实例属性
# 第二次实例化对象
a1 = A("李四")
a1.test()
print(a1.name)
print(a1.num)
14.5 析构方法
当对象被垃圾回收的时候,解释器会默认调用del的方法。
class A:
def __del__(self):
print("执行析构方法")
a = A()
print("打印最后一句")
# 运行结果
# 打印最后一句
# 执行析构方法
class A:
def __del(self):
print("执行析构方法")
a = A()
del p
print("打印最后一句")
# 运行结果
# 执行析构方法
# 打印最后一句
14.6 封装
14.6.1 封装的概念
将复杂的信息、流程给包起来,内部处理,让使用者只需要通过简单的操作步骤,就能实现。
封装的概念:
- 类 ,本身就是一种封装
- 类中定义私有,只在类的内部使用,外部无法访问
14.6.2 私有属性
私有权限:在属性名和方法名前加上两个下划线
class Person:
name = "jiuge" #类属性
__age = 18 #类的私有属性
a = Person()
print(a.name)
print(a.__age) #会报错
# 私有属性:外部不能直接访问
# 如何访问私有属性,不建议使用
# 第一种方式
# _类名__属性名
print(pe._Person__age)
# 第二种方式
class Person:
name = "jiuge"
__age = 18
def getAge(self): #获得私有属性的方法
return Person.__age
pe = Person()
print(pe.getAge())
14.6.3 私有方法、proteted方法
_xx : 单下划线开头,protected属性/方法,在对象和子类可以访问,其实并没有什么卵用
__xx : 双下划线开头,私有权限,无法在外部直接访问
class Person:
name = "jiuge" #类属性
_sex = '女' #protected方法
__age = 18 # 私有属性
a = Person()
print(a._sex) #可以访问
print(a.__age) #不可以访问
私有方法
双下划线开头
class Human:
def __play(self): #私有方法
print("玩手机")
def funa(self): #实例方法
# 不推荐
# Human.__play(self) #在实例方法中调用私有方法
self.__play() #直接用这个就可以了
ha = Human()
ha.__play() #会报错
ha.play() #会报错
ha.funa()
14.7 继承(单继承、多继承)
14.7.1 单继承的例子
继承可以使子类具有父类的所有属性和方法
语法:class 类名(父类名)
class A:
def __init__(self):
self.age = 18
def add(self):
print(self.age+10)
class B(A): #B类继承A类
pass
b = B()
print(b.age)
b.add()
class Animals: #父类
def eat(self):
print("吃")
def sleep(self):
print("睡")
class Pig(Animals):#子类
pass
class Dog(Animals): #子类
pass
pig = Pig() #实例化子类
pig.eat()
pig.sleep()
dog = Dog() #实例化子类
dog.eat()
dog.sleep()
14.7.2 单继承的传递性
如果C类(子类)继承于B类(父类),B类(子类)继承于A类(父类)
那么C类具有A类、B类的属性和方法
class A:
def eat(self):
print("吃")
def sleep(self):
print("睡")
class B(A): #继承于A类
def study(self):
print("学习")
class C(B): #继承于B类
pass
b = B()
b.eat()
b.study()
c = C()
c.study() #调用父类的方法
c.eat() #调用父类的父类的方法
14.7.3 多继承
python中内置属性__mro__
可以查看方法搜索顺序
确定子类对象调用方法的顺序
print(Son.__mro__)
# 打印执行方法的顺序
print(C.__mro__)
子类拥有所有父类的属性和方法
class A:
def eat(self):
print("吃")
class B:
def sleep(self):
print("睡")
class C(A,B): #C多继承A类和B类
pass
c = C()
c.eat()
c.sleep()
若D类继承于A类、B类
则D会先找A类中的方法,如果找不到再找B类的方法
class A:
def h(self):
print("a")
class B:
def h(self):
print("c")
class C(A,B):
pass
c = C()
c.h()
# 输出结果
# a
class D(B,A):
pass
# 输出结果
# c
查找顺序 D->A->B->C
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y3SN4NnR-1625891783101)(https://img2020.cnblogs.com/blog/1466380/202006/1466380-20200604210907285-1409082663.png)]
查找顺序D->B->A->C
查找顺序E->B->A->D->C
查找顺序 E->B->A->D->C->F
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DH3Pofj4-1625891783106)(https://img2020.cnblogs.com/blog/1466380/202006/1466380-20200604212718837-1012657606.png)]
14.7.4 继承的例子
奥特曼的例子
#奥特曼
# 一代产品
class Aoteman(object):
def __init__(self,year,name):
self.year = year
self.name = name
def speak(self):
print(f"{self.name}说,爱是一道光")
def attack(self):
print(f"{self.year}年的{self.name}会发光")
at = Aoteman(1996,"初代奥特曼")
at.speak()
at.attack()
# 二代奥特曼
class Aoteman2(Aoteman): #继承一代奥特曼的特性
def attack(self):
print(f"{self.year}年的{self.name}会投飞镖")
at2 = At2(1998,"赛文")
at2.speak()
at2.attack()
# 三代奥特曼
class At3(object):
def defend(self): #防御
print("会躲避怪兽的攻击")
class Aoteman3(Aoteman,At3): #多继承
def attack(self):
print(f"{self.year}年的{self.name}奥特曼会光之射线")
def color(self):
print(f"{self.name}奥特曼会变色")
at3 = Aoteman3(2000,'迪迦')
at3.speak()
at3.attack()
at3.color()
at3.defend()
14.8 重写
子类继承父类的方法和属性,对父类的方法和属性进行重写
14.8.1 覆盖父类方法
在子类中定义一个跟父类同名的方法
class Person:#父类
def work(self):
print("接受社会的考验")
class Son(Person): #子类
def work(self):
print("接受社会的毒打")
son = Son()
son.work()
# 运行结果
# 接受社会的毒打
14.8.2 对父类的方法进行扩展:
继承父类的方法,子类可以增加自己的功能
实现方法
- 父类名.方法名(self)
- super().方法名()
父类名.方法名(self)
class Person:
def work(self):
print("接受社会的考验")
class Son(Person):
def work(self):
Person.work(self)
print("接受社会的毒打")
son = Son()
son.work()
super().方法名()
super是一个特殊的类,super()是使用super类创建出来的对象,可以调用父类中的方法
class Animal:
def __init__(self,name):
self.name = name
def bark(self):
print(f'{self.name}在叫')
class Dog(Animal):#子类
def bark(self):
super().bark() #父类的方法
print(f"{self.name}在汪汪叫") #在父类的方法上进行扩展
dog = Dog('哈士奇')
dog.bark()
14.8.3 多继承中的重写
多继承的重写,根据广度优先搜索进行执行
14.9 新式类写法,object类
# 写法一
class A:
pass
# 写法二
class A():
pass
# 写法三,推荐
class A(object):
pass
object是为所有对象提供的基类,提供了一些内置的属性和方法
14.10 多态
14.10.1 多态基础
python中的多态是一个伪多态
一个对象具有多种形态,在不同的使用环境中以不同的形态展示其功能,那么我们就称该对象具有多态特性。
多态发生在具有继承关系的基础之上
不关注对象的类型,关注对象具有的行为
不同的子类对象,调用相同的父类方法,会产生不同的执行结果
# + 同样的+号,不同的对象使用,就会有不同的结果
print(10+10)
print("10"+"10")
class Animal(object):
def walk(self):
print("动物在走路")
class Cat(Animal):
def walk(self):
print("走猫步")
miao = Cat()
miao.walk()
# 输出结果
# 走猫步
class People(Animal):
def walk(self):
print("两条腿走路")
people = People()
people.walk()
# 输出结果
# 两条腿走路
14.10.2 多态性
一种调用方式,不同的执行结果
指具有不同功能的函数可以使用相同的函数名,把对象作为参数传入
class A(object): #父类
def speak(self):
print("群众的眼睛是雪亮的")
class C1(A): #子类1
def speak(self):
print("年轻人不讲武德")
class C2(A): #子类2
def speak(self):
print("心疼giegie")
def test(obj): #定义一个函数,把对象作为参数传入
obj.speak()
c1 = C1()
# c1.speak()
test(c1)
# 输出结果
# 年轻人不讲武德
c2 = C2()
# c2.speak()
test(c2)
# 输出结果
# 心疼giegie
14.11 类方法和静态方法
实例方法:方法内部访问实例属性,方法内部可以通过类名.类属性来访问类属性
静态方法:方法内部,不允许访问实例属性,类属性可以通过类名调用。但是不推荐这么使用
类方法:方法内部只允许访问类属性,通过cls.类属性访问
14.11.1 静态方法
使用@staticmethod符号进行定义
class 类名:
@staticmethod #定义为静态方法
def 方法名(形参):
方法体
# 调用格式
类名.方法名(参数)
对象名.方法名(参数)
class Dog(object):
@staticmethod #静态方法:类中的函数。可以用类名调用类属性,但是不推荐。
def run():
print("狗狗在跑步")
Dog.run()
14.11.2 类方法
使用@classmethod符号进行定义
针对类对象定义的方法
class 类名
@classmethod #类方法
def 方法名(cls): #出现cls参数
方法体
class Cat(object):
name = '布偶' #类属性
@classmethod #类方法
def sleep(cls): #这里的cls是指Cat这个类
print(cls)
print("猫猫在睡觉")
print(Cat)
Cat.sleep()
# 输出结果
# <class '__main__.Cat'>
# <class '__main__.Cat'>
# 猫猫在睡觉
14.12 魔法方法
魔法方法有很多,这里只举4个例子
__doc__ #类的描述信息
__module__ #表示当前操作的对象在哪个模块
__class__ # 表示当前操作的对象的类是什么
__str__ #对象的描述信息
14.12.1 __doc__方法
class A:
'''
这是类的描述信息
'''
# 必须要使用'''来表示
pass
print(A.__doc__)
# 这是类的描述信息
14.12.2 __str__方法
# 如果类中定义了__str__方法,那么在打印实例化对象,默认输出该方法的返回值
# str方法必须返回一个字符串
class A:
def __str__(self):
return "类似于java的toString方法"
a = A()
print(a)
14.12.3 __module__ 、__class__ 方法
pytest.py
class C:
def funa(self):
print("这是C类的实例方法")
main.py
import pytest
pt = pytest.C()
print(pt)
pt.funa()
print(pt.__module__) #输出结果: pytest 所在模块
print(pt.__class__) #<class 'pytest.C'>
14.12.4 __eq__
判断两个类是否相等,返回bool值