一 类对象的内置属性

在python中,万物皆为对象。对象是类创建的,且类本身存在于内存中,类本身也是一个对象,一般称之为类对象。类本身存在着一些内置的属性

属性类型描述示例
__dict__ 返回类的类方法、实例方法、静态方法和类属性组成字典
"""类的内置属性"""
class A(object):
pass

class B(object):
pass

class Person(A, B):
"""类的说明文档-人类"""
max_age = 150

def __init__(self, name ,age):
self.name = name
self.age = age

def run(self, step):
print(f"跑{step}步")

@staticmethod
def speak(message):
print(f"说:{message}")

@classmethod
def met(cls):
return f"{cls.max_age}年"

# 返回类的类方法、实例方法、静态方法和类属性组成字典
print("类.__dict__:", Person.__dict__)
#类.__dict__: {'__module__': '__main__', '__doc__': '类的说明文档-人类', 'max_age': 150, '__init__': <function Person.__init__ at 0x107f671f0>, 'run': <function Person.run at 0x107f67a60>, 'speak': <staticmethod object at 0x107f61fd0>, 'met': <classmethod object at 0x107f61fa0>}
__doc__ 类的说明文档
# # 返回类的说明文档
print("类.__doc__:", Person.__doc__) #类.__doc__: 类的说明文档-人类
 
__name__ 类名  
# # 类名
print("类.__name__:", Person.__name__) #类.__name__: Person
__module__ 类定义所在的模块,__main__表示当前主模块
# # 类定义所在的模块,`__main__`表示当前主模块(主程序文件)
print("类.__module__:", Person.__module__) #类.__module__: __main__
 
__bases__ 类的所有父类构成的元组(注意:仅仅是父类,不是祖先类)  
# # 类的所有父类构成元素(注意:仅仅是父类,不是祖先类)
print("类.__bases__:", Person.__bases__) #类.__bases__: (<class '__main__.A'>, <class '__main__.B'>)
__mro__ 类的所有父辈构成的元组(注意:不仅是父类,还包括祖先类)
# # mro获取类的继承顺序
print(Person.__mro__)#(<class '__main__.Person'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
 
__slots__ 限制类实例对象的属性,不允许外界新增__slots__成员以外的实例属性
class Person(object):
# 限制当前类的所有实例对象的属性只能是__slots__列出的,不能新增其他属性
__slots__ = ["name", "age", "sex"]

def __init__(self, name, age):
self.name = name
self.age = age

xm = Person("小明", 18)
print(xm.name, xm.age)

xm.sex = True # 不会报错,因为__slots__中注册了sex属性
xm.score = 96 # 此处会报错,因为score属性并没有在__slots__中注册
#AttributeError: 'Person' object has no attribute 'score'
 
__dict__ 实例对象 返回对象的实例属性组成的字典
xm = Person("小明", 16)
# 返回对象的实例属性组成的字典
print("对象.__dict__:", xm.__dict__)# 对象.__dict__: {'name': '小明', 'age': 16}
 
__class__ 实例对象 返回创建当前实例对象的类
# 返回创建当前实例对象的类
print("对象.__class__:", xm.__class__)
# 对象.__class__: <class '__main__.Person'>
print("对象.__class__.__name__:", xm.__class__.__name__)
# 对象.__class__: Person
 
__module__ 实例对象 对象所在的模块
# 对象所在的模块
print("对象.__module__:", xm.__module__)
# 对象.__module__: __main__
 
__doc__ 实例对象 对象所属类的说明文档  
# 对象所属类的说明文档
print("对象.__doc__:", xm.__doc__)
# 对象.__doc__: 对象的内置属性

 二 反射机制

反射:程序通过字符串的形式操作对象中(查找/获取/删除/添加)的成员,以达到访问、检测和修改对象本身状态或行为的一种能力。在python中一切皆对象(类,实例,模块等都是对象),我们可以通过反射的形式操作对象相关的属性。

方法描述对象示例
getattr(object,name[,default]) 获取object对象的name属性的值,如果不存在,则返回默认值default,如果没有设置default,则抛出异常
class Person(object):
"""人类"""
leg_length = 2
eye_length = 2

@classmethod
def info(cls):
print(f"人类有{cls.leg_length}条腿,{cls.eye_length}只眼睛")

@classmethod
def speak(cls, message):
return f"说话:{message}"

"""getattr 获取类属性/类方法"""
print(getattr(Person, 'leg_length')) # 2

# 获取类的方法
speak = getattr(Person, "speak")
print(speak("你好!!!")) # 说话:你好!!!
实例对象  
class Student(object):
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age

def introduction(self):
"""自我介绍"""
print(f"我叫{self.name}")

xm = Student("小明", 18)
"""getattr 获取对象中的属性/方法"""
name = getattr(xm, 'name')
print(name) #小明

# 不存在,则返回默认值
sex = getattr(xm, 'sex', False)
print(xm.__dict__, sex) #{'name': '小明', 'age': 18} False

f1 = getattr(xm, 'introduction') # f1 = xm.introduction
f1() # 我叫小明
模块文件对象
import random

# random是一个内置模块文件,同时python运行时,通过import导入后,也是一个模块对象
print(random) #<module 'random' from..

# getattr 读取属性
print(getattr(random, "Random")) # <class 'random.Random'> =random.Random
randint = getattr(random, 'randint')
if callable(randint):
print(randint(1, 10))
setattr(object,name,value) 设置object对象的name属性的值,如name属性存在则覆盖,name属性不存在则新增name属性  
"""设置类属性"""
setattr(Person, 'max_age', 150)
print(Person.max_age) # 150

