[Python-ideas] Multiple arguments for decorators

Chris Angelico rosuav at gmail.com
Mon Nov 30 21:14:48 EST 2015


On Tue, Dec 1, 2015 at 1:01 PM, Emanuel Barry <vgr255 at live.ca> wrote:
>> Date: Tue, 1 Dec 2015 12:43:28 +1100
>> From: rosuav at gmail.com
>> CC: python-ideas at python.org
>> Subject: Re: [Python-ideas] Multiple arguments for decorators
>>
>>
>> Other than @property, are there any use-cases you know of?
>
> Not in the builtins/stdlib, but I do have decorators that I'd probably
> rewrite to support the new syntax. In one of my projects I work around this
> by some other means, but it looks ugly.

The proposal would be strengthened by more examples. Currently,
@property can do something very similar to what your proposal offers,
so this is only a small improvement.

>> This is very close to the existing syntax for @property, and it better
>> adorns the individual functions. +0.5. What happens if you rename the
>> property, though? How about this:
>>
>> class Foo:
>> def __init__(self):
>> self._x = 42
>> @property
>> def x(self): # First positional parameter
>> return self._x
>> def :fset(self, value): # Named parameter
>> self._x = value
>> def :fdel(self): # Another named parameter
>> del self._x
>>
>> Remove the repetition of the name "x", and then there's no chance of
>> getting it wrong.
>
> I personally don't like this, the colon there looks weird to me. And you're
> just ignoring the first method's name and passing it positional, unlike the
> others which are named. I like the general idea you're bringing though, but
> it could use a tweak or three imo.

Agreed that the colon looks weird, but I don't know of any better way
to spell it. This was just a half-way house to the thought that
followed, though.

>> But it's sounding here more like you're creating a block of code. And
>> that, to my mind, suggests that it should be indented. Something like:
>>
>> class Foo:
>> def __init__(self):
>> self._x = 42
>> with @property as x:
>> def fget(self):
>> return self._x
>> def fset(self, value):
>> self._x = value
>> def fdel(self):
>> del self._x
>>
>> This groups the three functions, and their names would be available to
>> use as keyword arguments. It would be rather different from current
>> with-block semantics, though. Effectively, it'd be something like
>> this:
>>
>> 1) Evaluate the decorator itself (in this case, the simple name
>> 'property'), but don't call it.
>> 2) Create a new scope, nested inside the current scope. (Similar to a
>> list comp.)
>> 3) Execute the indented block in that scope.
>> 4) Call the decorator, passing all names bound in this scope as
>> keyword arguments.
>> 5) Bind the return value of the decorator to the given name.
>>
>> Thoughts?
>
> Glancing at this, it seems to me that "property" is having a unary @
> operator applied to it, but I guess that since the possibility to introduce
> a unary @ operator shrank down to exactly 0 when the decorator syntax was
> added, that's not really an issue. I'm also not sure about overloading the
> semantics of the 'with' statement.
>
> Nevertheless, I like this approach. I wonder if something similar (using a
> with statement) can be achieved right now. Probably, with the use of vars
> and sys._getframe (I never said it would be clean!)

I don't think it can, because there's no way for a context manager to
say "tell me about all name bindings in this block". The nearest I can
think of is a nested class definition, which I can make work, but it's
definitely ugly:

def call(func):
    def inner(cls):
        return func(**{k:v for k,v in cls.__dict__.items() if not
k.startswith('_')})
    return inner

class Foo:
    def __init__(self):
        self._x = 42
    @call(property)
    class x:
        def fget(self):
            return self._x
        def fset(self, value):
            self._x = value
        def fdel(self):
            del self._x


foo = Foo()
print(foo.x)
foo.x = 28
print(foo._x)

ChrisA


More information about the Python-ideas mailing list