Python基础 - 9.面向对象提高

面向对象提高

    • 面向对象思维
    • 继承
    • 在这里插入图片描述 类与类之间的关系
    • 在这里插入图片描述 多继承
    • 钻石继承
    • 多态
    • 类属性和实例属性
    • 类方法和实例方法
    • 静态方法
    • __new__
    • 单例
    • 练习
    • 项目实战
    • 本章项目源码

面向对象思维

OOA:(Object Oriented Analysis)面向对象分析。系统分析师。UML中的用例图

OOD:(Object Oriented Design)面向对象设计。系统架构师。UML中的类图、序列图

OOP:(Object Oriented Programming) 面向对象编程。程序猿。UML中的流程图

面向对象中有6大原则,23个设计模式

开闭原则:对修改关闭;对扩展开放(对于函数,使用装饰器来扩展。对于类,使用继承来扩展)

里氏替换原则:一个功能如果父类适用,那么子类也适用;反之则未必(子类适用的功能,父类不一定适用)

如果一个类作用于某个父类,则它一定可以作用于对应的子类;反之未必(如果一个类作用于子类,则它未必能作用于父类)
Python基础 - 9.面向对象提高_第1张图片

如图:法海抓妖,蛇妖是妖的子类,所以法海碰到蛇妖一定要抓

反之,许仙喜欢白蛇,白蛇是蛇妖的子类,但是许仙不一定喜欢蛇妖

根据这个原则:如果设计两个类之间的依赖、通信关系,应该尽量往父类发展。

继承

Python基础 - 9.面向对象提高_第2张图片
Python中实现继承的方式:

class 子类名(父类名):
  子类的具体实现

父类中所有非私有的属性和方法,在子类中可以访问

子类可以访问父类的方法

# 父类:动物
class Animal:
    def eat(self):
        print('Animal eat')

# 子类:狗
class Dog(Animal):
    pass

# 
dog1 = Dog()
dog1.eat()

在这里插入图片描述

子类可以重写父类的方法

# 父类:动物
class Animal:
    def eat(self):
        print('Animal eat')

# 子类:狗
class Dog(Animal):
    def eat(self):
        print('Dog eat')

# 
dog1 = Dog()
dog1.eat()

子类还可以主动调用父类的方法

# 父类:动物
class Animal:
    def eat(self):
        print('Animal eat')

# 子类:狗
class Dog(Animal):
    def eat(self):
        super().eat() # 调用父类的方法
        print('Dog eat')

# 
dog1 = Dog()
dog1.eat()

注意:super()是在类里面调用,不能在类外面调用。如果这样写,会报错:

dog2 = Dog()
dog2.super().eat()

在这里插入图片描述

Python中的规定,如果一个类没有明确指定继承的父类,默认继承object类。

class Animal:
    def eat(self):
        print('Animal eat')

等同于

class Animal(object):
    def eat(self):
        print('Animal eat')

前面一种写法(不继承任何父类)称为经典类(马上会过时,不建议使用)

后面一种写法(继承任意一个类,比如object)称为新式类。

以后,都建议写成新式类。

如果在子类中,想要调用父类的__init__或__del__方法。通过:

  • 父类名.init(参数)

  • 父类名.del()

