[Python-ideas] Explicit variable capture list

Ethan Furman ethan at stoneleaf.us
Tue Jan 26 11:55:04 EST 2016


On 01/25/2016 03:34 PM, Steven D'Aprano wrote:
> On Wed, Jan 20, 2016 at 05:04:21PM -0800, Guido van Rossum wrote:
>> On Wed, Jan 20, 2016 at 4:10 PM, Steven D'Aprano wrote:
> [...]
>>> (I'm saving my energy for Eiffel-like require/ensure blocks
>>> *wink*).
>>
>> Now you're making me curious.
>
> Okay, just to satisfy your curiosity, and not as a concrete proposal at
> this time, here is a sketch of the sort of thing Eiffel uses for Design
> By Contract.
>
> Each function or method has an (optional, but recommended) pre-condition
> and post-condition. Using a hybrid Eiffel/Python syntax, here is a toy
> example:
>
> class Lunch:
>      def __init__(self, arg):
>          self.meat = self.spam(arg)
>
>      def spam(self, n:int=5):
>          """Set the lunch meat to n servings of spam."""
>          require:
>              # Assert the pre-conditions of the method.
>              assert n >= 1
>          ensure:
>              # Assert the post-conditions of the method.
>              assert self.meat.startswith('Spam')
>              if ' ' in self.meat:
>                  assert ' spam' in self.meat
>          # main body of the method, as usual
>          serves = ['spam']*n
>          serves[0] = serves.title()
>          self.meat = ' '.join(serves)

I like that syntax.

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.

--
~Ethan~


More information about the Python-ideas mailing list