"""设置类方法"""
def info(cls, message):
return f"{message}:人类有{cls.leg_length}条腿,{cls.eye_length}只眼睛"

setattr(Person, 'info', info)
print(Person.info(Person, "自我介紹")) # 自我介紹:人类有2条腿,2只眼睛
实例对象
"""setattr 为对象设置属性"""
setattr(xm, 'sex', True)
print(xm.__dict__) #{'name': '小明', 'age': 18, 'sex': True}
 
模块文件对象
# 设置属性
if not hasattr(random, "name"):
setattr(random, 'name', '伪随机数模块')
print(random.name) # 伪随机数模块
hasattr(object,name) 判断object对象的name属性是否存在,返回布尔值  
# 判断属性/方法是否存在
ret = hasattr(Person, "max_age")
print(ret) # False,不存在
实例对象
"""hasattr 判断对象中是否存在 属性/方法"""
print(hasattr(xm, "name")) # True,存在name变量
print(hasattr(xm, "sex")) # False,不存在sex方法或属性
模块文件对象
# 设置属性
if not hasattr(random, "name"):
setattr(random, 'name', '伪随机数模块')
print(random.name) # 伪随机数模块
delattr(object,name) 删除object对象的name属性  
# 刪除类属性
delattr(Person, "max_age")
print(Person.__dict__)
实例对象
"""delattr 删除对象属性"""
attr = "age"
if hasattr(xm, attr):
delattr(xm, attr) # 等同于 del xm.属性名
print(xm.__dict__) # {'name': '小明', 'sex': True}
模块文件对象
print(hasattr(random,"name")) #True
delattr(random,"name")
print(hasattr(random,"name")) #False

附:

"""反射指定路径的模块"""
#基于字符串路径反射模块的方式,导入loggings.handle的RotatingFileHandler对象,并打印当前对象的所有属性和方法。
"""方式1"""
module = __import__('logging.handlers', fromlist=True)   #也可通过from logging.handlers import RotatingFileHandler直接导入。表达方式不同
print(module.RotatingFileHandler.__dict__) 

"""方式2"""
import importlib module = importlib.import_module('logging.handlers') print(module.RotatingFileHandler.__dict__)

三 异常处理

自定义异常:需要自定义异常类即可

目的:输出定义的报错信息,不只是输出内置的报错类型

"""自定义异常类,只要继承Exception异常基类,改动类型"""#
import time
class RegsiterFailException(Exception):
    pass

class NetWorkException(Exception):
    """自定义异常类"""
    def __init__(self, message):
        self.message = message
        self.time = time.time()
try: # raise NetWorkException("抛出一个自定义异常") #__main__.NetWorkException: 抛出一个自定义异常. 不做处理的输出 raise RegsiterFailException("用户注册失败!") except NetWorkException as e: print(e.message) print(e.time) except RegsiterFailException as e: print(e)
#处理后的输出:抛出一个自定义异常 1650977515.629212

四 元类(很少使用。底层代码存在着元类的使用)

1 元类:类的类。主要作用是为了控制类的创建行为

class A(object):
    pass

a = A()

"""查看一个对象的类型,也就是查看当前对象是谁实例化出来"""
print(type(a)) # <class '__main__.A'>

"""所以既然类也是一个对象,那么当然也可以通过type函数查看类是谁创建出来的"""
print(type(A)) # <class 'type'># 可以看到A这个类是由 type类创建出来的
#type是Python的一个内建元类,在python当中任何class定义的类其实都是type类实例化的结果

2 元类应用一:元类动态创建类

