python的装饰器与设计模式中的装饰器模式

简介: python的装饰器与设计模式中的装饰器模式

相信很多人在初次接触python中的装饰器时,会跟我一样有个疑问,这跟设计模式中的装饰器模式有什么区别吗?本质上是一样的,都是对现有对象,包括函数或者类的一种扩展。这篇文档将进行对比分析。

python的装饰器

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。

使用一个性能测试的例子来说明。当我们需要测试一段训练或者推理的时长时,可能会写出类似这样的代码,直接在一个训练函数里加入时间统计

def train(X, y):
    start = time.time()
    knn = KNeighborsTimeSeriesClassifier(n_neighbors=2)
    knn.fit(X, y)
    end  = time.time()
    train_time = end - start
    print('train time cost : %.5f sec' %train_time)
    return knn

这是一段真实的代码片段,选自在某次训练任务过程中。当然如果不知道python的装饰器,在这个比较简短的函数体内,加入三行代码,其实也无伤大雅。但是既然山在那里,就要去攀登呀,因此强行上装饰器。

def getDuration(func):
    def wrapper(*args, **kwargs):
        print("%s is running" % func.__name__)
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        train_time = end - start
        print('train time cost : %.5f sec' %train_time)
        return result
    return wrapper
@getDuration
def train(X, y):
    # start = time.time()
    knn = KNeighborsTimeSeriesClassifier(n_neighbors=2)
    knn.fit(X, y)
    # end  = time.time()
    # train_time = end - start
    # print('train time cost : %.5f sec' %train_time)
    return knn

在train上方加入@+函数名,即将train函数加上一层装饰器。train函数中原本记录时间的start和end,移到wrapper中。train的函数通过wrapper中的*args、**kwargs转发。

面向对象的装饰器模式

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。其结构图为

这个通用的结构看起来略显抽象,结合一个咖啡豆的例子来说明,其UML图为:

咖啡是由咖啡豆制作来的。而咖啡又根据糖分含量(或奶含量?)的比例不同分为美式、拿铁、摩卡、卡布奇洛等等。那不同的咖啡当然可以有咖啡这个类继承而来,但更灵活的方式是用装饰器模式。这里贴出这几个类的设计实现。

// 咖啡豆,抽象类,定义接口,可以显示咖啡类型和价格
class CoffeeBean
{
public:
  virtual void ShowCoffeeName() = 0;
  virtual void ShowPrice() = 0;
public:
  std::string m_sCoffeeName;
  int m_iPrice;
};
//对咖啡豆类的一份实现
class Coffee:public CoffeeBean
{
public:
  Coffee(std::string name,int price)
  {
    m_sCoffeeName = name;
    m_iPrice = price;
  }
  ~Coffee() {}
  void ShowCoffeeName()
  {
    std::cout << "CoffeeBean name:" << m_sCoffeeName << std::endl;
  }
  void ShowPrice()
  {
    std::cout << "CoffeeBean Price:" << m_iPrice << std::endl;
  }
};
// 对咖啡豆抽象类的扩展,这个扩展类相当于在caffeebean类型上加上一个装饰器
// 的效果,不同的装饰器成为美式、拿铁和摩卡
class ExtendCoffee :public CoffeeBean
{
public:
  ExtendCoffee(CoffeeBean* pBean)
  {
    m_pBean = pBean;
  }
  ~ExtendCoffee(){}
  virtual void ShowCoffeeName() = 0;
  virtual void ShowPrice() = 0;
protected:
  CoffeeBean* m_pBean;
};
// 美式的实现版本(通过装饰器的方式)
class Americano :public ExtendCoffee
{
public:
  Americano(CoffeeBean* pBean):ExtendCoffee(pBean){}
  ~Americano() {}
  void ShowCoffeeName()
  {
    std::cout << "I am Americano Coffee,Coffee name:" << m_pBean->m_sCoffeeName + " from American" << std::endl;
  }
  void ShowPrice()
  {
    m_pBean->m_iPrice = 48;
    std::cout << "Americano Coffee price:" << m_pBean->m_iPrice << std::endl;
  }
};
// 拿铁的实现版本(通过装饰器的方式)
class Latte :public ExtendCoffee
{
public:
  Latte(CoffeeBean* pBean) :ExtendCoffee(pBean) {}
  ~Latte() {}
  void ShowCoffeeName()
  {
    std::cout << "I am Latte Coffee,Coffee name:" << m_pBean->m_sCoffeeName + " from Italy" << std::endl;
  }
  void ShowPrice()
  {
    m_pBean->m_iPrice = 58;
    std::cout << "Latte Coffee price:" << m_pBean->m_iPrice << std::endl;
  }
};
// 摩卡的实现版本(通过装饰器的方式)
class Mocha :public ExtendCoffee
{
public:
  Mocha(CoffeeBean* pBean) :ExtendCoffee(pBean) {}
  ~Mocha() {}
  void ShowCoffeeName()
  {
    std::cout << "I am Mocha Coffee,Coffee name:" << m_pBean->m_sCoffeeName + " from Franch" << std::endl;
  }
  void ShowPrice()
  {
    m_pBean->m_iPrice = 68;
    std::cout << "Mocha Coffee price:" << m_pBean->m_iPrice << std::endl;
  }
};

