I have a set of functions that I use very often, so I would like to collect them in a library. Before I start writing the library, I was thinking about where to store the constants that influence the behavior of some of the functions.
What I would like to write when using the library was the following:
import tools
tools.collect(object_a, object_b, mode=tools.collect.RECURSIVE)
Generally speaking, the constants the function should be able to accept should be stored in the function itself.
In order to achieve this, I created a decorator function that assigns the passed attributes to the decorated function.
def attr_decorator(**attrs):
def decorator(f):
for k, v in attrs.iteritems():
setattr(f, k, v)
return f
return decorator
This decorator could be used like this:
@attr_decorator(
FLAT = 1 << 0,
RECURSIVE 1 << 1,
)
def collect(a, b, mode):
# ...
This works pretty fine so far.
@attr_decorator(
FLAT = 1 << 0,
RECURSIVE 1 << 1,
)
def collect(a, b, mode=collect.RECURSIVE):
# ...
This does not work, because the collect function is not defined (and therefore not even decorated) at the point the default-value for the mode argument is stored.
The only solution I was able to come up with resulted in an awkward syntax and it just didn't look nice. I am giving the decorator-function the same attributes as the function that is going to be decorated.
def attr_decorator(**attrs):
def decorator(f):
for k, v in attrs.iteritems():
setattr(f, k, v)
return f
for k, v in attrs.iteritems():
setattr(decorator, k, v)
return decorator
It doesn't take a genius to recognize that this is not nice to read:
collect_d = attr_decorator(
FLAT = 1 << 0,
RECURSIVE = 1 << 1,
)
@collect_d
def collect(root, callback, mode=collect_d.RECURSIVE):
# ...
Can you think of a better approach? I would really like to stay with the "one-statement-to-decorate" thing.
You could use a special variable as a reference to the function being defined.
class Attr(object):
def __init__(self, name): self.name = name
class Attributor(object):
def __getattr__(self, x): return Attr(x)
_ = Attributor()
def attr_decorator(**attrs):
def decorator(f):
for k, v in attrs.iteritems():
setattr(f, k, v)
f.func_defaults = tuple(attrs[t.name] if isinstance(t, Attr) else t for t in f.func_defaults)
return f
return decorator
@attr_decorator(
FLAT = 1 << 0,
RECURSIVE = 1 << 1,
)
def collect(a, b, mode=_.RECURSIVE, foo=123):
print a, b, mode, foo
collect(100,200) # 100 200 2 123
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