美文网首页
14--Python 面向对象进阶

14--Python 面向对象进阶

作者: Roger田翔 | 来源:发表于2019-06-11 20:06 被阅读0次
    @Author : Roger TX (425144880@qq.com)
    @Link : https://github.com/paotong999

    一、封装

    封装是从业务逻辑中抽象对象时,要赋予对象相关数据与操作,将一些数据和操作打包在一起的过程。
    封装对类形成了一种“黑盒”状态,我们不需要知道类内部是什么样的,只要对对象进行操作就可以。
    1.含义

    封装是对全局作用域中其它区域隐藏多余信息的原则。

    2.实例

    如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线 __,在Python中,实例的变量名如果以 __ 开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

    二、继承

    继承概念

    1. 继承实现了代码的重用,相同的代码不需要重复的编写。
    2. 在Python中子类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
    3. 父类的私有属性和方法不会被子类继承

    继承顺序

    1. Python3中如果不指定继承哪个类,默认就会继承Object类,继承了Object类的类就叫做新式类。
    2. Python2中如果不指定继承哪个类,不会默认去继承Object类,没有继承Object类的类就叫做经典类。
    3. 经典类和新式类的不同就在于对方法的搜索顺序不同,经典类是深度优先;而新式类是广度优先。
    继承类型
    继承顺序

    单继承与多继承

    1. 父类中没有的属性在子类中出现叫做派生属性,父类中没有的方法在子类中出现叫做派生方法
    2. 只要是子类的对象调用,子类中有的名字一定用子类的,子类中没有才找父类的,如果父类也没有报错
    3. 如果父类、子类都有则用子类的,如果还想用父类的,单独调用父类的
      -- 父类名.方法名 需要自己传self参数
      -- super().方法名 不需要自己传self
    4. 可以用 Foo.__mro__ 方法查看继承顺序
    class A:
        def __init__(self,a=None):
            self.a=a
        def test(self):
            print("A...test")
    
    class B:
        def __init__(self,b=None):
            self.b=b
        def test(self):
            print("B...test")
    
    class C(B,A):
        def __init__(self,a):
            A.__init__(self,a)
        def t(self):
            A.test(self)    # 调用A的test()
            super().test()    # 这个调用的也是B的test
            print("C....t")
    
    c=C("aa")
    #默认调用的是父类B的test方法,因为在class C(B,A),B在A前面
    c.test()
    c.t()
    

    二、super() 函数

    Python 要求:如果子类重写了父类的构造方法,那么子类的构造方法必须调用父类的构造方法。

    子类的构造方法调用父类的构造方法有两种方式:

    1. 使用未绑定方法,这种方式很容易理解。因为构造方法也是实例方法,当然可以通过这种方式来调用。
    2. 使用 super() 函数调用父类的构造方法。

    调用 super() 的本质就是调用 super 类的构造方法来创建 super 对象

    1. 使用 super() 构造方法最常用的做法就是不传入任何参数,然后通过 super 对象的方法调用父类方法。
    2. 在调用父类的实例方法时,程序会完成第一个参数 self 的自动绑定。
    3. 在调用类方法时,程序会完成第一个参数 cls 的自动绑定。
    class Employee :
        def __init__ (self, salary):
            self.salary = salary
    
        def work (self):
            print('普通员工正在写代码,工资是:', self.salary)
    
    class Customer:
        def __init__ (self, favorite, address):
            self.favorite = favorite
            self.address = address
    
        def info (self):
            print('我是一个顾客,我的爱好是: %s,地址是%s' % (self.favorite, self.address))
    
    class Manager(Employee, Customer):
        # 重写父类的构造方法
        def __init__(self, salary, favorite, address):
            print('--Manager的构造方法--')
            # 通过super()函数调用父类的构造方法
            super().__init__(salary)
            # 与上一行代码的效果相同
            #super(Manager, self).__init__(salary)
            # 使用未绑定方法调用父类的构造方法
            Customer.__init__(self, favorite, address)
    # 创建Manager对象
    m = Manager(25000, 'IT产品', '广州')
    m.work()
    m.info()
    

    三、抽象类&接口类

    @abstractmethod:抽象方法
    含abstractmethod方法的类不能实例化,继承的子类必须实现 @abstractmethod 装饰的方法,未被装饰的可以不重写

    from abc import abstractclassmethod,ABCMeta
    
    class Payment(metaclass=ABCMeta):
        @abstractclassmethod
        def pay(self):
            print('支付ing....')
    
    class Wechatpay(Payment):
        def pay(self):
            print('微信支付ing...')
    

    @ property:方法伪装属性
    方法返回值及属性值,被装饰方法不能有参数,必须实例化后调用,类不能调用
    将一个方法伪装成属性,被修饰的特性方法,内部可以实现处理逻辑,但对外提供统一的调用方式
    这个装饰器还有和其配套的setter、deleter

    class Data:
        def __init__(self):
            self.number = 123
    
        @property
        def operation(self):
            return self.number
    
        @operation.setter
        def operation_set(self, number):
            self.number = number
    
        @operation.deleter
        def operation_del(self):
            del self.number
    
    d = Data()
    print(d.operation)
    d.operation_set = 222
    del d.operation_del
    

    四、多态

    不同的子类对象调用相同的父类方法,产生不同的执行效果,可以增加代码的外部调用灵活度。
    父类变量能够引用子类对象,当子类有重写父类方法,调用的将是子类方法。

    1. 定义一个父类
    2. 定义多个子类,并重写父类的方法
    3. 传递子类对象给调用者,不同子类对象产生不同的执行效果

    五、枚举

    在某些情况下,一个类的对象是有限且固定的,比如季节类,它只有 4 个对象;再比如行星类,目前只有 8 个对象。这种实例有限且固定的类,在 Python 中被称为枚举类。

    程序有两种方式来定义枚举类:

    1. 直接使用 Enum 列出多个枚举值来创建枚举类。
    2. 通过继承 Enum 基类来派生枚举类。

    直接使用 Enum 列出多个枚举值来创建枚举类:

    import enum
    # 定义Season枚举类
    Season = enum.Enum('Season', ('SPRING', 'SUMMER', 'FALL', 'WINTER'))
    
    # 直接访问指定枚举
    print(Season.SPRING)    # Season.SPRING
    # 访问枚举成员的变量名
    print(Season.SPRING.name)    # SPRING
    # 访问枚举成员的值
    print(Season.SPRING.value)    # 1
    # 根据枚举变量名访问枚举对象
    print(Season['SUMMER'])    # Season.SUMMER
    # 根据枚举值访问枚举对象
    print(Season(3))    # Season.FALL
    

    上面程序使用 Enum() 函数(就是 Enum 的构造方法)来创建枚举类,该构造方法的第一个参数是枚举类的类名;第二个参数是一个元组,用于列出所有枚举值。

    Python 还为枚举提供了一个 __members__ 属性,该属性返回一个 dict 字典,字典包含了该枚举的所有枚举实例。程序可通过遍历 __members__ 属性来访问枚举的所有实例

    # 遍历Season枚举的所有成员
    for name, member in Season.__members__.items():
        print(name, '=>', member, ',', member.value)
    

    通过继承 Enum 基类来派生枚举类:

    from enum import Enum, unique, IntEnum 
    
    @unique
    class Orientation(Enum):
        # 为序列值指定value值
        EAST = '东'
        SOUTH = '南'
        WEST = '西'
        NORTH = '北'
        def info(self):
            print('这是一个代表方向【%s】的枚举' % self.value)
    
    # 通过枚举变量名访问枚举
    print(Orientation['WEST'])    # Orientation.WEST
    # 通过枚举值来访问枚举
    print(Orientation('南'))    # Orientation.SOUTH
    # 调用枚举的info()方法
    Orientation.EAST.info()    # 这是一个代表方向【东】的枚举
    

    上面程序通过继承 Enum 派生了 Orientation 枚举类,通过这种方式派生的枚举类既可额外定义方法,如上面的 info() 方法所示,也可为枚举指定 value(value 的值默认是 1、2、3、…)

    1. @unique 装饰器防止value值相同,如果没有这个标签,当有两个值相同时,第二个name相当于第一个name的别名
    2. Enum 允许 value 为非整型,IntEnum 只允许 value 为整型
    3. 枚举类是单例模式,不能实例化

    六、metaclass元类

    如果希望创建某一批类全部具有某种特征,则可通过 metaclass 来实现。使用 metaclass 可以在创建类时动态修改类定义

    为了使用 metaclass 动态修改类定义,程序需要先定义 metaclass, metaclass 应该继承 type 类,并重写 __new__() 方法。

    metaclass,直译为元类,可以理解为类的元数据
    先定义metaclass,就可以创建类,最后创建实例。你可以把类看成是metaclass创建出来的“实例”。

    定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

    # metaclass是类的模板,所以必须从`type`类型派生:
    class ListMetaclass(type):
        def __new__(cls, name, bases, attrs):
            attrs['add'] = lambda self, value: self.append(value)
            return type.__new__(cls, name, bases, attrs)
    

    __new__() 方法接收到的参数依次是:

    • 当前准备创建的类的对象;
    • 类的名字;
    • 类继承的父类集合;
    • 类的方法和属性集合。

    有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:

    class MyList(list, metaclass=ListMetaclass):

    当我们传入关键字参数metaclass时,Python解释器在创建MyList时,要通过 ListMetaclass.__new__() 来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

    现在 MyList 就可以调用add()方法

    L = MyList()
    L.add(1)
    L
    

    下面看一个元类使用的实例

    # 定义ItemMetaClass,继承type
    class ItemMetaClass(type):
        # cls代表动态修改的类
        # name代表动态修改的类名
        # bases代表被动态修改的类的所有父类
        # attr代表被动态修改的类的所有属性、方法组成的字典
        def __new__(cls, name, bases, attrs):
            # 动态为该类添加一个cal_price方法
            attrs['cal_price'] = lambda self: self.price * self.discount
            return type.__new__(cls, name, bases, attrs)
    
    # 定义Book类
    class Book(metaclass=ItemMetaClass):
        __slots__ = ('name', 'price', '_discount')
    
        def __init__(self, name, price):
            self.name = name
            self.price = price
    
        @property
        def discount(self):
            return self._discount
    
        @discount.setter
        def discount(self, discount):
            self._discount = discount
            
    # 定义cellPhone类
    class CellPhone(metaclass=ItemMetaClass):
        __slots__ = ('price', '_discount' )
    
        def __init__(self, price):
            self.price = price
    
        @property
        def discount(self):
            return self._discount
    
        @discount.setter
        def discount(self, discount):
            self._discount = discount
    
    b = Book("Python基础教程", 89)
    b.discount = 0.76
    # 创建Book对象的cal_price()方法
    print(b.cal_price())
    cp = CellPhone(2399)
    cp.discount = 0.85
    # 创建CellPhone对象的cal_price()方法
    print(cp.cal_price())
    

    通过使用 metaclass 可以动态修改程序中的一批类,对它们集中进行某种修改。这个功能在开发一些基础性框架时非常有用,程序可以通过使用 metaclass 为某一批需要具有通用功能的类添加方法。

    相关文章

      网友评论

          本文标题:14--Python 面向对象进阶

          本文链接:https://www.haomeiwen.com/subject/wwoyxctx.html