这里的ExtendCoffee相当于抽象装饰,由此基础上实现了不同的装饰效果,即不同的咖啡类型。

优点

  • 不改动原有代码,动态增加功能。
  • 对象间不会相互依赖、松耦合。
  • 符合开闭原则,扩展性好,便于维护。

缺点

  • 装饰器环节过多的话,导致装饰器类膨胀。
  • 装饰器层层嵌套比较复杂,可能导致排查问题流程繁琐。
参考文档

Python 函数装饰器

理解 Python 装饰器看这一篇就够了

C++装饰器模式

设计模式装饰器模式

相关文章
|
11天前
|
开发者 Python
探索Python中的装饰器:从基础到高级应用
本文将带你深入了解Python中的装饰器,这一强大而灵活的工具。我们将一起探讨装饰器的基本概念,它们如何工作,以及如何使用它们来增强函数和类的功能,同时不改变其核心逻辑。通过具体代码示例,我们将展示装饰器的创建和使用,并探索一些高级应用,比如装饰器堆栈和装饰带参数的装饰器。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角,帮助你更有效地使用装饰器来简化和优化你的代码。
|
12天前
|
测试技术 数据安全/隐私保护 开发者
探索Python中的装饰器:从基础到高级应用
装饰器在Python中是一个强大且令人兴奋的功能,它允许开发者在不修改原有函数代码的前提下增加额外的功能。本文将通过具体代码示例,带领读者从装饰器的基础概念入手,逐步深入到高级用法,如带参数的装饰器和装饰器嵌套等。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
12天前
|
开发框架 数据建模 中间件
Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器是那些静悄悄的幕后英雄。它们不张扬,却能默默地为函数或类增添强大的功能。本文将带你了解装饰器的魅力所在,从基础概念到实际应用,我们一步步揭开装饰器的神秘面纱。准备好了吗?让我们开始这段简洁而富有启发性的旅程吧!
23 6
|
3天前
|
设计模式 前端开发 JavaScript
前端必须掌握的设计模式——装饰器模式
装饰器模式是一种结构型设计模式,通过创建新类来包装原始对象,实现在不修改原有结构的前提下扩展新行为。其核心在于“组合”思想,使新功能可“即插即拔”。该模式具有解耦性、灵活性和动态性等特点,广泛应用于类的面向对象编程语言中,如JavaScript的注解和TypeScript的写法。示例中,通过装饰器模式为游戏角色动态添加装备,展示了其强大的扩展性和灵活性。
|
14天前
|
测试技术 Python
探索Python中的装饰器:简化代码,增强功能
在Python的世界中,装饰器是那些能够为我们的代码增添魔力的小精灵。它们不仅让代码看起来更加优雅,还能在不改变原有函数定义的情况下,增加额外的功能。本文将通过生动的例子和易于理解的语言,带你领略装饰器的奥秘,从基础概念到实际应用,一起开启Python装饰器的奇妙旅程。
31 11
|
11天前
|
测试技术 开发者 Python
探索Python中的装饰器:从入门到实践
装饰器,在Python中是一块强大的语法糖,它允许我们在不修改原函数代码的情况下增加额外的功能。本文将通过简单易懂的语言和实例,带你一步步了解装饰器的基本概念、使用方法以及如何自定义装饰器。我们还将探讨装饰器在实战中的应用,让你能够在实际编程中灵活运用这一技术。
26 7
|
10天前
|
Python
探索Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器就像是给函数穿上了一件神奇的外套,让它们拥有了超能力。本文将通过浅显易懂的语言和生动的比喻,带你了解装饰器的基本概念、使用方法以及它们如何让你的代码变得更加简洁高效。让我们一起揭开装饰器的神秘面纱,看看它是如何在不改变函数核心逻辑的情况下,为函数增添新功能的吧!
|
11天前
|
程序员 测试技术 数据安全/隐私保护
深入理解Python装饰器:提升代码重用与可读性
本文旨在为中高级Python开发者提供一份关于装饰器的深度解析。通过探讨装饰器的基本原理、类型以及在实际项目中的应用案例,帮助读者更好地理解并运用这一强大的语言特性。不同于常规摘要,本文将以一个实际的软件开发场景引入,逐步揭示装饰器如何优化代码结构,提高开发效率和代码质量。
35 6
|
10天前
|
存储 缓存 Python
Python中的装饰器深度解析与实践
在Python的世界里,装饰器如同一位神秘的魔法师,它拥有改变函数行为的能力。本文将揭开装饰器的神秘面纱,通过直观的代码示例,引导你理解其工作原理,并掌握如何在实际项目中灵活运用这一强大的工具。从基础到进阶,我们将一起探索装饰器的魅力所在。
|
10天前
|
设计模式 缓存 监控
Python中的装饰器模式及其高级应用####
本文探讨了Python中装饰器模式的基本原理、实现方式以及其在代码优化和功能扩展中的应用。通过具体示例,展示了如何利用装饰器简化代码结构、增强代码可读性及维护性,并进一步探讨了装饰器在日志记录、性能监控等高级场景下的实战应用。 ####