I have been trying to find a use case to learn decorators and I think I have found one which is relevant to me.
I am using the following codes.
In the file class1.py
I have:
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()
In the file class2.py
I have:
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)
Is there a way to define a generic decorator code in another file called utilities.py
so that I can do something of the following sort:
Desired in the file class1.py
I have:
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()
In the file class2.py
I have:
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()
Essentially mimicking the original behavior using a decorator.
Edit1:
I am looking to extend the functionality of my class definitions. In the both original class definitions, I repeat the code which checks for fnDone
file and if it is present, exits the class. Goal is to have a decorator which checks for the fnDone
file and exits the class if it is present.
Edit2: I can do this as a function also but I am trying to learn how to extend functionality of a class or method using decorators.
Edit3:
Does it make it easier if I have the following instead in 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()
and class2.py
as following:
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)
Because fnDone
is a local variable rather than a parameter, it makes using a decorator a bit awkward. If you modify the code slightly to pass in fnDone
as a parameter, it makes using a decorator more of a viable option.
For example, you could make a decorator that wraps the constructor of an object, and checks if the file passed in exist or not:
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")
Here is a decorator checking if the file exists before running your code:
File: 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
File: 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()
File: 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()
Some notes:
doneFilename
, which adds one more level of nested function than a simple decorator. You can see a detailed example here.doneFilename
inside the decorator, since the file check and the file writing are related. This is not mandatory though.self
arguments from your examples, as a class is not really needed in this example. If you really need a class, please don't put the decorator on __init__
and do it like this: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()
I upvoted the answer given by @pigeonhands since you were primarily interested in in using a class. But this is how I would accomplish your goal using regular functions, which I think makes more sense. For the same reason offered by @pigeonhands, it makes sense to have the CSV filename be an argument to the myclass
function as argument name 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')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With