style query: function attributes for return codes?

Steven Bethard steven.bethard at gmail.com
Fri Dec 10 11:40:25 EST 2004


george young wrote:
> This is obviously just evil, since a misspelling in the string
> return is treacherous.  I'm considering function attributes:
> 
> def get_connection():
>     if tcp_conn():
>         if server_allows_conn():
>             return get_connection.GOOD
>         else:
>             return get_connection.BAD_AUTH
>     else:
>         return get_connection.NO_SERVER
> get_connection.GOOD = 1
> get_connection.BAD_AUTH = 2
> get_connection.NO_SERVER = 3

Although in most cases this is probably okay, you're not guaranteed that 
the name of your function will stay the same, so there are some hazards 
in this style:

 >>> def f(x):
...     return f.y * x
...
 >>> f.y = 100
 >>> f(2)
200
 >>> g, f = f, None
 >>> g(2)
Traceback (most recent call last):
   File "<interactive input>", line 1, in ?
   File "<interactive input>", line 2, in f
AttributeError: 'NoneType' object has no attribute 'y'

One option is to turn your function into a class:

class get_connection(object):
     GOOD = 1
     BAD_AUTH = 2
     NO_SERVER = 3
     def __new__(cls):
         if tcp_conn():
             if server_allows_conn():
                 return cls.GOOD
             else:
                 return cls.BAD_AUTH
         else:
             return cls.NO_SERVER

This is a little sneaky here -- because you only need shared state 
between all instances of get_connection, you don't actually ever need to 
create an instance.  So I've overridden __new__ to return the result of 
the function instead.  This allows you to call the function just like 
you would have before.  I haven't tested the code above, but here's a 
simpler example that works:

 >>> class look_ma_no_function(object):
...     X = 42
...     def __new__(cls):
...         return cls.X
...
 >>> look_ma_no_function()
42

If you need to do this with a function that takes more parameters, you 
can just add them to the __new__ declaration:

 >>> class look_ma_no_function(object):
...     X = 42
...     def __new__(cls, s):
...         return cls.X / float(len(s))
...
 >>> look_ma_no_function('answer to life the universe and everything')
1.0

Despite the fact that this particular use seems a little sneaky to me, I 
do usually end up turning most functions that seem to need attributes 
into classes (usually with a __call__ method defined).

Steve



More information about the Python-list mailing list