在Python当中,使用class关键字创建一个类的本质其实在python解释器内部会自动转化为使用type元类实例化一个类对象。对元类type进行实例化,依次传入类名(实际上就是__name__),继承的父类元组(实际上就是__bases__),类的属性方法字典(实际上就是__dict__这三个参数即可。

示例:

类声明的本质

"""通过class声明类,其实是告诉Python解释器使用type元类创建类而已"""
country = 'China'

def constructor(self, name, age):
    self.name = name
    self.age = age

def introduction(self):
    print(f"我叫{self.name},今年{self.age}岁!")

Person = type("Person", (object,), {
    'country': country,
    '__init__': constructor,
    'introduction': introduction
})

xm = Person("小明", 17)
print(xm.country) #China
xm.introduction() #我叫小明,今年17岁!

# 基于现有类,扩充类成员
def speak(self):
    return "hello! 我是新人类!"

NewPerson = type("NewPerson", Person.__bases__, {
    "speak": speak,
    **Person.__dict__ #打散
})

p1 = NewPerson("小明", 17)
print(p1.speak()) #hello! 我是新人类!

例如:我们需要不断的生产不同类型的汽车,那么不使用元类的话,代码如下

# 因为有不同类型的汽车要求,所以我们需要提前声明各种类型的汽车类
class SUV(object):
    """越野车"""
    name = "SUV"
    def run(cls):
        return f"{cls.name}汽车在飞奔..."

class Sports(object): #如果后面需要生产其他类型的汽车,则需要不断的提前声明
    """跑车"""
    name = "Sports"
    def run(cls):
        return f"{cls.name}汽车在飞奔..."

def car_factory(name): #每增加一个种类的汽车,要对car_factory函数代码进行修改
    if name == 'SUV':
        return SUV     # 返回的是类,不是类的实例
    else:
        return Sports

Car = car_factory('SUV')
print(Car)    # <class '__main__.SUV'>
print(Car().run())  # SUV汽车在飞奔...

使用元类动态创建类方法,代码如下


  @classmethod # 类方法

  def stop(cls):    

      return f"{cls.name}停车..."

def run(self): # 实例方法
    return f"{self.name}汽车在飞奔..."

class Car(object):
    def add_oid(self):
        return "加油..."

# car_factory函数中只传入一个参数name仅为举例子,实际上开发中,可以传递更多用于定制动态创建类对象的参数。
def car_factory(name):
    return type(name, (Car,), {
        "name": name,
        "run": run,
        "stop": stop
    })

SUV = car_factory('SUV')
print(SUV)  # <class '__main__.SUV'>
suv1 = SUV()  # 你可以通过这个类创建实例对象 #不需要定义SUV类
print(suv1) #<__main__.SUV object at 0x109495fa0>
print(SUV.stop()) #SUV停车... print(suv1.run()) #SUV汽车在飞奔... print(suv1.add_oid()) #加油...

3 元类应用二:自定义元类

Python中除了内置的type元类以外,用户也是可以自定义元类的,只要继承type类即可。只有继承了type元类的子类才能称之为一个元类,否则就是一个普通的自定义类,自定义元类可以精准的控制类的产生过程

"""基于自定义元类实现单例模式"""
class Singleton(type):
    """基于自定义元类实现单例模式"""  #实现单例模式:1自定义元类 2__call__ 3__new__
    def __init__(self, *args, **kwargs):
        print("元类的.__init__执行了")
        super().__init__(*args, **kwargs)
        self.__instance = None  # 保存实例化对象

    def __call__(self, *args, **kwargs):
        print("元类的.__call__执行了")
        if self.__instance is None:
            # 因为,元类是通过__call__创建类的,而普通类是通过__new__创建对象的
            self.__instance = super().__call__(*args, **kwargs)
        return self.__instance

# 方式1:通过元类Singleton动态创建类
# Log = Singleton("Log", (object,),{})

# 方式2:通过class关键字创建类,指定Log的元类是Singleton,也就是接下来,Log类会被Singleton元类创建
class Log(object, metaclass=Singleton):
    def __init__(self):
        print("类的__init__执行了")
        super().__init__()

    def __new__(cls, *args, **kwargs):
        print("类的__new__执行了")
        return object.__new__(cls)

# 使用类实例化创建对象,所以我们在实例化对象时,给类的后面加上()小括号就会调用元类的__call__方法
logger1 = Log()
logger2 = Log()
print(logger1 is logger2)  # True

"""
元类的.__init__执行了   20行创建了一个类对象
元类的.__call__执行了   30行把类对象当成函数调用
类的__new__执行了       30行把类进行了实例化,会先创建对象,__new__会出发
类的__init__执行了      __new__执行完了以后,Python
"""

4 元类应用三:基于元类的特点,保证代码规范问题

"""基于自定义元类约束类在创建时的代码规范"""
class MetaClass(type):
    """基于自定义元类约束类在创建时的代码规范"""
    def __init__(self, cls_name, cls_bases, cls_dict):
        """
        元类实例化创建类的构造函数
        :param cls_name: 类名
        :param cls_bases: 类的父类元组
        :param cls_dict: 类的属性方法成员(不包括实例属性)
        """
        if cls_name != cls_name.title():
            raise TypeError(f"类名必须首字母大写!")

        # len(cls_dict['__doc__'].strip()) == 0 把文档说明的左右两边空格字符去除以后,判断文档字符串长度是否 等于 0
        if '__doc__' not in cls_dict or len(cls_dict['__doc__'].strip()) == 0:
            raise TypeError(f"类:{cls_name} 必须有文档说明,不能为空!")

        for key, value in cls_dict.items():
            if key.startswith('__'): # 以2个下划线开头的是私有属性/私有方法
                continue
            if not callable(value):  # 属性是不可调用的
                continue
            if not value.__doc__ or len(value.__doc__.strip()) == 0:
                raise TypeError(f"公有方法:{key} 必须有文档说明,不能为空!")

        super().__init__(cls_name, cls_bases, cls_dict)  # 重用父类的功能.

class Person(metaclass=MetaClass):
    """
    人类
    """
    def speak(self, message):
        """
        说话
        :param message: 内容
        :return: 返回值
        """
        print(f"说话:{message}")

注意:

1 object是所有类的基类,type是所有类的元类,但type本质上还是类,那么object就是type的基类。如何理解两者的关系?

object 和 type 在 CPython 解释器底层中都是用c代码同时生成的,并非在Python创建的,所以并没有先后关系,并且它们的依赖关系也是通过底层C代码由官方人员人为规定的。

五 抽象类(很少使用。底层代码存在着抽象类的使用)

定义:只能被子类继承,而不能直接实例化对象的特殊类。

用途:用于定义子类的代码编写规范和编写子类中公有的具体方法。

区别于接口类,接口类仅用于定义子类的代码编写规范,没有具体的实现方法代码。

操作说明示例
@abc.abstractmethod 标记一个方法为抽象方法,只定义方法名和参数,而要求子类继承时必须实现具体代码,是个装饰器。
import abc
"""
只要继承了abc.ABC抽象基类,当前类就可以变成抽象类
abc.ABC实际上就是元类abc.ABCMate创建出来的实例。
"""

"""抽象类的声明方式1"""
class Humen(abc.ABC):
"""抽象类的基本定义"""
@abc.abstractmethod
def run(self, step):
pass

"""抽象类的声明方式2"""
class Humen(metaclass=abc.ABCMeta):
"""抽象类的基本定义"""
@abc.abstractmethod
def run(self, step):
pass

"""当一个抽象类内部声明了抽象方法以后,就不能直接用于实例化对象了。会报错!"""
# h1 = Humen()#TypeError: Can't instantiate abstract class Humen with abstract method run

"""抽象类的抽象方法必须经过子类重写实现"""
# metaclass = abc.ABC 就是在定义抽象类,实际上是设置当前类为元类
# 所以abc模块实际上是基于元类限制类的创建这种方式来达到抽象类的效果
class Payment(abc.ABC):
# 使用abc.abstractmethod 装饰器定义抽象方法
@abc.abstractmethod
def pay(self, money):
"""发起支付,请求第三方支付平台进行转账"""
pass
@abc.abstractmethod
def result_handle(self):
"""支付结果处理,获取第三方支付平台的转账结果"""
pass

class AliPay(Payment):
"""支付宝"""
def pay(self, money):
print(f"支付宝转账{money}元。")
def result_handle(self):
"""支付结果处理,获取第三方支付平台的转账结果"""
print("支付宝转账成功!")

class WebChatPay(Payment):
"""微信支付"""
def pay(self, money):
print(f"微信支付转账{money}元。")
def result_handle(self):
"""支付结果处理,获取第三方支付平台的转账结果"""
print("微信支付转账成功!")

pay = AliPay()
pay.pay(1000) #支付宝转账1000元。
pay.result_handle() #支付宝转账成功!

pay = WebChatPay()
pay.pay(500) #微信支付转账500元。
pay.result_handle() #微信支付转账成功!
abc.ABC 定义当前类为抽象类,本质上就是一个经过元类实例化的父类
abc.ABCMate 定义当前类为抽象元类,abc.ABC的父类的元类。Python3.4以后建议使用上面的abc.ABC。

 六 设计模式

1 定义:是前人针对特定场景特定问题所总结出来的解决方案。不是语法规定,而是一套用来提高代码复用性、维护性、可读性、稳健性以及安全性的解决方案。

1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,人称「GoF设计模式」)。这 23 种设计模式的本质是对面向对象设计原则五大原则的实际运用,是对面向对象的封装、继承和多态的充分理解。

