重点:类的继承、父类的调用 、静态方法
难点:继承与重写、静态方法
析构方法
析构方法的概述
当一个对象被删除或者被销毁时,python解释器也会默认调用一个方法,这个方法为__del__()方法,也称为析构方法
class Animal():
def __init__(self,name):
self.name=name
print('__init__方法被调用')
#析构方法,当对象被销毁时,python解析器会自动调用
def __del__(self):
print('__del__方法被调用')
print('%s 对象被销毁'%self.name)
dog=Animal("旺财")
input('程序等待中.....')
程序执行结束自动调用__del__方法
--------------输出结果---------------
__init__方法被调用
程序等待中.....111
__del__方法被调用
旺财 对象被销毁
对象被删除时也会自动调用__del__方法,如下代码中我们利用del手动删除dog对象
class Animal():
def __init__(self,name):
self.name=name
print('__init__方法被调用')
#析构方法,当对象被销毁时,python解析器会自动调用
def __del__(self):
#主要用于对象的释放,一旦释放,对象不再使用
print('__del__方法被调用')
print('%s 对象被销毁'%self.name)
dog=Animal("旺财")
del dog #删除对象..........
input('程序等待中.....')
--------------输出结果---------------
__init__方法被调用
__del__方法被调用
旺财 对象被销毁
程序等待中.....111
析构方法总结
1、当整个程序脚本执行完毕后会自动调用__del__方法
2、当对像被手动销毁时也会自动调用 del 方法
3、析构函数一般用于资源回收,利用__del__方法销毁对象回收内存等资源
单继承
继承的概念
在现实生活中,继承一般指的是子女继承父辈的财产。在面向对象中同样有继承,例如:
猫的方法:喵喵叫、吃、喝
狗的方法:汪汪叫、吃、喝
如果给猫和狗都创建一个类,那么猫和狗的所有方法都要写,如:
class cat:
def call_Mao(self):
print('喵喵喵')
def eat(self):
print('鱼')
def drink(self):
print('water')
class dog:
def call_Wang(self):
print('汪汪汪')
def eat(self):
print('骨头')
def drink(self):
print('water')
上面的代码中我们可以发现,吃,喝方法时一样的,但是写了两遍。
如果用继承的思想,我们可以这样:猫和狗都是动物(动物有吃,喝的方法)
继承方法
print('-------------继承写法------------')
class Animal():
# 父类的实现
def eat(self):
print('爱吃东西')
pass
def drink(self):
print('爱喝水')
pass
class cat(Animal): #继承了Animal的方法
#子类的独有实现
def mmj(self):
print('喵喵喵')
class dog(Animal): #继承了Animal的方法
# 子类的独有实现
def wwj(self):
print('汪汪汪')
dog1=dog()
dog1.eat()
dog1.drink()
cat1=cat()
cat1.eat()
cat1.drink()
-------------输出结果------------
爱吃东西
爱喝水
爱吃东西
爱喝水
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法
class 类名(父类):
#子类就可以继承父类中公共的属性和方法
pass
总结:在定义子类时要继承父类,只需要类名后面的小括号()中写上父类的名字,那么父类的属性、方法,会被继承给子类。
多继承
多继承概念
子类可以继承一个父类,那是否可以继承两个父类或多个呢?答案是肯定的,这就是python的多继承
C类可以继承A、B两个类, 可以将A,B中的方法继承过来,C拥有A,B的方法和属性
class A():
def a(self):
print('A类的方法输出')
class B():
def b(self):
print('B类的方法输出')
#C继承AB
class C(A,B):
pass
c=C()
c.a()
c.b()
-------------输出结果------------
A类的方法输出
B类的方法输出
同名方法
如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?
class Base():
def test(self):
print('------Base test------')
class Base1():
def test(self):
print('------Base1 test------')
class A(Base):
def test(self):
print('--------A test--------')
class B(Base1):
def test(self):
print('--------B test--------')
class C(A,B):
pass
c=C()
c.test()
print(C.__mro__) # 可以查看C类的对象搜索方法是的先后顺序,也就是继承的顺序
-------------输出结果------------
--------A test--------
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.Base'>, <class '__main__.B'>, <class '__main__.Base1'>, <class 'object'>)
同名方法:按先后顺序继承
mro 方法解析顺序
在前面的代码中,我们使用了 mro 方法
功能:查询执行顺序
描述:方法的执行顺序可以用mro查看。前面代码查找顺序为 C->A->Base->B>Base1, 一旦找到,则寻找过程立即中断,便不会再继续找了
继承的传递
在现实中遗产继承,爷爷的遗产可以被父亲继承,儿子可以继承父亲的。这样看来是不是儿子也是有继承到爷爷的遗产。在面向对象中的继承呢?子类是否能继承父类的父类的方法?
看看下面的继承关系,Son类继承Father类,Father类并没有提供eat方法,但是父类又继承了Grandfather类。Son的对象调用eat方法可以正常执行,运行结果得出,Son类也继承了Granderfather类的方法。这就是继承的传递性
class GrandFather():
def eat(self):
print('吃饭')
class Father(GrandFather):
def drink(self):
print('喝水')
class Son(Father):
pass
Lg=Son()
Lg.eat()
Lg.drink()
-------------输出结果------------
吃饭
喝水
父辈继承了爷爷辈,儿子继承了父辈,所以儿子可以调用爷爷辈的方法
总结:类的传递过程中,我们把父类又称为基类,子类又称为派生类,父类的属性和方法可以一级一级的传递到子类
重写父类方法
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法 伪代码示例:
class GrandFather():
def eat(self):
print('吃饭')
class Father(GrandFather):
def eat(self):
print('经常吃烧烤') #在父类覆盖方法
def drink(self):
print('喝水')
class Son(Father):
pass
Lg=Son()
Lg.eat() #是从GrandFather继承过来的 只是在Father中北覆盖了
Lg.drink()
-------------输出结果------------
经常吃烧烤
喝水
调用父类方法
class Dog():
def __init__(self,name,colour):
self.name=name
self.colour=colour
def bark(self):
print('汪汪叫')
class KeJiDog(Dog):
def __init__(self,name,colour): #属于重写init方法
#Dog.__init__(self,name,colour) # 手动调用
#调用父类的方法,执行完毕就可以具备name,colour这两个实例属性了
#扩展其他属性
super().__init__(name,colour) #super是自动找父类,进而调用方法
self.height=90
self.weight=20
pass
def __str__(self):
return '{}的颜色是,他的身高是{},体重是{}'.format(self.name,self.height,self.weight)
def bark(self): #属于重写bark方法
super().bark() #调用父类方法
print('叫的跟神一样')
pass
kj=KeJiDog('科技犬','红色')
kj.bark()
print(kj)
-------------输出结果------------
汪汪叫
叫的跟神一样
科技犬的颜色是,他的身高是90,体重是20
多态
多态概念
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,Python崇尚“鸭子类型”,利用python伪代码实现Java和C#的多态
要想实现多态,必须要有2个前提要遵守:
1.继承关系,多态必须发生在父类和子类之间
1.重写 子类要重写父类的方法
class Animal:
'''
父类【基类】
'''
def Say(self):
print('我是一个动物.....')
pass
class Duck(Animal):
'''
鸭子类 【子类】 派生类
'''
def Say(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只漂亮的鸭子')
pass
class Dog(Animal):
'''
小狗类 【子类】 派生类
'''
def Say(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只可爱的小狗')
pass
class Cat(Animal):
'''
小狗类 【子类】 派生类
'''
def Say(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只可爱的猫咪')
pass
duck=Duck()
duck.Say()
dog=Dog()
dog.Say()
cat=Cat()
cat.Say()
多态案例
Python 天生就是支持多态,因为他是弱类型语言,不需要指定类型。 Python“鸭子类型”。
class Animal:
'''
父类【基类】
'''
def Say(self):
print('我是一个动物.....')
pass
class Duck(Animal):
'''
鸭子类 【子类】 派生类
'''
def Say(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只漂亮的鸭子')
pass
class Dog(Animal):
'''
小狗类 【子类】 派生类
'''
def Say(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只可爱的小狗')
pass
class Cat(Animal):
'''
小狗类 【子类】 派生类
'''
def Say(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只可爱的猫咪')
pass
class People(): #虽然不同类 但同样有Say方法 就能被统一调用
def Say(self):
print('我是人类')
pass
class Student(People): #虽然不同类 但同样有Say方法 就能被统一调用
def Say(self):
print('我是学生小明')
pass
# 多态的只需增加以下代码 就可以执行
def CI(obj):
obj.Say() #都具备Say的方法 就可以统一调用
ListObj=[Duck(),Dog(),Cat(),People(),Student()]
for item in ListObj:
CI(item)
-------------输出结果------------
我是一只漂亮的鸭子
我是一只可爱的小狗
我是一只可爱的猫咪
我是人类
我是学生小明
多态的作用 只要具备这个方法 不管类型都能调用,能简化代码
类属性和实例属性
类属性的概念
类属性:就是类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象可以访问
实例属性的概念
实例属性:实例对象所拥有的属性,只能通过实例对象访问
print('------通过实例对象去访问类属性-------')
class Student:
name='李明辉' #属于类属性 就是student类对象所拥有的
def __init__(self,age):
self.age=age
pass
pass
lm=Student(18)
print(lm.name) #通过实例对象去访问类属性
lm.name='刘德华' #通过实例对象,对类属性进行修改,可以吗?不可以
print(lm.age)
print(lm.name) #输出‘刘德华’只是临时的修改实例属性,不会修改类的
xh=Student(18)
print(xh.age)
print(xh.name) #输出‘李明辉’类属性没有被改变
print('------通过类对象student去访问name-------')
print(Student.name) #本身name就是类属性所有
#print(Student.age) #会报错,实例属性age不能被类属性访问
Student.name='李易峰' #通过类对象才能去修改
xh=Student(18)
print(xh.age)
print(xh.name) #输出‘李易峰’类属性改变啦
-------------输出结果------------
------通过实例对象去访问类属性-------
李明辉
18
刘德华
18
李明辉
------通过类对象student去访问name-------
李明辉
18
李易峰
从上面的的案例可以得出:
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性
类方法和静态方法
类方法
类对象所拥有的方法,需要用装饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数,类方法可以通过类对象,实例对象调用
class People:
country='china'
#类方法 需要用classmethod 来进行修饰
@classmethod
def get_country(cls):
return cls.country #访问类属性
pass
@classmethod
def change_country(cls,data):
cls.country=data #修改类属性的值 在类方法中
pass
print(People.get_country())
p=People()
print('实例对象访问%s'%p.get_country())
People.change_country('英国')
print(People.get_country())
-------------输出结果------------
china
实例对象访问china
英国
类方法主要可以对类属性进行访问、修改
静态方法
类对象所拥有的方法,需要用@staticmethod来表示静态方法,静态方法不需要任何参数,如:
为什么要使用静态方法?
由于静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互,也就是说,在静态方法中,不会涉及到类中方法和属性的操作
数据资源能够得到有效的利用
class People:
country='china'
#类方法 需要用classmethod 来进行修饰
@classmethod
def get_country(cls):
return cls.country #访问类属性
pass
@classmethod
def change_country(cls,data):
cls.country=data #修改类属性的值 在类方法中
pass
@staticmethod
def getData():
return People.country #通过类对象去引用
print(People.getData())
p=People()
print(p.getData())
import time
class TimeTest:
def __init__(self,hour,min,second):
self.hour=hour
self.min = min
self.second = second
@staticmethod
def showTime():
return time.strftime('%H:%M:%S',time.localtime())
print(TimeTest.showTime())
t=TimeTest(2,10,15)
print(t)
-------------输出结果------------
china
china
20:20:30
<__main__.TimeTest object at 0x0000023C42C6DCD0>
类方法、实例方法、静态方法对比
1.类方法的第一个参数是类对象cls,通过cls引用的类对象的属性和方法
2.实例方法的第一个参数是实例对象self,通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
3.静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。