Python学习 day8-2021.3.6(面对对象基础·中)


Python入门课程系列:

  • Python学习 day1:认识Python
  • Python学习 day2:判断语句与循环控制
  • Python学习 day3:高级数据类型
  • Python学习 day4:函数基础
  • Python学习 day5:函数
  • Python学习 day6:内置函数
  • Python学习 day7:面向对象基础·上

重点:类的继承、父类的调用、静态方法
难点:继承与重写、静态方法

1. 析构方法

当一个对象被删除或者销毁时 ,python解释器也会默认调用一个方法,这个方法为 _ del _()方法,也称为析构方法。(当在某个作用域下面,没有被使用【引用】的情况下,解释器会自动调用此函数来释放内存空间)
析构方法的定义:

class Animal(object):
    def __init__(self,name):
        self.name=name
        print('_init_方法被调用')

        #析构方法,当对象被销毁时python解析器会自动调用

    def __del__(self):
        #主要的应用就是来操作对象的释放 ,一旦释放完毕,对象便不能再使用。
        print('_del_方法被调用')
        print('%s对象被销毁'%self.name)

dog=Animal('旺财')
#_init_方法被调用
#_del_方法被调用
#旺财对象被销毁

class Animal(object):
    def __init__(self,name):
        self.name=name
        print('_init_方法被调用')
    def __del__(self):
        print('_del_方法被调用')
        print('%s对象被销毁'%self.name)

dog=Animal('旺财')
input('程序等待中...')
# _init_方法被调用
# 程序等待中...  #没有运行完,不会被销毁

当对象被删除的时候也会自动调用 _ del _ 方法,如下代码中我们利用del手动删除dog对象

class Animal(object):
    def __init__(self,name):
        self.name=name
        print('_init_方法被调用')
    def __del__(self):
        print('_del_方法被调用')
        print('%s对象被销毁'%self.name)

dog=Animal('旺财')
#将对象删除
del dog
input('程序等待中...')
# _init_方法被调用
# _del_方法被调用
# 旺财对象被销毁
# 程序等待中...  
#从上面输出结果中可以看出 ,当调用del dog这条语句时,_ del _ 方法也被执行了

析构方法总结

  1. 当整个程序脚本执行完毕后会自动调用 _ del _ 方法
  2. 当对象被 手动销毁时也会调用 _ del _ 方法
  3. 析构函数一般用于资源回收,利用 _ del _ 方法销毁对象回收内存资源等等

这个方法在对象被销毁(作为垃圾被收集)前被调用,但鉴于无法知道准确的调用时间,建议尽可能不要使用__ del __。

2. 单继承

在python当中展现oop的三大特征:封装、继承、多态

  • 封装:指的是把内容封装到某个地方,便于后面的使用。
    它需要:1. 把内容封装到某个地方;2. 从另外一个地方去调用被封装的内容;
    对于封装来说,其实就是使用初始化构造方法将内容封装到对象中,然后通过对象直接或者用self来获取被封装的内容。
  • 继承:和现实生活中的继承是一样的,也就是子可以继承父的内容【属性和行为】(父亲有的儿子都有,但是儿子有的父亲不一定有)
  • 多态:定义时的类型和运行时的类型不一样,此时就成为多态
#对于面向对象的继承来说,其实就是将多个共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法
class Animal(object):
    def eat(self):
        print('乖乖吃')
        pass
    def drink(self):
        print('乖乖喝')
        pass

class cat(Animal): #继承动物父类 此时cat就是子类
    def mew(self):
        print("小猫喵喵叫")

class dog(Animal): #继承动物父类 此时dog就是子类
    def woof(self):
        print("小狗汪汪叫")

c1=cat()
c1.eat()  #继承自父类
#乖乖吃
c1.mew() #子类独有
#小猫喵喵叫

3. 多继承

class shenxian:
    def fly(self):
        print('神仙可以飞')
    pass
class Monkey:
    def chitao(self):
        print('猴子喜欢吃桃')
    pass
class SunWuKong(shenxian,Monkey): #孙悟空既是神仙又是猴子
    pass
swk=SunWuKong()
swk.fly()
swk.chitao()
#神仙可以飞
#猴子喜欢吃桃

#问题是当多个父类中存在相同方法时,应该调用哪个类?
class D(object):
    def eat(self):
        print('D.eat')
        pass
    pass
class C(D):
    def eat(self):
        print('C.eat')
        pass
    pass
class B(D):
    pass
class A(B,C):
    pass
a=A()
a.eat()
#C.eat
#在执行eat方法时,查找的顺序是:首先到A里面去查找
#如果A中没有,则继续去第一个父类B类中查找,如果没有找到就去第二个父类C类中查找。
#如果C类中没有,则去D类中查找,如果还没有找到就会报错
#A-B-C-D
print(A.__mro__) #❗️魔术方法查询执行顺序
#(, , , , )

4. 继承的传承

类的传递过程中,我们把父类又称为基类,子类又称为派生类,父类的属性和方法可以一级一级的传递到子类(理论上多少级都可以,但实际操作中不建议超过三级)

