[Python-ideas] Decorators for variables

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

On Fri, Apr 01, 2016 at 08:08:54PM +0200, Matthias welp wrote:

> > So instead of
> >
> >   a = Char(length=10, value='empty')
> >
> > you want
> >
> >   @Char(length=10)
> >   a = 'empty'
> >
> > ?
> If possible, yes. So that there is a standardized way to access changing
> variables, or to put limits on the content of the variable, 

But decoratoring a *name binding* isn't going to do that. All it will do 
is limit the *initial value*, exactly as the function call does.

After calling 

a = Char(length=10, value='empty')

the name "a" is bound to the result of Char(...), whatever that happens 
to call. But that doesn't change the behaviour of the *name* "a", it 
only sets the value it is bound to. Nothing stops anyone from saying:

a = 42

and re-binding the name to another value which is no longer a Char.

Python has no default support for running custom code when binding 
arbitrary names to a value. To get this sort of thing to work, you are 
limited to attributes, using the descriptor protocol. I'm not going 
to explain descriptors now, you can google them, but property is 
a descriptor.

So let's imagine that Python allows the @ syntax as you request, and go 
through the cases to see what that would imply.

For local variables inside functions, or global top-level module 
variables, it doesn't give you any interesting power at all. All you 
have is an alternative syntax:

x = 999

is exactly the same as 

x = spam(eggs(cheese(999)))

right now. You don't even save any characters: 28 (including newlines) 
for both. But this doesn't give us anything new and exciting, since x is 
now just a regular variable that can be replaced with some other value.

So in this scenario, this sounds boring -- it gives you nothing you 
don't already have.

Inside a class, we have the power of descriptors available to us, so we 
can use them for computed attributes. (That's how property works, among 
many others.) So suppose we have:

class MyClass(object):
    x = 999

instance = MyClass()

Now instance.x can be a descriptor, which means that it can enforce 
type and value validation rules, or logging, or whatever amazing 
functionality you want to add. This is good.

But you can already do this. You just have to write:

class MyClass(object):
    x = decorate(999)

instead. If decorate() returns a descriptor, it returns a descriptor 
whatever syntax you use.

What benefit do you gain? Well, in the function and class decorator 
case, you gain the benefit that the decoration is close to the class or 
function header, and you don't have to write the name three times:

class MyClass(object):
    def method(self):
    method = decorate(method)

But this isn't an advantage in the case of the variable:

class MyClass(object):
    x = decorate(value)

You only have to write the name once, and the decoration is not going to 
be far away. So just like the global variable case, there's no advantage 
to @decorate syntax, regardless of whether you are in a class or not. 


More information about the Python-ideas mailing list