switching an instance variable between a property and a normal value

Steven Bethard steven.bethard at gmail.com
Fri Jan 7 18:49:01 EST 2005


Robert Brewer wrote:
> Steven Bethard wrote:
> 
>>I'm playing around with a mapping type that uses setdefault 
>>as suggested 
>>in http://www.python.org/moin/Python3_2e0Suggestions.  The 
>>default value 
>>for a missing key is either a simple value, or a value 
>>generated from a 
>>function.  If it's generated from the function, it should be 
>>generated 
>>new each time so that, for example, if the default is an empty list, 
>>d[1] and d[2] don't access the same list.  This is why 'c.x is c.x' 
>>should be False if I'm using the function.
>>
>>The best option I guess is to rewrite this with a 
>>_getdefault() function instead of a property:
>>
>>But I was hoping to avoid having two separate attributes (self._value 
>>and self._func) when only one should have a value at any given time.
> 
> 
> It seems to me like you were using the property as a glorified flag.
> Just use a flag.
> 
> ftypes = ('BuiltinFunctionType', 'BuiltinMethodType',
>           'FunctionType', 'GeneratorType', 'LambdaType',
>           'MethodType', 'UnboundMethodType',)
> 
> class D(dict):
>     def __init__(self):
>         self._default = None
>         self._call_default = False
>     def __getitem__(self, key):
>         if not key in self:
>             if self._call_default:
>                 self[key] = self._default()
>             else:
>                 self[key] = self._default
>         return dict.__getitem__(self, key)
>     def setdefaultvalue(self, value):
>         self._default = value
>         self._call_default = isinstance(value, ftypes)
> 
> ...or:
> 
>     def setdefaultvalue(self, value, call_callables=True):
>         self._default = value
>         self._call_default = callable(value) and call_callables

Well, the right solution using a flag for the particular behavior I was 
looking for would have to look something like:

class D(dict):
     def __init__(self):
         self._default = None
         self._call = False
     def __getitem__(self, key):
         if not key in self:
             if self._call:
                 func, args, kwds = self._default
                 self[key] = func(*args, **kwds)
             else:
                 self[key] = self._default
         return dict.__getitem__(self, key)
     def setdefault(self, value, call=False, *args, **kwds):
         if call:
             self._default = value, args, kwds
         else:
             self._default = value
         self._call = call

where I also accept *args and **kwds when the default value is to be 
called.  It's certainly doable with a flag, but note that I have to 
check the flag every time in both __getitem__ and setdefault.  It'd 
minimize redundancy a bit if I only had to check it in one place.  Guess 
I could do something like:

class D(dict):
     def __init__(self):
         self._default = None
         self._call_default = False
     def __getitem__(self, key):
         if not key in self:
             self[key] = self._default()
         return dict.__getitem__(self, key)
     def setdefault(self, value, call=False, *args, **kwds):
         if call:
             def caller():
                 return value(*args, **kwds)
         else:
             def caller():
                 return value
         self._default = caller

Then I only have to test call when setdefault is called.  Not sure I 
like this any better though...


Steve

P.S.  The reason I had two functions, setdefaultvalue and 
setdefaultfunction has to do with argument parsing for 
setdefaultfunction.  Note that

     def setdefault(self, value, call=False, *args, **kwds):
         ...

means that you can't call functions with keyword arguments 'value' or 
'call'.  That means I have to rewrite this function as something like

     def setdefault(*args, **kwds):
         self = args[0]
         value = args[1]
         call = ???
         ...

The problem is, if 'call' is a keyword argument, I don't know whether it 
was intended as one of the function arguments or as an argument to 
setdefault.

If setdefaultvalue and setdefaultfunction are two separate methods, I 
don't run into this problem.



More information about the Python-list mailing list