[Python-ideas] Adding __getter__ to compliment __iter__.
Ron Adam
ron3200 at gmail.com
Thu Jul 18 19:51:22 CEST 2013
On 07/18/2013 10:06 AM, Oscar Benjamin wrote:
> On 18 July 2013 10:08, Nick Coghlan <ncoghlan at gmail.com> wrote:
>>
>> Then builtin sum() would have the following case added to handle the
>> builder protocol:
>>
>> try:
>> bldr = builder(start)
>> except TypeError:
>> pass
>> else:
>> for item in iterable:
>> bldr += item
>> return bldr.finish()
>
> What use cases would the builder protocol have apart from using sum
> with collections (since that particular case is already well-covered
> by chain/join)?
>
> Wouldn't it be easier to put that logic into the constructor for
> type(collection) or into a factory function. Then you wouldn't need an
> additional protocol or an additional class (for each buildable
> collection).
>
> Why would you want to do this
>
> bldr = builder(()) # Build a tuple
> for val in stuff:
> bldr += item # Or append/extend
> result = bldr.finish()
>
> when you can just do this
>
> result = tuple(chain(stuff)) # or tuple(stuff)
>
> Most non-string collections already support this interface in their
> constructors or in a factory function.
If you know the result will always be a tuple, then you can certainly do
that. And probably should. But if you want the same result to be the same
type as the parts you start with, you need to write that for every type
your routine may handle.
It would look closer to this...
result = stuff[0]
result += chain(stuf[1:])
Which wouldn't always work due to not everything has an __iadd__.
So you would need to do...
result = stuff[0]
rest = chain(stuff[1:])
result = result + rest
But that creates a new object rather than extending the result. One of the
points is to be able to extend an existing obj easily.
We could do...
result = stuff[0]
rest = chain(stuff[1:])
result[:0] = rest[:]
But here again, not every type support slicing.
The current version I'm testing, based on Nicks example...
class DefaultBuilder(list):
def __init__(self, obj):
if hasattr(obj, "__iter__"):
self._obj = obj
self.__iadd__ = self.extend
else:
raise TypeError("%r is not iterabe" % obj)
def finish(self):
if self._obj is None:
raise RuntimeError("Builder already finished")
if isinstance(self._obj, str):
result = self._obj + ''.join(self)
else:
result = self._obj
result += type(self._obj)(self)
self._obj = None
return result
def builder(obj):
try:
meth = obj.__builder__
except AttributeError:
pass
else:
return meth(obj)
return DefaultBuilder(obj)
The advantages over chain, is builtin builders (Written in C) could have
access to both objects, (destination, and stuff to add), internally and may
be able to do fast memory copies and/or moves of the objects.
If we can reach that point with this suggestion, then:
1. More consistent way to copy and move data around.
(Although it may not be obvious right now.)
2. Do it much faster due to not iterating
over each object in many cases.
3. Makes it easier to write generalised routines like sum(). (*)
(* Although we are using sum() as an example, there isn't any intention of
replacing the current builtin sum() function at this time. It makes a nice
test case though.)
Cheers,
Ron
More information about the Python-ideas
mailing list