[Python-ideas] Explicit variable capture list
Ethan Furman
ethan at stoneleaf.us
Tue Jan 26 13:55:31 EST 2016
On 01/26/2016 08:55 AM, Ethan Furman wrote:
> Currently, something not too ugly would be to use descriptors --
> something like:
>
> from dbc import require, ensure
>
> class Frobnigate(object):
> @require
> def spammer(self, desc):
> desc.assertInRange(arg1, 0, 99)
>
> @spammer
> def _spammer(self, arg1, arg2):
> return arg1 // arg2 + arg1
>
> @spammer.ensure
> def spammer(self, desc, res):
> if desc.arg2 % 2 == 1:
> desc.assertEqual(res % 2, 1)
> else:
> desc.assertEqual(res % 2, 0)
>
> @ensure
> def egger(self, desc, res):
> desc.assertIsType(res, str)
>
> @egger
> def _egger(self, egg_type):
> 'scrambled, poached, boiled, etc'
> return egg_type
>
> Where 'desc' in the above code is 'self' for the descriptor so saved
> arguments could be accessed, etc.
>
> I put a leading underscore on the body so it could be kept separate and
> more easily subclassed without losing the DBC portions.
>
> If 'require' is not needed, one can use 'ensure'; both create the DBC
> object which would take care of calling any/all requires, then the
> function, then any/all ensures, and also grabbing and saving the
> function signature and actual parameters.
The descriptor itself might look like:
# untested
class require:
def __init__(desc, func=None):
desc.require = []
desc.ensure = []
desc.name = None
desc.func = None
def __call__(desc, func):
# desc.func is not None, func is the actual function,
# otherwise it's a requires function
if desc.func is None:
self.require.append(func)
return desc
else:
desc.func_name = name = func.__name__
if name.startswith('_'):
name = name[1:]
desc.name = name
return func
def __get__(desc, self, cls):
function = self.getattr(desc.func_name)
def caller(self, *args, **kwds):
for require in desc.require:
require(self, desc, *args, **kwds)
res = function(self, *args, **kwds)
for ensure in desc.ensure:
ensure(self, desc, res, *args, **kwds)
return res
return caller
def ensure(desc, func):
self.ensure.append(func)
return desc
def require(desc, func):
self.require.append(func)
return desc
I decided to pass args and kwds rather than save them to the descriptor
instance, hoping threading would be easier that way.
The 'ensure' class would be very similar.
This style does require the programmer to have both names: 'spammer' and
'_spammer' -- it would be a bit cleaner to have a metaclass with a
custom __getattribute__, but a lot more work and possible metaclass
conflicts when combining with other interesting metaclasses.
--
~Ethan~
More information about the Python-ideas
mailing list