接口:从协议到抽象基类

 

  封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)python

 

(1)封装(Encapsulation):类包含了数据和方法,将数据和方法放在一个类中就构成了封装。mysql

(2)继承(Inheritance):Java是单继承的(这点和C++有区别),意味着一个类只能继承于一个类,被继承的类叫父类(或者叫基类,base class),继承的类叫子类。Java中的继承使用关键字extends。可是,一个类能够实现多个接口,多个接口之间用逗号进行分割。实现接口使用关键字implements。sql

(3)多态(Polymorphism):多态最核心的思想就是,父类的引用能够指向子类的对象,或者接口类型的引用能够指向实现该接口的类的实例。数据库

 

“ 开闭 ”原则:编程

  对扩展开放:容许新增子类;对修改封闭:不须要修改依赖该类型的函数。 flask

  把不一样的子类对象都看成父类来看,能够屏蔽不一样子类对象之间的差别,写出通用的代码,作出通用的编程,以适应需求的不断变化。oracle

 

鸭子类型:app

  不关注对象的类型,而关注对象的行为(方法)。它的行为是鸭子的行为,那么能够认为它是鸭子。ide

  调用不一样的子类将会产生不一样的行为,而无须明确知道这个子类其实是什么,这是多态的重要应用场景函数

  鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

  这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试。

  “鸭子测试”能够这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。”

class Duck():
    def walk(self):
        print('I walk like a duck')
    def swim(self):
        print('i swim like a duck')

class Person():
    def walk(self):
      print('this one walk like a duck') 
    def swim(self):
      print('this man swim like a duck')

  Person类拥有跟Duck类同样的方法,当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。

  咱们传入Person类也同样能够运行,函数并不会检查对象的类型是否是Duck,只要他拥有walk()和swim()方法,就能够正确的被调用。

  在Java中的多态,子类须要继承与同一个父类而且覆盖掉父类的一些方法才能定义多态,这一点与python不一样。

  在python中咱们不须要继承同一个父类只须要实现同一个方法就好了。

 

接口:

  接口里有什么方法,继承类就必须有什么方法,接口中不能有任何功能代码。

  面向对象中的继承有两种用途:

    1)能够经过继承作到代码重用,并完成扩展;

    2)接口继承。定义一个接口类 Interface,接口类中定义了一些接口(函数,但这些函数都没有具体的实现),子类继承接口类,而且实现接口中的功能

class Operate_database():    # 接口类
    def query(self, sql):
        raise NotImplementedError

    def update(self, sql):
        raise NotImplementedError

class Operate_mysql(Operate_database):
    def query(self, sql):
        print('query mysql : %s' % sql)

    def update(self, sql):
        print('query mysql : %s' % sql)

class Operate_pg(Operate_database):
    def query(self, sql):
        print('query postgresql : %s' % sql)

    def update(self, sql):
        print('update postgresql : %s' % sql)

def query_data(operate_obj, sql):
    operate_obj.query(sql)

def update_data(operate_obj, sql):
    operate_obj.update(sql)

query_data(Operate_mysql(), 'select ...')    # query mysql : select ...
update_data(Operate_pg(), 'update...')    # update postgresql : update...
接口类

  若子类继承了Operate_database 接口类,可是没有实现其中的某一个方法的功能,调用时就会报错

  子类覆盖父类中的方法时,要注意方法名须要与父类中的方法名相同,且方法的参数个数与参数名也要相同

  这里更好的方式是经过 abc模块 来实现接口

from abc import ABCMeta,abstractmethod

class Operate_database(metaclass=ABCMeta):    # 接口类
    @abstractmethod
    def query(self, sql):
        pass

    @abstractmethod
    def update(self, sql):
        pass

class Operate_oracle(Operate_database):
    # 没有实现 query 方法
    def update(self, sql):
        print('update oracle : %s' % sql)

def query_data(operate_obj, sql):
    operate_obj.query(sql)

