Assigning generator expressions to ctype arrays
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Fri Oct 28 21:01:56 EDT 2011
On Fri, 28 Oct 2011 16:27:37 -0700, Patrick Maupin wrote:
> And, BTW, the example you give of, e.g.
>
> a,b,c = (some generator expression)
>
> ALREADY LOSES DATA if the iterator isn't the right size and it raises an
> exception.
Yes. What's your point? This fact doesn't support your proposal in the
slightest. You have argued against using a temporary array. I quote:
"It is incredibly inefficient to have to create a temp array."
[Aside: how do you know this is not just inefficient but *incredibly* so?]
But that's exactly what happens in tuple unpacking: the generator on the
right hand side is unpacked into a temporary tuple, and then assigned to
the targets on the left hand side. If the number of elements on both
sides aren't the same, the assignment fails. That is, tuple unpacking
behaves like this pseudo-code:
targets = left hand side
values = tuple(right hand side)
if len(targets) != len(values):
fail
otherwise:
for each pair target, value:
target = value
This has the important property that the assignment is atomic: it either
succeeds in full, or it doesn't occur. The only side-effect is to exhaust
the generator, which is unavoidable given that generators don't have a
length.
Without temporary storage for the right hand side, you lose the property
of atomicism. That would be unacceptable.
In the case of the ctypes array, the array slice assignment is also
treated as atomic: it either succeeds in full, or it fails in full. This
is an important property. Unlike tuple unpacking, the array is even more
conservative about what is on the right hand size: it doesn't accept
iterators at all, only sequences. This is a sensible default, because it
is *easy* to work around: if you want to unpack the iterator, just make a
temporary list:
array[:] = list(x+1 for x in range(32))
Assignment remains atomic, and the generator will be unpacked into a
temporary list at full C speed.
If you don't care about assignment being atomic -- and it's your right to
weaken the array contract in your own code -- feel free to write your own
helper function based on your earlier suggestion:
"It merely needs to fill the slice and then ask for one more and check
that StopIteration is raised."
def array_assign(array, values):
try:
if len(values) == len(array):
array[:] = values # runs at full C speed
except TypeError:
try:
for i in xrange(len(array)):
array[i] = next(values) # or values.next in Python 2.5
except StopIteration:
raise TypeError('too few items')
try:
next(values)
except StopIteration:
pass
else:
raise TypeError('too many values')
But this non-atomic behaviour would be entirely inappropriate as the
default behaviour for a ctypes array.
--
Steven
More information about the Python-list
mailing list