# 父类:动物
class Animal(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'name:' + self.name
    def __del__(self):
        print('Animal del')

# 子类:狗
class Dog(Animal):
    def __init__(self, name):
        # 尝试调用父类的__init__方法
        Animal.__init__(self,name)
    def __del__(self):
        # 尝试调用父类的__del__方法
        Animal.__del__(self)
        
# 
dog1 = Dog('旺财')
print(dog1)

如果父类中有私有属性和私有的方法,子类不能访问。

# 父类:动物
class Animal(object):
    def __init__(self, name):
        self.name = name
        self.__age = 0
    def __info(self):
        print(self.__age)
# 子类:狗
class Dog(Animal):
    def __init__(self, name):
        # 尝试调用父类的__init__方法
        Animal.__init__(self,name)
    def info(self):
        print(self.__age)
        self.__info()

dog1 = Dog('旺财')
dog1.info()

在这里插入图片描述

Python基础 - 9.面向对象提高_第3张图片
类与类之间的关系

1 继承。子类继承父类。如果两个类之间可以用“是一个”(is a)来描述,可以使用继承关系:比如:

狗是一个动物

class Animal(object):
    pass

class Dog(Animal):
    pass

2 包含。大类包含小类。如果两个类之间可以用“有一个”(has a)来描述,可以使用包含关系。比如:

狗有一条腿

class Dog(Animal):
    def __init__(self):
        self.leg = Leg()

class Leg(object):
    pass

3 通信。如果两个类之间可以通过动词连接起来,可以使用通信关系,比如:

人养狗、狗咬人

class Dog(Animal):
    def __init__(self):
        self.leg = Leg()
    # 狗咬人
    def bid(self, man):
        print(self.name + '咬' + man.get_name())

class Man(Animal):
    def __init__(self):
        self.leg = Leg()
    # 人养狗
    def feed(self, dog):
        print(self.name + '养' + dog.get_name())

Python基础 - 9.面向对象提高_第4张图片
多继承

一个子类可以继承多个父类

C++、Python是有多继承。Java是没有多继承的

骡子是驴和马的杂交。就可以认为是骡子是驴和马的子类。

多继承的格式:

class  子类(父类1, 父类2, 父类3):
  方法中的代码

比如:

class Base1(object):
    def test1(self):
        print('---Base1---')


class Base2(object):
    def test2(self):
        print('---Base2---')

# Sub是Base1和Base2的子类
class Sub(Base1, Base2):
    pass


sub = Sub()
sub.test1()
sub.test2()

钻石继承

所谓的钻石继承,就是指如下的继承方式:
Python基础 - 9.面向对象提高_第5张图片
编写如下的代码:

class Animal(object):
    # 初始化
    def __init__(self):
        print('Animal init')


class Horse(Animal):
    # 初始化
    def __init__(self):
        Animal.__init__(self) #调用父类的__init__方法
        print('Horse init')

class Lv(Animal):
    # 初始化
    def __init__(self):
        Animal.__init__(self) #调用父类的__init__方法
        print('Lv init')
        
class Luo(Horse, Lv):
    # 初始化
    def __init__(self):
        Horse.__init__(self)
        Lv.__init__(self)
        print('Luo init')
        
print('--------------------------')
luo = Luo()

运行的结果:

在这里插入图片描述

会发现Animal init调用了两次。囧么办?

C++:使用虚拟继承。

Java:不允许使用多继承

Pyton:super

class Animal(object):
    # 初始化
    def __init__(self):
        print('Animal init')


class Horse(Animal):
    # 初始化
    def __init__(self):
        #Animal.__init__(self) #调用父类的__init__方法
        super(Horse, self).__init__()
        print('Horse init')

class Lv(Animal):
    # 初始化
    def __init__(self):
        #Animal.__init__(self) #调用父类的__init__方法
        super(Lv, self).__init__()
        print('Lv init')
        
class Luo(Horse, Lv):
    # 初始化
    def __init__(self):
        #Horse.__init__(self)
        #Lv.__init__(self)
        super(Luo, self).__init__()
        print('Luo init')
        
luo = Luo()

运行的结果

在这里插入图片描述

多态

不同的事物,对同一个操作所作出的不同响应

外壳医生、屠夫、演员。Cut…

与其他编程语言(Java)比较起来,Python中的多态的概念,大大的弱化。

class Animal(object):
    def eat(self):
        print('Animal eat')

class Dog(Animal):
    def eat(self):
        print('Dog eat')


class Man(Animal):
    def eat(self):
        print('Man eat')


class Woman(Animal):
    def eat(self):
        print('Woman eat')


def do_eat(obj):
    obj.eat()


dog1 = Dog()
man1 = Man()
woman1 = Woman()
do_eat(dog1)
do_eat(man1)
do_eat(woman1)

isinstance(obj, Cls)判断某个对象obj是否某个类Cls(或它的子类)的实例

如果是则返回True 如果不是则返回False

def do_eat(obj):
    if(isinstance(obj, Animal)):
        obj.eat()

类属性和实例属性

类属性是指一个类中的所有对象都共用的属性。又称为静态属性

实例属性是指类的某个对象独有的属性,又称为非静态属性

比如:每条狗都有4条腿

比如:你们家多少人?4个:我、爸、妈、狗

如果要描述实例属性,使用“对象名. 属性名”去描述(dog1.name、self.name)

如果要描述类属性,使用“类名.属性名”去描述。

class Dog(object):
    # 类属性
    _count = 0

    def __init__(self,name):
        self._name = name
        Dog._count += 1

    def __str__(self):
        return self._name + "说:目前总共有" + str(Dog._count) + "只狗"

dog1 = Dog('旺财')
dog2 = Dog('小白')
dog3 = Dog('雪梨')
print(dog3)
print(dog2)
print(dog1)

注意:类属性也可以使用“对象名.属性名”去访问,但一般不建议。

思考:下面哪个是不能正常访问的?

类名.类属性 类名.实例属性 对象名.类属性 对象名.实例属性

类方法和实例方法

如果要实现类方法,需要:

  • 1 把实例方法的第1个参数从self改为cls,表示当前类

  • 2 在类方法前添加@classmethod

  • 3 外部调用类方法的时候,使用“类名.方法名()”

class Dog(object):
    # 类属性
    _count = 0

    def __init__(self,name):
        self._name = name
        Dog._count += 1

    def __str__(self):
        return self._name + "说:目前总共有" + str(Dog._count) + "只狗"

    # 增加狗的数量 类方法
    @classmethod # add_one = classmethod(add_one)
    def add_one(cls):
        cls._count += 1
        
dog1 = Dog('旺财')
dog2 = Dog('小白')
print(dog2)

Dog.add_one()
print(dog2)

Python中一切皆对象。类也是一个对象。比如前面的:狗类是一个对象,动物类也是一个对象。这些对象一般称为“类对象”(Class对象),“类对象”所属的类称为“类类”(Class类)。就是,在系统中会有如下的定义:

class  Class(object):
   …..

当我们写

class Dog(object):
   ….

实际上是写了

Dog = Class()

注意一点:我们之前调用的__init__的写法是:

class Horse(Animal):
    # 初始化
    def __init__(self):
        Animal.__init__(self) #调用父类的__init__方法
        print('Horse init')

因为,其实__init__方法是在类中定义的类方法。
Python基础 - 9.面向对象提高_第6张图片

静态方法

有些独立的代码,可能和类的属性、方法并没有什么关系。

但是和类本身又有关系。这些代码封装到哪里比较合适?

  • 1 封装到独立的函数中。与面向对象的思维本身有所冲突

  • 2 封装到类的方法中,但是self、cls参数感觉好像没有关系

  • 3 封装到类的独立的静态方法中

class Dog(object):
    # 类属性
    _count = 0

    def __init__(self,name):
        self._name = name
        Dog._count += 1

    # 增加狗的数量 类方法
    @classmethod # add_one = classmethod(add_one)
    def add_one(cls):
        cls._count += 1
        
    # 对象方法
    def bark2(self):
        print('旺旺')
    
    # 类方法
    @classmethod
    def bark3(cls):
        print('旺旺')
    
    # 静态方法
    @staticmethod
    def bark4():
        print('旺旺')
        
# 独立的函数
def bark1():
    print('旺旺')
        
bark1()
#bark()
dog1 = Dog('旺财')
dog1.bark2()
Dog.bark3()
Dog.bark4()

上面的bark1()函数、bark2()对象方法、bark3()类方法、bark4()静态方法,都可以实现类似的功能。

new

之前接触过的前后双下划线的方法:

  • _init_(初始化)

  • _str_(描述对象)

  • _del_(删除销毁对象)

class Dog(object):

    def __init__(self):
        print('init')
        
    def __str__(self):
        return 'str'

    def __del__(self):
        print('del')

    def __new__(cls):
        print('new')

dog1 = Dog() # __init__
print(dog1) # __str__
del dog1 # __del__
print('------')

运行程序,会发现:

在这里插入图片描述

证明只有__new__()被执行,init()没有被执行。

如何才能让__init__()被执行?需要让__new__返回一个值

def __new__(cls):
        print('new')
        return object.__new__(cls) # 让父类的__new__()方法去创建该对象
        # __new__的返回值会传递到__init__()的第一个参数中

_new_()的操作实际上是创建对象。init()的操作是初始化。init()的第一个参数是self,描述对象本身,说明当__init__被调用的时候,对象已经创建好了,现在知道,对象是在__new__()里面去创建的。

当用户调用Dog()创建一个实例的时候,到底内部做了什么事情?

  • 1 先调用__new__方法来创建并返回一个对象,用一个变量保存该对象,然后把该对象传递到__init__方法中作为第一个参数。以便初始化

  • 2 调用__init__方法。初始化该对象

  • 3 返回该对象

实现原理大致如下:

def unknown():
    obj = __new__(Dog)
    __init__(obj)
    return obj

单例

面向对象有23种设计模式。创建型模式(5种)、结构型模型(7种)、行为型模型(11种)

单例模式属于创建型模型。

在某个系统中,一个类的对象只有一个。

class Sun(object):
    pass

sun1 = Sun()
sun2 = Sun()
print(id(sun1))
print(id(sun2))
print(id(sun1) == id(sun2))

假设有一个类Sun(太阳)。太阳既然是唯一的,这里打印出来的结果应该是True

思路:创建类的时刻只有一次

如果是第一次创建对象,直接创建即可

如果不是第一次创建对象,返回之前创建过的那个

此时,就需要重写__new__方法。

class Sun(object):
    _instance = None
    # 重写__new__方法
    def __new__(cls):
        if cls._instance == None:
            # 如果该类还没创建对象 就调用父类的__new__方法创建该对象
            cls._instance = object.__new__(cls)
            return cls._instance
        else:
            # 如果该类已经创建了对象 则返回该对象本身
            return cls._instance

假如给类设置一个属性(姓名)

class Sun(object):
    _instance = None
    # 重写__new__方法
    def __new__(cls, name):
        if cls._instance == None:
            # 如果该类还没创建对象 就调用父类的__new__方法创建该对象
            cls._instance = object.__new__(cls)
            return cls._instance
        else:
            # 如果该类已经创建了对象 则返回该对象本身
            return cls._instance
            
    def __init__(self, name):
        self._name = name

    def __str__(self):
        return self._name
        

sun1 = Sun('喜洋洋')
print(sun1)
print(id(sun1))
sun2 = Sun('懒羊羊')
print(sun2)
print(id(sun2))

运行的结果

在这里插入图片描述
会发现名字被改了,如何才能让名字不能修改?

class Sun(object):
    _instance = None
    _init_flag = False
    # 重写__new__方法
    def __new__(cls, name):
        if cls._instance == None:
            # 如果该类还没创建对象 就调用父类的__new__方法创建该对象
            cls._instance = object.__new__(cls)
            return cls._instance
        else:
            # 如果该类已经创建了对象 则返回该对象本身
            return cls._instance
            
    def __init__(self, name):
        if Sun._init_flag == False:
            self._name = name
            Sun._init_flag = True

    def __str__(self):
        return self._name

练习

1 根据如图所示,类之间的关系,写出对应的代码
Python基础 - 9.面向对象提高_第7张图片

项目实战

项目需求(二)

通用类开发

  1. 目录:app/common

  2. 状态类Status, 文件status.py ,
    包括成功和失败两种状态,每个状态包含状态码和状态信息两个属性。

    1. 如成功状态调用方法Status.SUCCESS.status Status.SUCCESS.message

    2. 失败状态调用方法:Status.ERROR.status Status.ERROR.message

    3. SUCCESS 属性 status 值 200000,message 值 Success

    4. ERROR 属性 status 值 200400,message 值 Error

  3. 响应结果类 Result,文件 result.py,包括成功和失败两种返回结果。
     {

“status”: status,

“message”: message,

“data”: data

}

  1. 静态方法 success() 和 error(), status和message默认来自Status

本章项目源码

URL:https://gitee.com/yuanbaonet/master_python/tree/baoai_python_v9/

对应版本:baoai_python_v9

对应文件:sample/python/p9.py

你可能感兴趣的:(python)