在玩具示例中实现 python 个装饰器

Implementing python decorators in a toy example

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

我正在使用以下代码。

在文件 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: 我希望扩展 class 定义的功能。在两个原始 class 定义中,我重复检查 fnDone 文件的代码,如果它存在,则退出 class。 目标是有一个装饰器来检查 fnDone 文件并在 class 存在时退出。

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

编辑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)

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

文件: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 参数,因为此示例中并不真正需要 class。如果你真的需要一个 class,请不要把装饰器放在 __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()

因为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")

我对@pigeonhands 给出的答案投了赞成票,因为您主要对使用 class 感兴趣。但这就是我使用常规函数实现您的目标的方式,我认为这更有意义。出于与@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')