isiter builtin

2014-11-16 Thread Garrett Berg
I have been working with python for a few years now, and two of my favorite
features about it are iterators and duck typing. The fact that every
iterator under the sun can be accessed with a simple for loop is one of the
most amazing features of python.

However, there are times when I want to do type checking, and the builtin
function *isinstance* is of great use. However, this function fails to be
satisfactory in returning whether the object is a valid iterator. The call
hasattr(obj, '__iter__') also fails because str and bytes types both have
that, and are rarely what you mean when you are asking if something is an
iterator (how often have you iterated over a string?)

I propose a new builtin to address this problem. I have created code for it
here:

https://gist.github.com/cloudformdesign/de9b54d30547ddd28ec4

This will allow simple type checking on an input to determine whether it is
an iterator.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: isiter builtin

2014-11-16 Thread Chris Angelico
On Sun, Nov 16, 2014 at 6:57 PM, Garrett Berg googb...@gmail.com wrote:
 However, there are times when I want to do type checking, and the builtin
 function isinstance is of great use. However, this function fails to be
 satisfactory in returning whether the object is a valid iterator. The call
 hasattr(obj, '__iter__') also fails because str and bytes types both have
 that, and are rarely what you mean when you are asking if something is an
 iterator (how often have you iterated over a string?)

But the correct answer is that str and bytes are both iterable. So
what you're asking is not whether something's a valid iterator, but
whether it is a non-string-iterable, which is a definitely different
thing. And that's basically what your gist is doing, although there's
a slight error in its docstring: you talk about iterators, but
you're actually looking for iterables.

In Python, an iterable is something which you can iterate over:
calling iter() on it will return something (rather than raising
TypeError), which means you can use it in a 'for' loop. An iterator is
an iterable which, when you call iter() on it, returns *itself*.
Normally, you get an iterator by calling iter() on something iterable,
if that makes sense. For example:

 iter([1,2,3,4])
list_iterator object at 0x7f2908ec0978
 iter(_)
list_iterator object at 0x7f2908ec0978
 iter(range(10))
range_iterator object at 0x7f2908f21db0
 iter(_)
range_iterator object at 0x7f2908f21db0
 iter(globals())
dict_keyiterator object at 0x7f2908eb8f98
 iter(_)
dict_keyiterator object at 0x7f2908eb8f98

The list, range, and dict (as returned by globals()) are all iterable;
the ...iterator objects are iterators.

I suspect that your use-case is actually looking for iterables, not
iterators, so the fix is simply a docs change. At that point, your
function becomes 100% correct, and a perfectly idiomatic way to
express iterable that isn't one of these types. It doesn't need to
be a builtin, though.

Incidentally, if you're doing a lot of isinstance tests like this, you
might want to look at the collections module, which has some 'base
classes' which can be used like that.

 isinstance([1,2,3],collections.Iterable)
True

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: isiter builtin

2014-11-16 Thread Ben Finney
Garrett Berg googb...@gmail.com writes:

 However, there are times when I want to do type checking, and the
 builtin function *isinstance* is of great use.

I would advise that when you think you want type checking, you are
probably being overly restrictive.

 However, this function fails to be satisfactory in returning whether
 the object is a valid iterator. The call hasattr(obj, '__iter__') also
 fails because str and bytes types both have that

Yes, because both ‘str’ and ‘bytes’ objects are valid iterables.


Using ‘isinstance’ in particular is a code smell: it breaks duck typing
(checking not for the object's type, but for how the object behaves,
which is usually what matters) and is almost always the wrong choice.

Not *always* the wrong choice, which is why it's provided, and which is
why I call it a code smell: a strong but not infallible sign something
is wrong with the code as written.

When you reach for ‘isinstance’ (LBYL, which is un-Pythonic), instead
ask yourself: ignoring what type this object is, how do I want it to
behave, and should I just use it in the expectation that it will raise
an exception if it doesn't support that (EAFP, which is very Pythonic)?

-- 
 \ “What is needed is not the will to believe but the will to find |
  `\   out, which is the exact opposite.” —Bertrand Russell, _Free |
_o__)   Thought and Official Propaganda_, 1928 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: isiter builtin

2014-11-16 Thread Peter Otten
Garrett Berg wrote:

 I have been working with python for a few years now, and two of my
 favorite features about it are iterators and duck typing. The fact that
 every iterator under the sun can be accessed with a simple for loop is one
 of the most amazing features of python.
 
 However, there are times when I want to do type checking, and the builtin
 function *isinstance* is of great use. However, this function fails to be
 satisfactory in returning whether the object is a valid iterator. The call
 hasattr(obj, '__iter__') also fails because str and bytes types both have
 that, and are rarely what you mean when you are asking if something is an
 iterator (how often have you iterated over a string?)
 
 I propose a new builtin to address this problem. I have created code for
 it here:
 
 https://gist.github.com/cloudformdesign/de9b54d30547ddd28ec4

I see no reason not to inline this short function:

 def isiter(obj, exclude=(str, bytes, bytearray)):
 '''Returns True if object is an iterator.
 Returns False for str, bytes and bytearray objects
 by default'''
 return (False if isinstance(obj, exclude)
 else True if hasattr(obj, '__iter__')
 else False)

Why not

  return hasattr(obj, __iter__) and not isinstance(obj, exclude)

 This will allow simple type checking on an input to determine whether it
 is an iterator.

You mean iterable.

I'd like to see a few of your use-cases. I think most the time you don't mix 
arbitrary iterables, so to walk a tree like

[alpha, beta, [3, delta, [epsilon, zeta]]]

isinstance(obj, list) would be sufficient. Likewise for

[alpha, iter(abc), [beta, [gamma]]]

isinstance(obj, str) would work.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: isiter builtin

2014-11-16 Thread Terry Reedy

On 11/16/2014 2:57 AM, Garrett Berg wrote:

(how often have you iterated over a string?)


Often enough, but perhaps more often have written functions for which a 
string is as valid an input as many other iterables.


def cross(iterable, reiterable):
for a in iterable:
for b in reiterable:
yield a, b

print(list(cross('ab', 'cde')))
# [('a', 'c'), ('a', 'd'), ('a', 'e'), ('b', 'c'), ('b', 'd'), ('b', 'e')]

For me, there is no point in excluding such input and output.

(And yes, the code above could be enhanced by at least checking that 
reiterable is *not* an iterator, or by replacing the second arg, renamed 
iterable2, with list(iterable2), but this is besides the point.)


--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list