python快速学习系列(6):面向对象编程(OOP)

一、面向对象编程:

1.比设计模式更重要的是设计原则:
1)面向对象设计的目标:
·可扩展:新特性很容易添加到现有系统中,基本不影响系统原有功能
·可修改:当修改某一部分代码时,不会影响到其他不相关的部分
·可替代:用具有相同接口的代码去替换系统中某一部分代码时,系统不受影响
2)面向对象设计的SOLID原则:
·单一职责原则:设计出来的每一个类,只有一个引起这个类变化的原因
·开闭原则:对扩展开放,对修改封闭
·替换原则:父类能用的功能,换成子类一样可以用
·接口隔离原则:接口设计要按需供给(类似微服务器设计)
·依赖倒置原则:抽象不依赖于细节,细节应该依赖于抽象(针对接口编程)
3)AI(场景)+python(语言)+OOP(编程模式)
·AI:业务导向不明显,需求变动频率较低,实现和复现频率高
·python:虽然是一门面向对象语言,但与传统的面向对象并不相同
·OOP:使用python时,并不需要深入学习OOP或OOD那些理论

二、OOP in python

1.类的创建、数据绑定

class Model:
    __name = 'DNN'
    def __init__(self,name):
        self.__name = name
    def print_name(self):
        print(self.__name)
    @classmethod        #装饰器,表示下面这个函数是类所有的
    def print_cls_name(cls):#这里的参数名一般使用cls(同之前的函数都使用self一样)
        print(cls.__name)


class CNNModel(Model):
    __name = 'CNN'
    def __init__(self,name,layer_num):
        Model.__init__(self,name)#子类的初始化函数必须显式的包含父类的初始化函数
        self.__layer_num = layer_num

    def print_layer_num(self):
        print(self.__layer_num)

def main():
    cnnmodel = Model('CNN')   #实例化
    rnnmodel = Model('RNN')
    print(cnnmodel)  #实例化完的对象可以直接print
    print(rnnmodel.__name)  #用.访问成员,报错,因为是私有成员,不能类外访问
    cnnmodel.print_name()

要点:
·类名一般大写,实例化出来的对象,名称一般小写
·类在被定义时,创建了一个局部作用域;类实例化出对象后,创建了新的局部作用域,额就是对象自己的作用域
·类名加上()生成对象,说明类被定义后便可以调用
·本质上讲,类本身是一个对象
·在类定义中,self只带实例化出来的对象
·cnnmodel.print_name()等价于Model.print_name(cnnmodel)
·通过双下划线开头,可以将数据属性私有化,对于方法一样适用(数据在外部不能访问,但是可以通过函数访问)
·python中的私有化是假的,本质上做了一次名称替换,因此实际中也有为了方便调试而使用单下划线的情况,而私有化也全凭自觉了
·使用了@classmethod后的方法虽然可以继承,但是方法里面的cls参数绑定了父类,即使在子类中调用了类方法,但通过cls引用的属性依旧是父类的类属性

三、 Pythonic OOP

1.关于双下划线
1)怎么读?
_name:single underscore name
__name:double underscore name
name:dunder name(dunder来源:double首字母d,underscore取under)
init():dunder init method (function)
2)双下划綫开头和结尾的变量或方法叫什么?
·类别:special;magic;dunder
·实体:attribute,method
3)如何认识python的special method
·special method:method with special name(dunder)
·Why use it?A class can implement certain operation that are invoked by special syntax
·original intention of design:operation overloading

2.Pythonic OOP with Special Method and Attribute
·每一个class在定义的时候如果没有继承,都会隐式继承object这个superclass(超类)
·每一个自定义的class在Python中都是一个type object

class X:
    pass 
class Y(x):
    pass
def main():
    x = X()
    y = Y()
    print(x.__class__.__name__)  #X
    print(y.__class__.__name__)  #Y
    print(X.__class__.__name__)  #type
    print(Y.__class__.__name__)  #type
    print(x.__class__.__base__.__name__)  #object
    print(y.__class__.__base__.__name__)  #X
    print(X.__class__.__base__.__name__)  #object
    print(Y.__class__.__base__.__name__)  #object

3.Attribute Access and Peoperties
Attribute相关的操作一般有:
·create
·read
·Update
·Delete

1)level 1 basic access(default access)
class X:
pass
if name == ‘main’:
X.a = ‘a’
print(X.a)
X.a = ‘aa’
print(X.aa)
del X.a
上述例子说明即使在类中没有绑定数据,外部依然可以绑定(太逆天了)
但是如果实例化的对象中没有这个Attribute,访问时会报错