范围/目的创建型模式结构型模式行为型模式
类模式 工厂(Factory Pattern) 适配器(Adapter Pattern) 模板方法(Template Method Pattern) 解释器(Interpreter Pattern)
对象模式 单例(Singleton Pattern) 原型(Prototype Pattern) 抽象工厂(Abstract Factory Pattern) 建造者(Builder Pattern) 代理(Proxy Pattern) 适配器(Adapter Pattern) 桥接(Bridge Pattern) 装饰器(Decorator Pattern) 外观(Facade Pattern) 享元(Flyweight Pattern) 组合(Composite Pattern) 策略(Strategy Pattern) 命令(Command Pattern) 职责链(Chain of Responsibility Pattern) 状态(State Pattern) 观察者(Observer Pattern) 中介者(Mediator Pattern) 迭代器(Iterator Pattern) 访问者(Visitor Pattern) 备忘录(Memento Pattern)

除了23种GoF设计模式,还有其他的常用新型设计模式,如:MVC模式(Model-View-Controller Pattern)链式模式(Chain Pattern)发布订阅模式(Publish-Subscribe Pattern)反应堆模式(Reactor Pattern)、委托模式(Delegation Pattern)、过滤器模式(Filter Pattern)。

2 模式介绍

2.1 工厂模式

工厂模式(Factory Pattern)是一种用来创建对象/类的设计模式,同时属于类与对象的创建型设计模式。在工厂模式中,我们在创建对象时不会对客户端暴露创建对象的过程,并且可以通过一个共同的接口来指向新创建的对象。工厂模式基于不同的抽象程度,分三种写法。

简单工厂模式 工厂方法模式 抽象工厂模式
import abc

class Payment(abc.ABC):
"""抽象支付类:规定每种支付方式都有同一个同样的对外调用接口方法"""
@abc.abstractmethod
def pay(self, money):
pass
'''违背了solid的 "开闭原则",如果还需要新增一种支付手段,就必须要对简单工厂PaymentFactory进行源码修改。'''
class AliPay(Payment):
"""支付宝"""
def pay(self, money):
print("支付宝支付%d元" % money)

class WechatPay(Payment):
"""微信支付"""
def pay(self, money):
print("微信支付%d元" % money)

# 工厂角色
class PaymentFactory(object):
def create(self, name):
if name == "ali":
return AliPay()
elif name == "wechat":
return WechatPay()
else:
raise TypeError("No such payment named %s." % name)

if __name__ == '__main__':
# 使用
    #创建一个工厂对象,这个对象存在的作用就是根据用户的选择不同实例化出不同的支付对象给调用方
    factory = PaymentFactory()
payment = factory.create('ali')
payment.pay(100) #支付宝支付100元

payment = factory.create('wechat')
payment.pay(100) #微信支付100元
import abc

class Payment(abc.ABC):
"""抽象支付类:规定每种支付方式都有同一个同样的对外调用接口方法"""
@abc.abstractmethod
def pay(self, money):
pass
'''遵循了solid的"开闭原则",如果需要新增支付方式时,只需要增加相应的支付方式工厂类即可。'''
class AliPay(Payment):
"""支付宝"""
def pay(self, money):
print("支付宝支付%d元" % money)

class WechatPay(Payment):
"""微信支付"""
def pay(self, money):
print("微信支付%d元" % money)

