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'>
posted @ 2020-07-21 17:02  Raigor  阅读(245)  评论(0编辑  收藏  举报