2)level 2:Property(长得像Attribute的Method) ——推荐使用
Property的设计初衷:
·代码复用
·延迟计算
·更加规范的对象属性访问管理

场景:我要减肥,需要监控BMI指标,但是只能测量体重,每天更新体重,隔几天看一次BMI指数

class X:
    def __init__(self,w,h):
        self.w = w
        self.h = h
        self.BMI = w / h ** 2
def main():
    x = X(175,1.83)
    print(x.BMI)    #22.3954
    x.w = 74
    x.w = 73
    x.w = 72
    print(x.BMI)    #22.3954

注意:上述虽然把w传入了,但是__init__()函数只在初始化的时候执行一次,后续不再执行

改进一:

class X:
    def __init__(self,w,h):
        self.__w = w
        self.__h = h
        self.BMI = w / h ** 2
    def update_w(self,w):
        self.__w = w
        self._update_bmi()
    def _update_bmi(self):
        self.BMI = self.__w / self.__h ** 2

def main():
    x = X(75,1.83)
    print(x.BMI)  #22.3954
    x.update_w(74)
    x.update_w(73)
    x.update_w(72)
    print(x.BMI)  #21.4996

缺点:
BMI属性依旧可以被外部访问和修改
无论BMI属性是否被访问,每次w更新均更新BMI,造成一定的计算资源浪费

改进二:

class X:
    def __init__(self,w,h):
        self.w = w
        self.h = h
    def get_bmi(self):
        return self.w / self.h ** 2
def main():
    x = X(75,1.83)
    print(x.get_bmi())
    x.w = 74
    x.w = 73
    x.w = 72
    print(x.get_bmi())

缺点:
·上述代码依然需要更改main函数中的语句
·当w变化频率小于BMI访问次数时,需要进行大量的重复计算

改进三:

class X:
    def __init__(self,w,h):
        self.__w = w
        self.__h = h
        self.__bmi = w / h ** 2
    def get_w(self):
        return self.__w
    def set_w(self,value):
        if value <= 0 :
            raise ValueError('weight below 0 is not possible')
        self.__w = value
        self.__bmi = self.__w / self.__h ** 2
    def get_bmi(self):
        return self.__bmi
    w = property(get_w,set_w)  #***括号里面的参数必须是函数名
    BMI = property(get_bmi)    #***非常重要
def main():
    x = X(75,1.83)
    print(x.BMI)
    x.w = 74
    x.w = 73
    x.w = 72
    print(x.BMI)

分析:
·通过Property对象显式的控制属性的访问
·仅在w被更改的时候更新BMI,充分避免了重复计算
·很容易的增加了异常处理,对更新属性进行预检验
·完美复用原始调用代码,在调用方不知情的情况完成功能添加
实际使用中并不会这样写,有更加优美的写法

改进四:

class X:
    def __init__(self,w,h):
        self.__w = w
        self.__h = h
        self.__bmi = w / h ** 2
    @property   #只读
    def w(self):
        return self.__w
    @w.setter   #可写
    def w(self,value):
        if value <= 0 :
            raise ValueError('weight below 0 is not possible')
        self.__w = value
        self.__bmi = self.__w / self.__h ** 23
    @property   #只读
    def BMI(self):
        return self.__bmi

def main():
    x = X(75,1.83)
    print(x.BMI)
    x.w = 74
    x.w = 73
    x.w = 72
    print(x.BMI)

关于property的用法:
·property(fget=None,fset=None,fdel=None,doc=None)
·使用@property默认实现了可读
·被@property装饰过的method可以通过@method.setter继续装饰单输入参数方法实现可写

3.Cross-Cutting and Duck Typing
·单继承vs多态
-单继承保证了纵向的复用和一致性
-多态保证了跨类型的复用和一致性

·传统OOP vs 鸭子类型
-传统OOP基于类别进行设计,从类别出发逐步扩展
-鸭子类型进考虑功能,从需要满足的功能出发进行设计

·传统的OOP的多态 vs 鸭子类型的多态
-传统OOP中的多态大多基于共同的基类进行设计
-python中的鸭子类型无需考虑继承关系,实现了某个通用的接口就可以完成多态设计(special method)

Cross-Cutting:基于鸭子类型的视角看Decorator与special method
·通过一个类实现一个个的special method,你就让这个类越来越像python的Built-in class
·实现special method是从语言衔接层面为你的class赋能
·实现decorator是从通用的函数功能层面为你的class赋能
·通过multi-Inheritance,利用MixIn的理念,你可以为你的class批量化的赋能

猜你喜欢

转载自blog.csdn.net/shanlepu6038/article/details/84671434