# 抽象工厂类
'''父类负责定义创建对象的公共接口,而子类则负责生成具体的对象'''
'''目的是将类的实例化操作延迟到工厂子类中完成,即由工厂子类来决定究竟应该实体化哪一种对象。'''
class PaymentFactory(abc.ABC):
"""抽象工厂: 每种支付方式都有自己的工厂"""
def create(self):
pass

class AliPayFactory(PaymentFactory):
"""支付宝对象的工厂子类"""
def create(self):
"""创建对象的工厂方法"""
return AliPay()

class WechatPayFactory(PaymentFactory):
"""微信支付的工厂子类"""
def create(self):
"""创建对象的工厂方法"""
return WechatPay()

if __name__ == '__main__':
factroy = AliPayFactory()
payment = factroy.create()
payment.pay(100) #支付宝支付100元

factroy = WechatPayFactory()
payment = factroy.create()
payment.pay(100) #微信支付100元
import abc
'''抽象工厂模式与工厂方法模式的区别仅在于:'''
'''抽象工厂模式中的每个工厂子类可以负责多个不同产品对象的创建,而工厂方法模式的每个工厂子类都是只负责创建一个产品对象的创建'''
class Payment(abc.ABC):
"""抽象支付类:规定每种支付方式都有同一个同样的对外调用接口方法"""
@abc.abstractmethod
def pay(self, money):
pass

class AliPay(Payment):
"""支付宝"""
def pay(self, money):
print("支付宝支付%d元" % money)

class WechatPay(Payment):
"""微信支付"""
def pay(self, money):
print("微信支付%d元" % money)

class GoodsHandle(object):
"""支付结束以后的商品处理"""
def handle(self):
print("支付转账成功!进行商品打包发快递")
def send(self):
print("使用菜鸟快递!")

class Goods2Handle(object):
"""支付结束以后的商品处理"""
def handle(self):
print("支付转账成功!进行商品打包发快递")
def send(self):
print("使用顺丰快递!")

class OrderHandle(object):
"""支付结束以后的订单处理"""
def handle(self):
print("支付转账成功!进行订单信息的记录")

class AbstractFactory(abc.ABC):
   '''可以一次性生产多个产品或可以完成多个动作步骤'''

"""抽象工厂: 每种支付方式都有自己的工厂"""
@abc.abstractmethod
def create(self):
"""创建支付对象的工厂方法"""
pass
@abc.abstractmethod
def order_handle(self):
"""创建支付结束以后的订单处理对象对象"""
pass
@abc.abstractmethod
def goods_handle(self):
"""创建支付结束以后的商品处理对象对象"""
pass

class AliPayFactory(AbstractFactory):
"""支付宝工厂子类"""
def create(self):
return AliPay()
def order_handle(self):
return OrderHandle()
def goods_handle(self):
return GoodsHandle()

class WechatPayFactory(AbstractFactory):
"""微信支付工厂子类"""
def create(self):
return WechatPay()
def order_handle(self):
return OrderHandle()
def goods_handle(self):
return Goods2Handle()

if __name__ == '__main__':
# 实例化工厂子类对象
factory = AliPayFactory()
# 创建支付对象
payment = factory.create()
payment.pay(1000) #支付宝支付1000元
# 创建订单处理对象
order = factory.order_handle()
order.handle() #支付转账成功!进行订单信息的记录
# 创建商品处理对象
goods = factory.goods_handle()
goods.handle() #支付转账成功!进行商品打包发快递
goods.send() #使用菜鸟快递!
 

2.2 外观模式

也叫门面模式。可以隐藏内部类的复杂实现代码的同时,为调用者提供了一个外观接口,以便调用者可以非常轻松的访问程序内部

示例:一个运维监控系统,在服务器宕机时发出警报通知管理员

import abc

class AbstractAlarm(abc.ABC):
    @abc.abstractmethod
    def get(self, message):
        """接受并整理警报信息"""
        pass
    @abc.abstractmethod
    def send(self, user):
        """发送警报内容给管理员"""
        pass

class MailAlarm(AbstractAlarm):
    """邮件发送警报类"""
    def get(self, message):
        print(f"接受并整理警报信息{message}成邮件格式")
    def send(self, user):
        print(f"通过邮件发送警报内容给管理员{user}")

class SMSAlarm(AbstractAlarm):
    """邮件发送警报类"""
    def get(self, message):
        print(f"接受并整理警报信息{message}成短信格式")
    def send(self, user):
        print(f"通过短信发送警报内容给管理员{user}")

class VoiceAlarm(AbstractAlarm):
    """语音发送警报类"""
    def get(self, message):
        print(f"接受并整理警报信息{message}成语音内容")
    def send(self, user):
        print(f"通过语言电话发送警报内容给管理员{user}")

class Log(object):
    def write(self, message):
        """记录警报信息到日志中"""

class AlarmFacade(object):
    # 定义一个外观类,其中封装对子系统的复杂操作
    def __init__(self):
        self.logger = Log()
        self.voice_alarm = VoiceAlarm()
        self.sms_alarm = SMSAlarm()
        self.mail_alarm = MailAlarm()
    def alarm(self, user, message, level):
        if level == 1:
            self.mail_alarm.get(message)
            self.mail_alarm.send(user)
        elif level == 2:
            self.sms_alarm.get(message)
            self.sms_alarm.send(user)
        elif level == 3:
            self.voice_alarm.get(message)
            self.voice_alarm.send(user)
        self.logger.write(message)

if __name__ == '__main__':
# 门面模式的最主要特点就是,提供一个外部统一调用接口,在门面类的内部实现复杂的实现功能
facade = AlarmFacade() 
facade.alarm(
"小明", "IP: 192.168.3.101,服务器宕机", 3) #接受并整理警报信息IP: 192.168.3.101,服务器宕机成语音内容 #通过语言电话发送警报内容给管理员小明

