【问题标题】:Implementing python decorators in a toy example在玩具示例中实现 python 装饰器
【发布时间】:2021-10-10 12:57:42
【问题描述】:

我一直在寻找一个学习装饰器的用例,我想我找到了一个与我相关的用例。

我正在使用以下代码。

在文件class1.py我有:

import pandas as pd, os

class myClass():
    def __init__(self):
        fnDone = f'C:\user1\Desktop\loc1\fn.csv'
        if os.path.exists(fnDone): return
        self.Fn1()
        pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)

    def Fn1(self):
        print('something')

if __name__ == '__main__':
    myClass()

在文件class2.py我有:

class myClassInAnotherFile():
    def __init__(self):
        fnDone = f'C:\user1\Desktop\loc2\fn.csv'
        if os.path.exists(fnDone): return
        self.Fn1()
        self.Fn2()
        pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)

    def Fn1(self):
        print('something')

    def Fn2(self):
        print('something else')

if __name__ == '__main__':
    myClassInAnotherFile('DoneFile12)

有没有办法在另一个名为 utilities.py 的文件中定义通用装饰器代码,以便我可以执行以下操作:

文件中需要class1.py 我有:

import pandas as pd, os

class myClass():
    def __init__(self):
        fnDone = f'C:\user1\Desktop\loc1\fn.csv'
        self.Fn1()
        pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)

    def Fn1(self):
        print('something')

if __name__ == '__main__':
    @myDecorator
    myClass()

在文件class2.py我有:

class myClassInAnotherFile():
    def __init__(self):
        fnDone = f'C:\user1\Desktop\loc2\fn.csv'
        self.Fn1()
        self.Fn2()
        pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)

    def Fn1(self):
        print('something')

    def Fn2(self):
        print('something else')

if __name__ == '__main__':
    @myDecorator
    myClassInAnotherFile()

本质上是使用装饰器模仿原始行为。

编辑1: 我希望扩展我的类定义的功能。在两个原始类定义中,我重复检查fnDone 文件的代码,如果存在,则退出该类。 目标是有一个装饰器检查fnDone 文件,如果存在则退出该类。

编辑2: 我也可以将其作为函数执行,但我正在尝试学习如何使用装饰器扩展类或方法的功能。

编辑3: 如果我在class1.py 中有以下内容是否会更容易:

def myClass():
    fnDone = f'C:\user1\Desktop\loc1\fn.csv'
    if os.path.exists(fnDone): return
    self.Fn1()
    pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)

def Fn1(self):
    print('something')

if __name__ == '__main__':
    myClass()

class2.py如下:

def myClassInAnotherFile():
    fnDone = f'C:\user1\Desktop\loc2\fn.csv'
    if os.path.exists(fnDone): return
    self.Fn1()
    self.Fn2()
    pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)

def Fn1(self):
    print('something')

def Fn2(self):
    print('something else')

if __name__ == '__main__':
    myClassInAnotherFile('DoneFile12)

【问题讨论】:

  • 抱歉,不清楚装饰器在这里应该做什么。唯一的区别似乎是 fnDone 的(有点可疑的)使用从 __init__ 中删除了——这是装饰者无法做到的。
  • @MisterMiyagi 我更新了问题。
  • 我仍然不确定你想要完成什么,但装饰器可能不是正确的工具。抛开装饰器(或任何其他实现细节),您希望能够做什么?
  • 另外,一般来说,使用类的__init__ 方法将一些任务组合在一起通常没有多大意义。为什么不直接写一个函数呢?
  • 也许,这不是学习装饰器的最佳玩具任务。在这个问题中,继承会是一个更简洁的解决方案,而装饰器是一种反模式

标签: python python-decorators python-3.8


【解决方案1】:

因为fnDone 是一个局部变量而不是一个参数,所以使用装饰器有点尴尬。如果您稍微修改代码以将 fnDone 作为参数传递,它会使使用装饰器成为一种更可行的选择。

例如,您可以制作一个装饰器,将对象的构造函数包装起来,并检查传入的文件是否存在:

import os.path
from functools import wraps

import pandas as pd

def check_file_exists(f):
    @wraps(f)
    def _inner(self, fn_done):
        if os.path.exists(fn_done):
            return
        f(self, fn_done)
    return _inner

class MyClass:
    @check_file_exists
    def __init__(self, fn_done) -> None:
        pd.DataFrame({'Done': 1}, index=[0]).to_csv(fn_done)

if __name__ == "__main__":
    MyClass("fn.csv")

【讨论】:

    【解决方案2】:

    这是一个在运行代码之前检查文件是否存在的装饰器:

    文件:my_decorator.py

    import os
    import pandas as pd
    
    
    def checkDoneDecorator(doneFilename):
        def _decorator(decorated):
            def _wrapper_function(*args, **kwargs):
                if os.path.exists(doneFilename):
                    return
    
                try:
                    result = decorated(*args, **kwargs)
                finally:
                    pd.DataFrame({'Done': 1}, index=[0]).to_csv(doneFilename)
            return _wrapper_function
        return _decorator
    

    文件:class1.py

    from my_decorator import checkDoneDecorator
    
    
    @checkDoneDecorator(doneFilename='C:\user1\Desktop\loc1\fn.csv')
    def myClass():
        Fn1()
    
    
    def Fn1():
        print('something')
    
    
    if __name__ == '__main__':
        myClass()
    

    文件:class2.py

    from my_decorator import checkDoneDecorator
    
    
    @checkDoneDecorator(doneFilename='C:\user1\Desktop\loc2\fn.csv')
    def myClassInAnotherFile():
        Fn1()
        Fn2()
    
    
    def Fn1():
        print('something')
    
    
    def Fn2():
        print('something else')
    
    
    if __name__ == '__main__':
        myClassInAnotherFile()
    

    一些注意事项:

    • 我使用了带有参数doneFilename 的装饰器,它比简单的装饰器多了一层嵌套函数。您可以查看详细示例here
    • 我还将doneFilename 的写入包含在装饰器中,因为文件检查和文件写入是相关的。但这不是强制性的。
    • 我从您的示例中删除了self 参数,因为在此示例中实际上不需要类。如果你真的需要上课,请不要把装饰器放在__init__ 上,这样做:
    class myClass:
    
        @checkDoneDecorator(doneFilename='C:\user1\Desktop\loc1\fn.csv')
        def start(self):
            self.Fn1()
    
        def Fn1(self):
            print('something')
    
    if __name__ == '__main__':
        myClass().start()
    

    【讨论】:

      【解决方案3】:

      我赞成@pigeonhands 给出的答案,因为您主要对使用课程感兴趣。但这就是我使用常规函数实现您的目标的方式,我认为这更有意义。出于@pigeonhands 提供的相同原因,将CSV 文件名作为myclass 函数的参数作为参数名称是有意义的fnDone

      import os.path
      from functools import wraps
      
      import pandas as pd
      
      def myDecorator(func):
          @wraps(func)
          def wrapper(fnDone):
              if os.path.exists(fnDone):
                  return
              func(fnDone)
          return wrapper
      
      @myDecorator
      def myClass(fnDone):
          Fn1()
          pd.DataFrame({'Done': 1}, index=[0]).to_csv(fnDone)
      
      def Fn1():
          print('something')
      
      if __name__ == '__main__':
          myClass('test1.csv')
      

      【讨论】:

        猜你喜欢
        • 2016-07-25
        • 2011-03-08
        • 2013-06-23
        • 2010-12-12
        • 2017-07-26
        • 1970-01-01
        • 2011-05-06
        • 2018-03-10
        • 2021-12-01
        相关资源
        最近更新 更多