class GrandFather:
    def eat(self):
        print('开心的吃')
        pass
    pass
class Father(GrandFather):
    pass
class Son(Father):
    pass
son=Son()
son.eat()  #此方法从GrandFather继承而来
#开心的吃

5. 重写父类方法

所谓重写,就是子类中有一个和父亲相同名字的方法 ,在子类中的方法会覆盖掉父亲中同名的方法。
重写的原因:父类的方法已经不能满足子类的需要,那么子类就可以重写父类或者完善父类

class Father:
    def drinking(self):
        print('喝二锅头')
        pass
    pass
class Son(Father):
    # 与父类drinking同名方法,就是重写父类方法
    def drinking(self):
        print('喝茅台')
    pass
#重写父类方法后,子类调用父类的方法将调用的是子类的方法
son=Son()
son.drinking()
# 喝茅台

class Father:
    def __init__(self,SkinColor,HairColor):
        self.SkinColor = SkinColor
        self.HairColor=HairColor
    def drinking(self):
        print('喝二锅头')
        pass
    pass
class Son(Father):
    # 与父类drinking同名方法,就是重写父类方法
    def __init__(self): #属于重写父类的方法,这里如果没有重写,直接继承父类init但没有传入SkinColor和HairColor参数就会报错
        #重写的init没有SkinColor和HairColor属性
        pass
    def drinking(self): #属于重写父类的方法
        print('喝茅台')
    pass
#重写父类方法后,子类调用父类的方法将调用的是子类的方法
son=Son()
son.SkinColor() #重写后父类的属性子类不再具有
#AttributeError: 'Son' object has no attribute 'SkinColor'

6. 调用父类方法

class Father:
    def __init__(self,SkinColor,HairColor):
        self.SkinColor = SkinColor
        self.HairColor=HairColor
    def drinking(self):
        print('喝二锅头')
        pass
    pass
class Son(Father):
    def __init__(self,SkinColor,HairColor,height,weight):
        Father. __init__(self,SkinColor,HairColor) #调用方法1:如果不调用父类方法 ,定义了子类属性之后,子类会整个覆盖父类的init函数
        super().__init__(SkinColor,HairColor) #调用方法2:super是自动找到父类,进而调用方法。假设继承了多个父类,那么会按照顺序逐个寻找,然后再调用
        #这个方法的好处在于。子类可以增加父类不需要的属性。当再初始化这个子类对象的时候,不但可以继承父类的属性,还可以继承子类的属性。
        #拓展其他属性
        self.height=height
        self.weight=weight
        pass
    def __str__(self):
        return '儿子的头发颜色是{},他的身高是{}cm,体重是{}kg'.format(self.HairColor,self.height,self.weight)
    def drinking(self): #属于重写父类的方法
        super().drinking() #super()自动调用父类方法
        print('喝茅台')
    pass
son=Son('yellow','black',180,75)
print(son.SkinColor)
# yellow
print(son)
#儿子的头发颜色是black,他的身高是180cm,体重是75kg
son.drinking()
#喝二锅头 (来自super调用父类的结果)
#喝茅台  (来自子类结果)

7. 多态

多态:顾名思义就是多种状态、形态。定义时的类型和运行时的类型不一样,此时就成为多态。通俗来讲,就是同一种行为,对于不同的子类【对象】有不同的行为表现。
实现多态必须的两个前提:1. 必须存在一种继承关系(必须发生在父类和子类之间);2. 子类重写父类的方法
Python不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,Python崇尚“鸭子类型”,利用python伪代码实现Java和C#的多态

鸭子类型(duck typing)
在程序设计中,鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。“鸭子测试”可以这样表述:“当看到一只鸟走起路来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。

class Animal:
    '''
    父类【基类】
    '''
    def say_who(self):
        print('我是一个动物...')
        pass
    pass
class Duck(Animal):
    '''
    鸭子类【子类/派生类】
    '''
    pass
duck1=Duck()
duck1.say_who()
#我是一个动物...

class Duck(Animal):
    def say_who(self):
        print('我是一只漂亮的鸭子')  # 在子类中覆盖掉了父类的方法
    pass
class Dog(Animal):
    def say_who(self):
        print('我是一只机灵的小狗')  # 在子类中对父类的方法有自己的实现
    pass
class Cat(Animal):
    def say_who(self):
        print('我是一只可爱的小猫')  
    pass

#重新构建新的父类
class People:
    def say_who(self):
        print('我是人类')
    pass
class student(People):
    def say_who(self):
        print('我是一年级的九九')
    pass

#多态的优越性:统一调用(不管出处)
def commonInvoke(obj):
    '''
    统一调用方法
    :param obj: 对象的实例
    :return:
    '''
    obj.say_who() #不关注对象类型,只要这个方法存在(不管调用自Animal父类还是People父类),且参数正确就可以调用(鸭子类型的体现)
    pass

listObj=[Duck(),Dog(),Cat(),student()] #来自不同的类,但都具备同一个方法
for item in listObj:
    '''
    循环调用函数 
    '''
    commonInvoke(item)