2.3 链式模式

是一种对象的行为型模式,主要的作用是简写对同一个对象的不同的方法多次调用过程

class Person(object):
    def __init__(self):
        self.range = 0 #模式移动距离为0

    def walk(self, step):
        print(f"走{step}步")
        self.range += step
        return self 

    def run(self, step):
        print(f"跑{step}步")
        self.range += step
        return self

    def get_range(self):
        return self.range

p1 = Person()
'''p1.run(5)返回值'return self。即p1 = p1.run(5),p1 = p1.run(5).walk(5)'''
ret = p1.run(5).walk(5).walk(5).run(5).get_range() #跑5步 走5步 走5步 跑5步 20
print(ret)

2.4 策略模式

属于一种对象的行为型模式,指对象有某个行为,但是在不同业务或不同场景中,该行为有不同的实现算法。

import abc

class Item(object):
    """商品类"""
    def __init__(self, product, unit_price, quantity):
        self.product = product # 商品名称/商品唯一标记
        self.quantity = quantity # 数量
        self.unit_price = unit_price # 单价
    def total(self):
        # 单品总价 = 单价 * 数量
        return round(self.unit_price * self.quantity, 2)

class Promotion(abc.ABC):
    @abc.abstractmethod
    def discount(self, order):
        """计算优惠,统计订单中所有商品的优惠比例,返回折扣价格"""
        pass

class FidelityPromo(Promotion):  # 折扣策略
    """拥有1000个以上积分的老客户可享受5%的折扣"""
    def discount(self, order):
        return order.total() * .05 if order.user["credit"] >= 1000 else 0

class BulkItemPromo(Promotion):  # 折扣策略
    """每件商品20件或以上可享受10%的折扣"""
    def discount(self, order):
        discount = 0
        for item in order.cart:
            # 计算每件商品的数量是否达到优惠门槛:20件
            if item.quantity >= 20:
                discount += item.total() * .1
        return discount

class LargeOrderPromo(Promotion):  # 折扣策略
    """订购10件或10件以上不同商品可享受7%的折扣"""
    def discount(self, order):
        distinct_items = {item.product for item in order.cart}
        if len(distinct_items) >= 10:
            return order.total() * .07
        return 0

class Order(object):
    """订单"""
    def __init__(self, user, cart, promotion=None):
        self.user = user  # 购买商品的用户
        self.cart = list(cart) # 本次购买的商品列表,也叫购物车
        self.promotion = promotion  # 优惠策略对象
        self.__total = 0  # 本次购买商品的总价格
    def total(self):
        """商品列表[购物车]的总金额"""
        # 总金额 = 单品总价 * 商品种类数量
        self.__total = sum(item.total() for item in self.cart)
        return self.__total
    def due(self):
        """经过优惠以后,用户实际应支付金额"""
        if self.promotion is None:
            discount = 0
        else:
            discount = self.promotion.discount(self)
        return self.total() - discount
    def __str__(self):
        return f"<Order total: {self.total():.2f} due: {self.due():.2f}>"

# 用户数据格式
xm = {"name":"小明", "credit": 3500}

item1 = Item("鞋子", 200, 2)
item2 = Item("裙子", 600, 1)
item3 = Item("袜子", 30, 5)

cart = [item1, item2, item3]

order1 = Order(xm, cart, FidelityPromo())
print(order1) #<Order total: 1150.00 due: 1092.50>

order2 = Order(xm, cart, BulkItemPromo())
print(order2) #<Order total: 1150.00 due: 1150.00>

order3 = Order(xm, cart, LargeOrderPromo())
print(order3) #<Order total: 1150.00 due: 1150.00>

# 查找最优惠策略
ret = min([(order.due(), order.promotion) for order in [order1, order2, order3]])
print(ret) #(1092.5, <__main__.FidelityPromo object at 0x105cb8670>)

2.5 装饰器模式

装饰器模式就是在现有的函数、类外层,套上一段逻辑代码,对其功能进行扩展和延伸,而且不会影响现有函数、类的本身结构。

import time
"""装饰器模式 = 高阶函数+闭包函数+嵌套函数"""
def decorator(fun):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = fun(*args, **kwargs)
        end = time.time()
        print(f"fun run time is {end - start}")
        return result

    return wrapper  # 返回内函数,但是没有调用


def fn(user, message):
    time.sleep(2)
    print(f"fn is running: {user}: {message}")
    return 100

ret = decorator(fn)
# 返回值就是之前的内函数wrapper,它是在被装饰的函数fn的基础上增加了功能的新函数
r = ret("xiaoming","一段话")
print(r)
'''fn is running: xiaoming: 一段话
fun run time is 2.0028302669525146
100'''

七 装饰器

基本使用

装饰器嵌套                             函数装饰器 类装饰器
被装饰对象是函数 被装饰对象是类 被装饰对象是方法
def decorator1(fun):
def wrapper():
print("decorator1-1")
fun()
print("decorator1-2")
return wrapper

def decorator2(fun):
def wrapper():
print("decorator2-1")
fun()
print("decorator2-2")
return wrapper

def decorator3(fun):
def wrapper():
print("decorator3-1")
fun()
print("decorator3-2")
return wrapper

@decorator3
@decorator2
@decorator1
def fn():
print("执行fn函数")
fn()
# 嵌套情况下,装饰器的执行顺序是从上往下开始,从下往上结束
'''decorator3-1
decorator2-1
decorator1-1
执行fn函数
decorator1-2
decorator2-2
decorator3-2'''
def decorator(func):
def wrapper(a, b):
if type(a) is not int or type(b) is not int:
print("a或者b不是整数!")
return 0
ret = func(a, b)
return ret
return wrapper

