I have to mention: Fundamentally, wanting to do this in the first place is almost certainly a mistake. You should surely, instead, modify the part of the code that calls these functions to just call one function and pass in a parameter, or otherwise work around the problem.
There are two parts to this question: how to generate the functions, and how to wrap them in decorators.
Create methods programmatically
To create methods programmatically, you need to use setattr()
. This allows you to add attributes to an object specified by string rather than explicitly in your code. However, for a function added in this way to be treated as a method (i.e. your object is automatically passed as the self
parameter), it must set on the class, not directly on the object.
class Foo:
pass
def f(self, val):
self.x = val
setattr(Foo, "f", f)
def g(self):
print(self.x)
setattr(Foo, "g", g)
obj = Foo()
obj.f(3)
obj.g() # prints 3
Call decorator manually
The second part is the easy bit. A decorator is just some syntactic sugar on a function that receives and returns another function. So these are equivalent:
@task(1)
def foo():
pass
def bar():
pass
bar = task(1)(bar)
In your case, you can simply call the decorator directly rather than having to use anything like the @task(1)
notation.
Putting it together
Putting the two ideas together, here's what you want. (By the way, your default parameter technique is fine for creating the function, but functools.partialmethod
is a bit cleaner because the result doesn't allow the caller to override the parameter, so I've used that below.)
def MyClass
pass # Whatever else you need
def fn(self, item_nr):
self.client.get(
url=f"/my-url/id=SEPT24_00{item_nr}",
verify=False,
auth=(os.environ['USERNAME'], os.environ['PASSWORD'])
)
for item_nr in range(1, 51):
this_fn = functools.partialmethod(fn, item_nr)
decorated_fn = task(1)(this_fn)
setattr(MyClass, f"view_item_with_id_{item_nr}", decorated_fn)