[Python-ideas] Decorators for variables

Steven D'Aprano steve at pearwood.info
Sun Apr 3 00:19:22 EDT 2016


On Fri, Apr 01, 2016 at 07:49:41PM +0200, Matthias welp wrote:
> > > tldr
> > Check out how Django dealt with this. And SQLAlchemy
> > Do their solutions satisfy
> 
> The thing I'm missing in those solutions is how it isn't chainable. If I
> would want something that uses access logging (like what Django and
> SQLAlchemy are doing), and some 'mixin' for that variable to prevent cycle
> protection, then that would be hard. The only other way is using function
> composition, and that can lead to statements that are too long to read
> comfortably.

I'm not sure how function composition is harder to read than decorator 
syntax:

@logging
@require(int)
@ensure(str)
@validate
@spam
@eggs
x = 999


versus:

x = logging(
    require(int)(
    ensure(str)(
    validate(
    spam(
    eggs(
        999
    ))))))


is not that different. Sure, you have a few extra brackets, but you have 
fewer @ symbols. And if you're going to do that sort of thing a lot, you 
just need a couple of helper functions:

def compose(f, g):
    # Return a new function which applies f(g(args)).
    def composed(*args, **kw):
        return f(g(*args, **kwargs))
    return composed
        
def helper(use_logging=False, requires=None, ensures=None, 
           use_validate=False, use_spam=False, use_eggs=False):
    funcs = []
    if use_logging:
        funcs.append(logging)
   if requires:
        funcs.append(require(requires))
   if ensures:
        funcs.append(ensure(ensures))
   if use_validate:
        funcs.append(validate)
   # likewise for spam and eggs
   if not funcs:
       # Identity function returns whatever it is given.
       return lambda arg: arg
   else:
       f = funcs[0]
       for g in funcs[1:]:
           f = compose(g)
       return f


all_validation = helper(True, int, str, True, True, True)
x = all_validation(999)



-- 
Steve


More information about the Python-ideas mailing list