@decorator #fn= decorator(fn)
def fn(a, b):
return a + b
ret = fn(10, 2)
print(f"ret={ret}") #ret=12
'''实现单例模式'''
def singleton(cls):
# 外函数中声明一个变量__instance,用于保存类的实例对象,那么这个实例对象将始终是通一个实例对象
__instance = None
def wrapper(*args, **kwargs):
# 使用nonlocal关键字将作用域扩展到上一级,拿到上一级的__instance变量
nonlocal __instance
# 如果__instance是None,那么将__instance通过cls类(也就是被装饰的类)进行实例化
if __instance is None:
__instance = cls(*args, **kwargs)
return __instance
return wrapper

# 使用装饰器装饰一个类
@singleton #Log = singleton(Log)
class Log(object):
def __init__(self, level="DEBUG"):
self.level = level

# 实例化装饰后的类
logger1 = Log()
print(id(logger1)) # 140115885432448
logger2 = Log()
print(id(logger2)) # 140115885432448
#实现单例模式方法:类(__new__),元类(__call__),函数装饰器
 
def decorator1(fun):
def wrapper(self, *args, **kwargs): # 关键代码,显式声明第一个参数是当前被装饰的方法所属对象
print(f"{self.name}准备好了")
ret = fun(self, *args, **kwargs) # 关键代码
print(f"{self.name}操作结束了")
return ret
return wrapper

class Person(object):
def __init__(self, name):
self.name = name
@decorator1 #speak = decorator1(speak)
def speak(self, message):
ret = f"{self.name}: {message}"
print(ret)
return ret

"""当使用装饰器装饰方法,那么该类实例化出来的所有对象,调用该方法都会被装饰"""
p1 = Person("小明")
p1.speak("你好!!!")
'''小明准备好了
小明: 你好!!!
小明操作结束了'''
 
""""基于__call__方法,实现装饰器效果"""
class Decorator(object):
def __call__(self, func):
def wrapper(*args, **kwargs):
print("__call__执行")
result = func(*args, **kwargs)
print("__call__执行")
return result
return wrapper

# 因为此处必须实例化类才能触发__call__的调用,所以类装饰器下必须加上小括号
@Decorator()
def fn1(): # 相当于 fn1 = Decorator(fn1)
print("fn1函数执行!")

fn1()
'''__call__执行
fn1函数执行!
__call__执行'''
 
"""基于对象方法实现装饰器"""
class Decorator(object):
def decor1(self, func):
def wrapper(*args, **kwargs):
print("decor1 执行-1")
result = func(*args, **kwargs)
print("decor1 执行-2")
return result
return wrapper
@classmethod
def decor2(cls, func):
def wrapper(*args, **kwargs):
print("decr2-1")
result = func(*args, **kwargs)
print("decr2-2")
return result
return wrapper
@staticmethod
def decor3(func):
def wrapper(*args, **kwargs):
print("decr3-1")
result = func(*args, **kwargs)
print("decr3-2")
return result
return wrapper

@Decorator().decor1
def fn1(): # 相当于 fn1 = Decorator().decor1(fn1)
print("fn1函数执行!")
fn1()
'''decor1 执行-1
fn1函数执行!
decor1 执行-2'''
装饰器的2种写法:函数装饰器与类装饰器。而被装饰的3种对象:函数、类、类方法三种。都大同小异。
 

八 垃圾回收机制

垃圾回收机制(Garbage collection,简称GC)是Python解释器自带一种机制,专门用来回收不可用的变量值所占用的内存空间,主要由gc模块实现。

gc模块采用了引用计数法为主标记-清除和分代回收两种机制为辅的策略实现了垃圾回收机制。其中,引用计数法用于跟踪和回收垃圾,在引用计数法的基础上,通过标记-清除机制解决容器对象可能产生的循环引用的问题,最后通过分代回收机制以空间换取时间的方式提高垃圾回收的效率。

1 引用计数

定义:变量值被变量名关联的次数。

示例:
a = "A" # 数据"A"被变量名a关联了,所以数据"A"的引用次数+1
b = "a" # 数据"a"被变量名b关联了,所以数据"a"的引用次数+1

引用计数法的原理是每个数据对象维护一个变量ob_refcnt,叫引用计数器(整型),用来记录当前对象被引用的次数,也就是来追踪到底有多少变量引用指向了这个数据对象。#c

typedef struct_object {
    int ob_refcnt;
    struct_typeobject *ob_type
} PyObject;                 

当发生以下4种情况的时候,数据对象的引用计数器ob_refcnt+1:

条件举例
对象被创建赋值 a="A"
对象被引用 b=a
对象被作为参数,传到函数中 func(a)
对象作为一个元素,保存在容器类型的数据中 data=[a,2]

当发生以下4种情况时,该对象的引用计数器ob_refcnt-1,当指向该对象的内存的引用计数器为0的时候,该内存将会被Python虚拟机销毁:

条件举例
当该对象的别名被显式删除时 del a
当该对象的引别名被赋予新的对象 a="a"
当该对象离开它的作用域,如func函数执行完毕,函数中的局部变量的引用计数-1,但是全局变量与闭包不会。  
当该对象从容器中删除时,或容器本身被销毁时。 del data

可以通过sys.getrefcount(变量)来查看对象的引用次数。

import sys
class Person(object):
    pass
