• Python基础学习笔记(23)继承 类部分属性的补充 方法和函数 利用 pickle 存储对象


    Python基础学习(23)继承 类部分属性的补充 方法和函数 利用 pickle 存储对象

    一、今日大纲

    • 继承
    • 类部分属性的补充
    • 方法和函数
    • 利用 pickle 存储对象

    二、继承的基本实现

    面向对象有三大特性:继承、封装、多态;今天我们主要介绍继承;假如我们要定义一个猫类和一个狗类,他们各自具有下面的绑定方法:

    • 猫:吃、喝、睡、爬树

    • 狗:吃、喝、睡、看家

    class Cat:
        def __init__(self, name):
            self.name = name
    
        def eat(self):
            print(f'{self.name} is eating.')
    
        def drink(self):
            print(f'{self.name} is drinking.')
    
        def sleep(self):
            print(f'{self.name} is sleeping.')
    
        def climb_tree(self):
            print(f'{self.name} is climbing the tree.')
    
    
    class Dog:
        def __init__(self, name):
            self.name = name
    
        def eat(self):
            print(f'{self.name} is eating.')
    
        def drink(self):
            print(f'{self.name} is drinking.')
    
        def sleep(self):
            print(f'{self.name} is sleeping.')
    
        def guard_house(self):
            print(f'{self.name} is guarding the house.')
    
    
    xiaobai = Cat('xiaobai')
    xiaobai.eat()
    xiaobai.drink()
    xiaobai.climb_tree()
    xiaohei = Dog('xiaohei')
    xiaohei.eat()
    xiaohei.drink()
    xiaohei.guard_house()
    

    根据上面的代码我们可以看到,由于猫狗两个类由于大部分绑定方法比较相似,所以存在大量的重复代码,所以我们引入一个概念:继承;它主要用于解决代码的重复问题,下面是继承的基本实现方式:

    class A:
        pass
    
    
    class B(A):
        pass
        
    # B继承A,A是父类,B是子类
    # A是父类 基类 超类
    # B是子类 派生类
    

    而根据继承的思想,我们可以新定义一个父类:Pet,让CatDog都继承此类,从而达到解决代码重复问题的作用:

    class Pet:
        def __init__(self, name):
            self.name = name
    
        def eat(self):
            print(f'{self.name} is eating.')
    
        def drink(self):
            print(f'{self.name} is drinking.')
    
        def sleep(self):
            print(f'{self.name} is sleeping.')
    
    
    class Dog(Pet):
        def guard_house(self):
            print(f'{self.name} is guarding the house.')
    
    
    class Cat(Pet):
        def climb_tree(self):
            print(f'{self.name} is climbing the tree.')
    
    
    xiaobai = Cat('xiaobai')
    xiaobai.eat()
    xiaohei = Dog('xiaohei')
    xiaohei.eat()
    

    对象寻找方法的基本逻辑:在定义对象时,先开辟空间,空间里存放一个类指针指向类;当调用某些方法时,对象会首先在自己的命名空间中寻找,如果没找到会借助类指针去类的命名空间中寻找,如果还未找到,则会借助类指针去父类中进行寻找。

    如果我们这时想给猫和狗类定义一个新的方法:吃猫粮/狗粮

    class Pet:
        def __init__(self, name):
            self.name = name
    
        def eat(self):
            print(f'{self.name} is eating.')
    
        def drink(self):
            print(f'{self.name} is drinking.')
    
        def sleep(self):
            print(f'{self.name} is sleeping.')
            
    
    class Dog(Pet):
        def guard_house(self):
            print(f'{self.name} is guarding the house.')
    
        def eat(self):
            print(f'{self.name}吃狗粮。')
    
    
    class Cat(Pet):
        def climb_tree(self):
            print(f'{self.name} is climbing the tree.')
    
        def eat(self):
            print(f'{self.name}吃猫粮。')
    
    
    # 这时在重新调用小白和小黑的吃方法
    xiaobai = Cat('xiaobai')
    xiaobai.eat()  # xiaobai吃猫粮。
    xiaohei = Dog('xiaohei')
    xiaohei.eat()  # xiaohei吃狗粮。
    

    这时我们发现,代码又出现了重复,我们是否可以利用传参实现把吃猫粮和吃狗粮合并称为一个方法呢?我们新定义两个属性来更直观地观察实现方法:智力值、血量;猫吃了猫粮智力值会增加,狗吃了狗粮生命值会增加。

    class Pet:
        def __init__(self, name, food):
            self.name = name
            self.food = food
            self.blood = 100
            self.wise = 100
    
        def eat(self):
            print(f'{self.name} is eating {self.food}.')
    
        def drink(self):
            print(f'{self.name} is drinking.')
    
        def sleep(self):
            print(f'{self.name} is sleeping.')
    
    
    class Dog(Pet):
        def eat(self):
            self.blood += 100
            Pet.eat(self)  # 手动调用父类的方法,由于父类方法没有实例化,必须吧self传入
    
        def guard_house(self):
            print(f'{self.name} is guarding the house.')
    
    
    class Cat(Pet):
        def eat(self):
            self.wise += 100
            Pet.eat(self)  # # 手动调用父类的方法,由于父类方法没有实例化,必须吧self传入
    
        def climb_tree(self):
            print(f'{self.name} is climbing the tree.')
    
    
    xiaobai = Cat('xiaobai', '猫粮')
    xiaobai.eat()  # xiaobai吃猫粮。
    xiaohei = Dog('xiaohei', '狗粮')
    xiaohei.eat()  # xiaohei吃狗粮。
    print(xiaobai.wise, xiaohei.blood)  # 200 200
    

    在子类和父类方法存在重名的时候:子类对象调用方法会按照子类 -> 父类的顺序检查,先检查到的优先调用。而如果想同时调用子类和父类就要在子类中手动调用父类的方法,即添加Classname.func(self)

    # 思考题1:
    class Father:
        def __init__(self):
            self.func()
    
        def func(self):
            print('in father')
    
    
    class Son(Father):
        def func(self):
            print('in son')
            Father.func(self)
    
    
    Son()
    
    
    # in son
    # in father
    
    # 思考题2:想给猫和狗定制个性属性
    # 猫有eye_color眼睛的颜色
    # 狗有size大小
    class Pet:
        def __init__(self, name, food):
            self.name = name
            self.food = food
            self.blood = 100
            self.wise = 100
    
        def eat(self):
            print(f'{self.name} is eating {self.food}.')
    
        def drink(self):
            print(f'{self.name} is drinking.')
    
        def sleep(self):
            print(f'{self.name} is sleeping.')
    
    
    class Dog(Pet):
        def __init__(self, name, food, size):
            Pet.__init__(self, name, food)
            self.size = size
    
        def eat(self):
            self.blood += 100
            Pet.eat(self)  # 手动调用父类的方法,由于父类方法没有实例化,必须吧self传入
    
        def guard_house(self):
            print(f'{self.name} is guarding the house.')
    
    
    class Cat(Pet):
        def __init__(self, name, food, eye_color):
            Pet.__init__(self, name, food)  # 调用了父类的初始化,去完成一些通用属性的初始化
            self.eye_color = eye_color  # 派生属性
    
        def eat(self):
            self.blood += 100
            Pet.eat(self)
    
        def climb_tree(self):
            print(f'{self.name} is climbing the tree.')
    
    
    xiaobai = Cat('xiaobai', 'maoliang', 'blue')
    xiaohei = Dog('xiaohei', 'gouliang', 'big')
    print(xiaobai.__dict__)
    print(xiaohei.__dict__)
    
    
    # {'name': 'xiaobai', 'food': 'maoliang', 'blood': 100, 'wise': 100, 'eye_color': 'blue'}
    # {'name': 'xiaohei', 'food': 'gouliang', 'blood': 100, 'wise': 100, 'size': 'big'}
    

    继承主要分为单继承和多继承:单继承只继承一个类;而多继承可以继承多个类;多继承的调用逻辑如下:

    # 单继承: 只继承一个类
    # 调子类的:子类自己有的时候
    # 调父类的:子类自己没有的时候
    # 调子类和父类的:子类父类都有,在子类中调用父类的
    class A:
        def func(self): print('in A')
    
    
    class B(A): pass
    
    
    class C(B): pass
    
    
    class D(C): pass
    
    
    d = D()
    d.func()  # in A
    # 多继承:继承多个类
    # 按照继承顺序,先继承的先寻找,找到就停止
    # 有一些语言不支持多继承 java
    # python语言的特点:可以在面向对象中支持多继承
    
    class A:
        def func(self): print('in A.')
    class B:
        def func(self): print('in B.')
    
    class C(A, B): pass
    C().func()  # 继承第一个传入的类
    

    三、类部分属性的补充

    1. object

      所有在 Python3x 中的类,都是继承 object 类的。一般按照引用的逻辑来讲,我们定义一个类中未定义__init__的对象,发现并不会报错,这就是因为对象的类中其实是继承了object类的,所以我们以后在定义类的时候最好还是把object写出来,虽然实际上可以省略,但是这是一个约定的写法,即:

      class A: pass  # 等价于 class A(object): pass
      
      
      class B: pass
      
      
      class C(A, B): pass
      
      
      print(C.__bases__)  # (<class '__main__.A'>, <class '__main__.B'>)
      print(B.__bases__)  # (<class 'object'>,)
      
    2. 类属性的补充

      # 类属性的补充
      # class_name.__name__  # 类的名字
      # class_name.__doc__ # 类的文档字符串
      # class_name.__base__  # 类的第一个父类
      # class_name.__bases__  # 类的所有父类构成的元组
      # class_name.__dict__  # 类的字典属性
      # class_name.__module__  # 类定义所在的模块
      # object_name.__class__  # 实例对应的类
      
      class A: pass
      
      
      class B:
          """
          这个类主要是用来卖萌
          """
          pass
      
      
      class C(A, B): pass
      
      
      print(A.__base__)  # <class 'object'>
      print(B.__base__)  # <class 'object'>
      print(C.__bases__)  # (<class '__main__.A'>, <class '__main__.B'>)
      print(C.__class__)  # <class 'type'>
      print(C.__module__)  # __main__
      print(B.__doc__)  # 这个类主要是用来卖萌
      

    四、函数和方法

    # 绑定方法和普通的函数
    from types import FunctionType, MethodType
    
    
    # FunctionType: 函数
    # MethodType: 方法 是对一个对象进行的操作
    class A:
        def func(self):
            print('in func')
    
    
    print(A.func)  # 函数 <function A.func at 0x0000019D33A1BD90>
    a = A()
    print(a.func)  # 方法 <bound method A.func of <__main__.A object at 0x0000019D33A1F780>>
    print(isinstance(a.func, MethodType))  # True
    print(isinstance(a.func, FunctionType))  # False
    print(isinstance(A.func, MethodType))  # False
    print(isinstance(A.func, FunctionType))  # True
    
    # isinstance 和 type
    # isinstance和type大部分时间用法是比较类似的
    # 但是isinstance可以鉴别对象的类的父类,而type是不可以的
    a = 1
    b = 'abc'
    print(isinstance(a, int))  # True
    print(isinstance(a, float))  # False
    print(isinstance(b, str))  # True
    print(type(a) is int)  # True
    print(type(b) is str)  # True
    
    
    class Cat:
        pass
    
    
    xiaobai = Cat()
    print(type(xiaobai) is Cat)  # True
    print(isinstance(xiaobai, Cat))  # True
    
    
    class Animal: pass
    
    
    class Cat(Animal): pass
    
    
    xiaobai = Cat()
    print(type(xiaobai) is Cat)  # True
    print(type(xiaobai) is Animal)  # False
    print(isinstance(xiaobai, Cat))  # True
    print(isinstance(xiaobai, Animal))  # True
    

    五、利用 pickle 存储对象

    class Course:
        def __init__(self, name, period, price):
            self.name = name
            self.period = period
            self.price = price
    
    python = Course('python', '6 month', 21800)
    linux = Course('python', '5 month', 19800)
    go = Course('python', '4 month', 12800)
    import pickle
    with open('pickle_file', 'ab') as f:
        pickle.dump(python, f)
        pickle.dump(linux, f)
        pickle.dump(go, f)
    with open('pickle_file', 'rb') as f:
        while True:
            try:
                ret = pickle.load(f)
                print(ret.__dict__)
            except EOFError:
                break
    # {'name': 'python', 'period': '6 month', 'price': 21800}
    # {'name': 'python', 'period': '5 month', 'price': 19800}
    # {'name': 'python', 'period': '4 month', 'price': 12800}
    
    
    # 在游戏中保存对象
    # json不可以连续load连续dump
    # json不可以dump和load对象(模块中没有类的声明,读取过程中会出现报错)
    # AttributeError: Can't get attribute 'Course' on <module '__main__' from 'D:/Python/Python_Project/day24/03 pickle用法.py'>
    
  • 相关阅读:
    突然连不上VSS等服务器的原因之一
    “复制到剪贴板”的解决方案
    设置SQLServer2008开启远程连接(转)
    对List(IList)集合作求和,最大(小)值操作
    .net实现简单语音朗读(TTS)功能
    17点成就你的好代码
    2011年上半年五大臭名昭著的数据库泄密事件
    Spring Mobile 1.1.0.M2 发布
    Rainbows! 4.4.3 发布,修复 EventMachine 问题
    情绪不是反应,而是决定
  • 原文地址:https://www.cnblogs.com/raygor/p/13356087.html
Copyright © 2020-2023  润新知