14--Python|14--Python 面向对象进阶

@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. 经典类和新式类的不同就在于对方法的搜索顺序不同,经典类是深度优先;而新式类是广度优先。
14--Python|14--Python 面向对象进阶
文章图片
继承类型
14--Python|14--Python 面向对象进阶
文章图片
继承顺序 单继承与多继承
  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 = salarydef work (self): print('普通员工正在写代码,工资是:', self.salary)class Customer: def __init__ (self, favorite, address): self.favorite = favorite self.address = addressdef 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,ABCMetaclass 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.numberd = 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 = discountb = 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())

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

    推荐阅读