p = Person()
print(sys.getrefcount(Person())) 
# 1,Person()本身代表1个对象,但是该对象并没有进行赋值,所以此处的1是因为把对象作为参数传入sys.getrefcount而增加的
print(sys.getrefcount(p))
# 2,p经过赋值引用了Person的实例对象,然后把p作为参数传递给sys.getrefcount,所以此处引用了2次。

# 因为小数据池和Python内存驻留机制的原因,所以针对数字、字符串、空元祖等小数据在系统属于常驻内存,为公用对象,
# 所以他们的引用次数,无法得知
print(sys.getrefcount('10202-222'))  # 3
print(sys.getrefcount(1))  # 168

引用计数机制的优缺点:

优点缺点
简单 每一个数据对象都需要维护自己的引用计数器消耗资源 引用次数的增加或减少,都会引发引用计数机制的执行
实时,一旦没有引用(也就是ob_refcnt==0),内存就直接释放了。 处理回收内存的时间分散,而其他两种机制需要等待特定时机,时间相对集中。 容易出现容器数据类型循环引用的情况

2 标记-清除

定义:标记-清除机制就是为了解决循环引用的问题而设计的,只针对容器类型

示例:

import sys
import ctypes
"""容器数据类型的循环引用"""
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
# 你中有我,我中有你,这就是循环引用了,也就是无限循环嵌套了。
# print(list1) # [[[...]]]
list1_addr = id(list1)
list2_addr = id(list2)
del list1
del list2
# id就是内存地址,可以通过id值查找到内存中保存的数据
print(sys.getrefcount(ctypes.cast(list1_addr, ctypes.py_object).value)) # 2
print(sys.getrefcount(ctypes.cast(list1_addr, ctypes.py_object).value)) # 2
# 除了本次作为getrefcount的参数的1次引用以外,还有1个引用次数。而这个引用次数就是它们双方之间的循环引用导致的。

标记清除主要有两阶段构成,即第一阶段标记(Mark)与第二阶段清除(Sweep)。当应用程序可用的内存空间耗尽时,Python解释器就会停止整个应用程序,进行标记、清除的这两段操作。

 

栈区(也叫堆栈区),主要保存变量名、变量名与变量数据的映射关系

堆区:主要保存数据

第一阶段:标记阶段(Mask):遍历程序内部的可能会造成循环引用容器对象,如果还有其他对象引用该容器对象,则把该容器对象标记为可达(reachable)。在Python解释器内部实际上遍历所有的栈区中GC Roots对象(也就是上面提到的所有保存在栈区中的变量名等内容),并把所有GC Roots对象直接或间接关联起来的对象标记为可达状态,没有关联的则不会标记。

第二阶段:清除阶段(Sweep):再次遍历程序内部的可能会造成循环引用容器对象,如果发现某个对象没有标记为可达,则就将其回收,从而解决容器类型数据带来的循环引用问题。

标记-清除优缺点:

优点缺点
可以解决循环引用的问题
  1. 当执行标记清除时正常的程序将会被阻塞
  2. 标记清除算法在执行很多次数后,程序的堆空间会产生一些小的内存碎片。

3 分代回收

基于标记-清除这种回收机制,每次gc回收内存时,都需要把所有容器对象的引用计数都遍历一遍,这是非常消耗时间的,于是引入了分代回收来提高回收效率。

分代指的是根据存活时间来把变量划分不同等级(也是不同的代),在Python解释器内部通过NUM_GENERATIONS变量来设置为3代,分为 0(新生代),1(青春代),2(老年代) 三代,所有的新建对象都是 0 代对象,当某一代对象经历过垃圾回收,依然存活,就被归入下一代对象(按新生代->青春代->老年代顺序),每一代数据都由一个 gc_generation 数据结构来保存。Python的gc模块的分代回收机制策略如下:

  • 每新增 701 个需要 GC 的对象,触发一次新生代 GC

  • 每执行 11 次新生代 GC ,触发一次青春代 GC

  • 每执行 11 次青春代 GC ,触发一次老年代 GC (老年代 GC 还受其他策略影响,频率更低)

通俗来说,就是每新增701个引用计数为0的变量,就会对内存中所有的新生代数据进行gc遍历,每11次遍历新生代的数据以后,就会对青春代的数据进行gc遍历,每11次遍布青春代的数据以后,就会对老年代的数据进行gc遍历。

import gc
"""获取的gc模块中自动执行垃圾回收的频率。"""
print(gc.get_threshold())  # (700, 10, 10)
# 当然,我们也可以临时设置gc模块自动执行垃圾回收机制的频率
# gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率。(最好不要自己设置)
class A():
    pass

# 计算3代变量的引用次数的变化统计
print(gc.get_count()) # (584, 5, 1)
a = A()
print(gc.get_count()) # (585, 5, 1)
del a
print(gc.get_count()) # (584, 5, 1)

注:

当计数器从(699,2,1)增加到(700,2,1),gc模块就会执行一次垃圾回收gc.collect(0), 即检查0代(新生代)对象中的垃圾,并重置引用计数器为(0,3,1)
当计数器从(699,9,1)增加到(700,9,1),gc模块就会执行一次垃圾回收gc.collect(1), 即检查0代(新生代)、1代(青春代)对象的垃圾,并重置计数器为(0,0,2)
当计数器从(699,9,9)增加到(700,9,9),gc模块就会执行一次垃圾回收gc.collect(2), 即检查0(新生代)、1(青春代)、2(老年代)三代对象的垃圾,并重置计数器为(0,0,0)

posted on 2022-04-27 21:37  大明花花  阅读(26)  评论(0编辑  收藏  举报