Newbie - How to iterate list or scalar ?
Bruno Desthuilliers
onurb at xiludom.gro
Tue Aug 8 06:50:19 EDT 2006
Andy Dingley wrote:
> I seem to be writing the following fragment an awful lot, and I'm sure
> it's not the best and Pythonic way to do things. Any advice on better
> coding style?
>
> pluginVersionNeeded is a parameter passed into a method and it can
> either be a simple scalar variable, or it can be a list of the same
> variables. My problem is how to iterate it, whether it's a a list or
> not.
>
> I can't simply throw it into the for...in loop -- if the scalar
> variable were to be a string (which is considered to be atomic in my
> application) then Python sees this string as iterable and iterates over
> the characters in it!
Yes, this is a common gotcha. Problem is that, as someone here noticed
sometimes ago, what should be a scalar and what should be an iterable is
often application or context dependent... And according to the context,
string being iterable can be either a royal PITA or the RightThing(tm).
>
> versionsNeeded = pluginVersionNeeded
> if isinstance( versionsNeeded, list):
> versionsToTest = versionsNeeded
> else:
> versionsToTest = [ versionsNeeded ]
> for versionNeeded in versionsToTest:
> pass
<OT>
Too much names essentially refering to the same thing IMHO... this could
be simplified to:
if not isinstance(pluginVersionNeeded, list):
pluginVersionNeeded = [pluginVersionNeeded]
for versionNeeded in pluginVersionNeeded:
pass
</OT>
The second problem here is that, given the above (I mean in your
original snippet) use of versionsToTest, there's really no reason to
assume it should be a list - any iterable could - and IMHO should - be
accepted... expect of course for strings (royal PITA case, duh).
AS far as I'm concerned, I'd rather either:
1/ test versionsToTest against basestring (which is the parent class for
both strings and unicode) and turn it into a list if needed
if isinstance( pluginVersionsNeeded, basestring):
pluginVersionsNeeded = [pluginVersionsNeeded]
for versionNeeded in pluginVersionsNeeded:
pass
The problem is that it limits 'scalars' to strings (and unicodes), and
will fail with say ints or floats etc...
This could be handled with a tuple of 'to-be-considered-as-scalar' types:
scalars = (basestring, int, float)
if isinstance( pluginVersionsNeeded, scalars):
pluginVersionsNeeded = [pluginVersionsNeeded]
for versionNeeded in pluginVersionsNeeded:
pass
2/ test for pluginVersionsNeeded.__iter__ (an attribute of most
iterables except strings...):
if not hasattr(pluginVersionsNeeded, '__iter__'):
pluginVersionsNeeded = [pluginVersionsNeeded]
for versionNeeded in pluginVersionsNeeded:
pass
It's probably the most general scheme, but won't work if you intend to
consider tuples as scalars since tuples have the __iter__ attribute.
Also, if this is a common pattern in your module, you perhaps want to
abstract this out (example for both of the above solutions):
1/
def enumerateVersion(versions, scalars=(basestring, int, float)):
if isinstance(versions, scalars):
yield versions
raise StopIteration # not required but clearer IMHO
else:
for version in versions:
yield version
NB : the default arg 'scalars' let you customize what is to be
considered as a scalar...
2/
def enumerateVersion(versions):
if hasattr(versions, '__iter__'):
for version in versions:
yield version
raise StopIteration # not required but clearer IMHO
else:
yield versions
and in the calling code:
...
for versionNeeded in enumerateVersions(pluginVersionNeeded):
do_whatever_with(versionNeeeded)
...
HTH
More information about the Python-list
mailing list