Reputation:
I know wraps has attributes as below:
functools.wraps(wrapped[, assigned][, updated])
But I want know how to use the assigned
and updated
params, does anyone have an example?
Upvotes: 9
Views: 1241
Reputation: 110311
The "assigned" parameter tells which attributes on the wrapper function will be assigned to the attributes of the same name on the wrapped (decorated) function. By default they are '__module__', '__name__', '__doc__'
, which are defined as default in the variable functools.WRAPPER_ASSIGNMENTS . As @abarnet put it in the comments, one example of another attribute one might want to copy over would be function annotations in Python 3.x- so you might want the assigned parameter to be: ('__module__', '__name__', '__doc__', '__annotations__')
.
The "updated" parameter is more or less the same, but the corresponding attributes in the wrapper will have it's "updated" method called with that attribute from the decorated function - which means that it only works when both attributes are a mutable mapping - for example, a dictionary. By default that will update the __dict__
attribute of the wrapper with the __dict__
of the wrapped, making the wrapper function reflect all extraneous attributes it might have been assigned by different underlying decorators (decorators applied before the decorator with the call to "wraps" get applied). The only possible use cases for changing "updated" would be to change it to an empty sequence, to prevent __dict__
from being updated. Otherwise, if you wanted to update more attributes, that would mean you would be working on a project that would use different dictionary attributes on callable objects, and you certainly would have more problems at that point than not knowing how to use "wraps" properly.
TL;DR: you might find of some use for something along:
@wraps(func, ('__module__', '__name__', '__doc__', '__anotations__'), ())
def wrapper(*args, **kw):
...
But not necessarily.
As a final, anecdotal note, if the decorated object is an instance of a class with a __call__
method, instead of a function, the __dict__
update will make the wrapper function have a copy of all instance attributes of the decorated instance - that is more a side effect than a useful thing, as these copied attributes won't follow further changes of state in the decorated instance (and methods on the decorated instance will still receive "self" as the unwrapped instance):
>>> from functools import wraps
>>> class MyCallable(object):
... def pr(self):
... print(self.a)
... def __call__(self, new_a):
... self.a = new_a
>>> def deco(clb):
... @wraps(clb)
... def wrapper(*args):
... return clb(*args)
... return wrapper
...
>>> m = MyCallable()
>>> m(1)
>>> m.a
1
>>> m.pr()
1
>>> m = deco(m)
>>> m(2)
>>> m.a
1
>>> m.__wrapped__.pr()
2
So, this might be an example of you not wanting to copy __dict__
over to the wrapper, and always access the orignal instance through the __wrapped__
attribute, as above. But, I actually digress, as I had never seen anyone wrapping instances of classes with decorators, and can't think of a usecase for such that would not be better resolved by a class method doing what the decorator should do in the first place.
Upvotes: 5