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++装饰器模式

设计模式装饰器模式

目录
打赏
0
0
0
0
3
分享
相关文章
探索Python中的装饰器:从基础到高级应用
本文将带你深入了解Python中的装饰器,这一强大而灵活的工具。我们将一起探讨装饰器的基本概念,它们如何工作,以及如何使用它们来增强函数和类的功能,同时不改变其核心逻辑。通过具体代码示例,我们将展示装饰器的创建和使用,并探索一些高级应用,比如装饰器堆栈和装饰带参数的装饰器。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角,帮助你更有效地使用装饰器来简化和优化你的代码。
探索Python中的装饰器:从基础到高级应用
装饰器在Python中是一个强大且令人兴奋的功能,它允许开发者在不修改原有函数代码的前提下增加额外的功能。本文将通过具体代码示例,带领读者从装饰器的基础概念入手,逐步深入到高级用法,如带参数的装饰器和装饰器嵌套等。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器是那些静悄悄的幕后英雄。它们不张扬,却能默默地为函数或类增添强大的功能。本文将带你了解装饰器的魅力所在,从基础概念到实际应用,我们一步步揭开装饰器的神秘面纱。准备好了吗?让我们开始这段简洁而富有启发性的旅程吧!
64 6
Python装饰器实战:打造高效性能计时工具
在数据分析中,处理大规模数据时,分析代码性能至关重要。本文介绍如何使用Python装饰器实现性能计时工具,在不改变现有代码的基础上,方便快速地测试函数执行时间。该方法具有侵入性小、复用性强、灵活度高等优点,有助于快速发现性能瓶颈并优化代码。通过设置循环次数参数,可以更准确地评估函数的平均执行时间,提升开发效率。
118 61
Python装饰器实战:打造高效性能计时工具
Python 高级编程与实战:深入理解设计模式与软件架构
本文深入探讨了Python中的设计模式与软件架构,涵盖单例、工厂、观察者模式及MVC、微服务架构,并通过实战项目如插件系统和Web应用帮助读者掌握这些技术。文章提供了代码示例,便于理解和实践。最后推荐了进一步学习的资源,助力提升Python编程技能。
Python装饰器是什么?
装饰器是Python中用于动态修改函数、方法或类功能的工具,无需改变原代码。通过将函数作为参数传递并返回新函数,装饰器可以在原函数执行前后添加额外逻辑。例如,使用`@logger`装饰器可以打印函数调用日志,而`@timethis`则可用于计算函数执行时间。为了保持被装饰函数的元信息(如`__name__`和`__doc__`),可使用`functools.wraps`装饰器。此外,带参数的装饰器可通过嵌套函数实现,如`@timeitS(2)`,以根据参数条件输出特定信息。
93 59
设计模式3:代理、适配器、装饰器模式
代理模式是一种结构型设计模式,通过中间件解耦服务提供者和使用者,使使用者间接访问服务提供者,便于封装和控制。
41 2
前端必须掌握的设计模式——装饰器模式
装饰器模式是一种结构型设计模式,通过创建新类来包装原始对象,实现在不修改原有结构的前提下扩展新行为。其核心在于“组合”思想,使新功能可“即插即拔”。该模式具有解耦性、灵活性和动态性等特点,广泛应用于类的面向对象编程语言中,如JavaScript的注解和TypeScript的写法。示例中,通过装饰器模式为游戏角色动态添加装备,展示了其强大的扩展性和灵活性。
深入理解Python中的装饰器
本文旨在通过具体实例和详细解释,帮助读者深入理解Python中装饰器的工作原理及其在实际开发中的应用。我们将从装饰器的基本概念开始,逐步深入到其高级用法,包括自定义装饰器、带参数的装饰器以及类装饰器等。通过本文的学习,读者将能够掌握装饰器的核心思想,提高代码的可读性和可维护性。
python装饰器底层原理
Python装饰器是一个强大的工具,可以在不修改原始函数代码的情况下,动态地增加功能。理解装饰器的底层原理,包括函数是对象、闭包和高阶函数,可以帮助我们更好地使用和编写装饰器。无论是用于日志记录、权限验证还是缓存,装饰器都可以显著提高代码的可维护性和复用性。
57 5

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等