oracle = Operate_oracle()        # 因为没有实现接口中的全部方法,在这一步就会报错
query_data(oracle, 'select ...')
abc模块

▲ 在其余的语言里,好比Java,继承类没有重写接口方法是会报错的,而在python里不会,就是由于python没这个类型,因此只是在咱们编程过程的一个规定,以I开头的类视为接口

 

抽象类:

  抽象类和接口类同样是一种规范,规定子类应该具有的功能。

  在Python中,抽象类和接口类没有明确的界限。

  如果类中全部的方法都没有实现,则认为这是一个接口类,如果有部分方法实现,则认为这是一个抽象类。

  抽象类和接口类都仅用于被继承,不能被实例化

 

  声明抽象基类最简单的方式是继承 abc.ABC 或其余抽象基类。

  然而,abc.ABC 是 Python 3.4 新增的类,所以若是你使用的是旧版Python,那么没法继承现有的抽象基类。

  此时,必须在 class 语句中使用 metaclass= 关键字,把值设为 abc.ABCMeta(不是 abc.ABC)

class Tombola(metaclass=abc.ABCMeta):    # Python 3
 # ...

class Tombola(object):                   # Python 2
    __metaclass__ = abc.ABCMeta
 # ...  

  抽象类,能够说是类和接口的混合体,既能够定义常规方法,也能够约束子类的方法(抽象方法)

from abc import ABCMeta,abstractmethod

class Operate_database(metaclass=ABCMeta):    # 抽象类

    log_path = '/tmp/db.log'

    def connect(self):
        print('connect db ...')

    @abstractmethod
    def query(self, sql):
        pass

    @abstractmethod
    def update(self, sql):
        pass
抽象类

 

协议:

  (1)协议是非正式的接口,是一组方法,Python没有interface 关键字,定义接口只是一我的为约定。

  (2)Python中存在多种协议,用于实现鸭子类型(对象的类型可有可无,只要实现了特定的协议(一组方法)便可)。

  (3)须要成为相对应的鸭子类型,那就实现相关的协议,即相关的__method__。例如实现序列协议(__len__和getitem),这个类就表现得像序列。

  (4)能够根据具体场景实现一个具体协议的一部分。例如,为了支持迭代,只需实现__getitem__,不须要实现__len__。

  (5)在Python文档中,若是看到“文件类对象“(表现得像文件的对象),一般说的就是协议,这个对象就是鸭子类型。这是一种简短的说法,意思是:“行为基本与文件一致,实现了部分文件接口,知足上下文相关需求的东西。”

import collections

Card = collections.namedtuple('Card', ['rank','suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        
    def __len__(self):
        return len(self._cars)

    def __getitem__(self, position):
        return self._cards[position]

# Python的序列协议只须要__len__和__getitem__两个方法。
序列协议

 

DI(依赖注入):

  在系统运行中,动态的向某个对象提供它所须要的其余对象。这一点是经过DI(Dependency Injection,依赖注入)来实现的。

  好比对象A须要操做数据库,之前咱们老是要在A中本身编写代码来得到一个Connection对象;

  A中须要一个Connection,至于这个Connection怎么构造,什么时候构造,A不须要知道。

  在系统运行时,会在适当的时候制造一个Connection,而后像打针同样,注射到A当中,这样就完成了对各个对象之间关系的控制。(以参数的形式传入

  A须要依赖 Connection才能正常运行,而这个Connection是经过注入到A中的,依赖注入的名字就这么来的。

from flask import Flask

app = Flask("example")

class DAO:
    def __init__(self):
        self.data = []

dao = DAO()

@app.route("/")
def m():
    return dao.data

if __name__ == "__main__":
    app.run()
没有使用DI
from flask import Flask

class DAO:
    def __init__(self):
        self.data = []

def App(dao):

    app = Flask("example")

    @app.route("/")
    def m():
        return dao.data

    return app

if __name__ == "__main__":
    app = App(DAO())
    app.run()
使用DI
相关文章
相关标签/搜索