#我是一只漂亮的鸭子
#我是一只机灵的小狗
#我是一只可爱的小猫
#我是一年级的九九

多态的作用:增加程序的灵活性和拓展性

8. 类属性和实例属性

类属性 :就是类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象可以访问类属性
实例属性:实例对象所拥有的属性,只能通过实例对象访问

class Student:
    name='feifei' #属于类属性 是Student这个类对象多拥有的
    def __init__(self,age):
        self.age=age #实例属性
        pass
    pass
zlf=Student(8)
print(zlf.name)  #通过实例对象访问类属性
# feifei
print(zlf.age)  #通过实例对象访问实例属性
# 8
print(Student.name)  #通过类对象访问类属性
# feifei
print(Student.age)  #通过类对象访问实例属性 会报错
# AttributeError: type object 'Student' has no attribute 'age'

类属性的修改与访问
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方法修改的是实例属性,不会影响类属性,并且之后如果通过实例对象去引用该名称的属性,实例对象会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。

class Student:
    name='feifei' #属于类属性 是Student这个类对象多拥有的
    def __init__(self,age):
        self.age=age #实例属性
        pass
    pass
zlf=Student(8)
zlf.name='lingfei'
print(zlf.name)  #通过实例对象修改类属性,只修改了这一个,类属性本身没有变,在被别的对象调用的时候还是修改前的
huahua=Student(5)
print(huahua.name)
# lingfei
# feifei

Student.name='hui' #通过类对象修改类属性
huahua=Student(5)
print(huahua.name)
#hui

9. 类方法和静态方法

类方法:类对象所拥有的方法,需要用装饰器 @classmethod来标识其为类方法。对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数,类方法可以通过类对象和实例对象调用。

#创建类方法 
class Person:
    country='China' #类属性
    def __init__(self,name):
        self.name=name
    #用类方法,用装饰器classmethod装饰
    @classmethod
    def get_country(cls):
        print(cls.country) #访问类属性

Person.get_country() #通过类对象可以去引用类方法
# China
p=Person('geng')  #通过实例对象也可以去引用类方法
p.get_country()
# China

#在类方法中修改类属性
class Person:
    country='China'
    def __init__(self,name):
        self.name=name
    @classmethod
    def get_country(cls):
        print(cls.country)
        pass
    @classmethod
    def change_country(cls):
        cls.country='Iceland' #在类方法中修改类属性
Person.change_country()
# Iceland

静态方法:类对象所拥有的方法,需要@staticmethod来表示静态方法,静态方法不需要任何参数
一般情况下,把一个方法声明为静态方法意味着这个方法跟实例属性和静态属性都没有什么关系,也就没有必要通过实例对象去访问

class Person:
    country='China'
    def __init__(self,name):
        self.name=name
    #静态方法,用装饰器staticmethod修饰
    @staticmethod
    def get_country(): #静态方法可以不传任何参数
        print(Person.country)
        pass
people=Person('chen')
result=Person.get_country()
#China

静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互。在静态方法中,不会涉及到类中方法和属性的操作。
数据资源可以得到有效的充分利用

#练习 返回当前的系统时间
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())
        pass
    pass
print(TimeTest.showTime())
#23:57:15 (当前时间)
t=TimeTest(2,10,15)
print(t.showTime()) #没有必要通过这种方式访问静态方法

⚠️ 类方法、实例方法、静态方法,类属性和实例属性

  • 类属性和实例属性的区别:
    在Python中一切皆对象,类是一个特殊的对象即类对象,描述类的属性称为类属性,它属于类。类属性在内存中只有一份,所有实例对象公用,在 _ init _ 外部定义。
    实例属性:用来描述类创建出来的实例对象,需要通过对象来访问,在各自对象的内存中都保存一份,在 _ init _ 方法内部定义。

  • 实例方法、类方法、静态方法的区别:
    这三种方法都是保存在类的内存中,调用者不同。
    实例方法由对象调用,至少一个self参数,self代表对象的引用。
    类方法由类调用,至少一个cls参数,并且需要装饰器@classmethod修饰
    静态方法由类调用,不需要参数,需要装饰器@staticmethod修饰

复习知识点

  • 析构方法:当一个对象被删除或被销毁时,python解释器也会 默认使用一个方法,这个方法为del()方法

  • 单继承:子类在继承的时候,在定义类时,如果小括号()中为父类的名字,父类的属性和方法会被继承给子类

  • 多继承:子类可以继承多个父类,在小括号()中用逗号隔开

  • 继承的传承:子类可以继承父类的父类的方法

  • 重写和调用父类方法:重写父类方法后,调用的是子类的方法。在重写的方法里还可以调用父类的方法。

  • 多态:定义时的类型和运行时的类型不一样,此时就成为多态

  • 类属性和实例属性:
    类属性:就是类对象拥有的属性
    实例属性:就是实例对象拥有的属性

  • 类方法和静态方法:
    用@classmethod来表示类方法
    用@staticmethod来表示静态方法

你可能感兴趣的:(Python学习 day8-2021.3.6(面对对象基础·中))