Re: Python Iterables struggling using map() built-in

2014-12-11 Thread Ian Kelly
On Wed, Dec 10, 2014 at 9:01 PM, Steven D'Aprano st...@pearwood.info
wrote:

 On Wed, 10 Dec 2014 09:46:55 -0700, Ian Kelly wrote:

  I don't particularly have a problem with functions having attributes,
  e.g. I think itertools.chain.from_iterable is just peachy. There is a
  downside though, which is that making those functions attributes of
  another function rather than of the module defeats the dir() function
  for that module.

 I think you are missing the point of namespaces :-)

 When I call dir(os), I see os.path, but I don't see the names in os.path.
 That is working as designed.

I don't think I'm missing the point. os.path is a module. That makes it
well suited for a namespace, because that's what modules are designed and
expected to provide: the implementation of namespaces. A function, on the
other hand, is not well suited to be a namespace, because it's not expected
to provide one. When I see a new function, I don't think I should check
what attributes it has. Instead, I wonder about what parameters it takes
and what it returns.

I expect to be able to open up my web browser and go to the library
documentation for the os.path module. I don't expect to find a separate
page for statistics.median, because the docs are organized around modules,
not functions.  Would the median function itself be documented in the
statistics page, in the statistics.median page, or redundantly in both?

I expect to be able to import modules, and by extension, namespaces.
 import os.path works.  import statistics.median.low would return an
error, as would from statistics.median import low as median, if that were
the only function I wanted. In the latter case I would have to do something
like from statics import median; median = median.low. That's not just
ugly; it also demonstrates how the programmer needs to remember and
incorporate the detail that this namespace does not work like other
namespaces.

 As the designer of the module I thought that median_low and median_high
 were important enough to include but not important enough to stick in the
 main module namespace.

To me this sounds backward. Namespaces are a great idea, but at the same
time they also create cognitive burden. One creates a namespace because the
contents are important enough to be referenced as a group, not because they
aren't as important as the other things they're grouped with. The reason
the collections ABCs were moved into collections.abc wasn't because they
weren't important enough to be in the top-level collections module.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-11 Thread Steven D'Aprano
Ian Kelly wrote:

 A function, on the
 other hand, is not well suited to be a namespace, because it's not
 expected to provide one.


And that is exactly the point I am making about the inherent
conservativeness of Python developers.

Functions ARE namespaces, like instances of user-defined classes they have a
__dict__ and can carry per-instance state. (If you think that only modules
are namespaces, you are badly mistaken. Instances are namespaces too.) This
is *not* an accident of implementation, it is a deliberate design choice.
Contrast functions with builtin functions:

py def f(): pass
...
py vars(f)
{}
py vars(len)
Traceback (most recent call last):
  File stdin, line 1, in module
TypeError: vars() argument must have __dict__ attribute


Although functions are written in pure Python, their type is implemented in
C and could easily have been designed to be __dict__-less just like the
builtin_function_or_method type. But they weren't. It is a language feature
that functions are namespaces.

So why don't we use functions as namespaces?

We don't use functions as namespaces because nobody thinks of them as
namespaces, and we don't think of them as namespaces because nobody uses
them as namespaces -- and when somebody tries to break out of that vicious
circle, the conservativeness kicks in and the very idea is rejected.

I think that there is a legitimate debate to be had as to whether this
conservativeness and resistance to change is a good thing or a bad thing,
but I don't think that there should be any debate about the reality of the
Python community being strongly conservative, compared to the anything
goes attitude of (say) Ruby, Perl and Lisp programmers. We don't like code
that does anything we haven't seen a hundred times before.



-- 
Steven

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


Re: Python Iterables struggling using map() built-in

2014-12-11 Thread Ian Kelly
On Thu, Dec 11, 2014 at 4:28 PM, Steven D'Aprano 
steve+comp.lang.pyt...@pearwood.info wrote:

 Ian Kelly wrote:

  A function, on the
  other hand, is not well suited to be a namespace, because it's not
  expected to provide one.


 And that is exactly the point I am making about the inherent
 conservativeness of Python developers.

 Functions ARE namespaces, like instances of user-defined classes they
have a
 __dict__ and can carry per-instance state. (If you think that only modules
 are namespaces, you are badly mistaken. Instances are namespaces too.)

I never said that functions can't be used as namespaces. I said that
functions are *bad* namespaces, and I gave reasons why I think this is true.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-11 Thread Chris Angelico
On Fri, Dec 12, 2014 at 10:28 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 I think that there is a legitimate debate to be had as to whether this
 conservativeness and resistance to change is a good thing or a bad thing,
 but I don't think that there should be any debate about the reality of the
 Python community being strongly conservative, compared to the anything
 goes attitude of (say) Ruby, Perl and Lisp programmers. We don't like code
 that does anything we haven't seen a hundred times before.

I agree that Python is a lot more conservative than many languages,
and personally, I think it's good. When it comes to monkeypatching,
less is *definitely* better - less places where you have to figure out
what import changed the meanings of your built-ins, and so on. (Case
in point: A logger replacement tampered with the
log.info()/log.error() family of functions, adding a keyword
parameter. Took me a fair amount of digging to find out exactly where
that happened, and it made REPL importing of the module's functions
rather awkward.) The same is true of obscure namespaces, specifically
because they're obscure; if I do a JSON decode of something, I'm not
going to check to see if numbers came through as a subclass of float
with a .warnings attribute to tell me about loss of numeric
precision, even though that would be perfectly possible. Since almost
nobody does this, nobody will expect it to be done, ergo nobody will
go looking for it, and the feature may as well not even have been
implemented if it's never seen. Popularity *is* sometimes a legitimate
argument.

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


Re: Python Iterables struggling using map() built-in

2014-12-11 Thread Roy Smith
In article mailman.16880.1418342293.18130.python-l...@python.org,
 Ian Kelly ian.g.ke...@gmail.com wrote:

 I never said that functions can't be used as namespaces. I said that
 functions are *bad* namespaces, and I gave reasons why I think this is true.

An excellent example of functions acting as namespaces is nosetest's 
@attr() decorator.  We use this, for example, to tag certain test cases 
as being reliant on facebook being up(*):

@attr('facebook', 'services')
def test_some_facebook_thing():
# whatever

this lets us turn all those tests on or off with a single switch.  The 
way @attr() is implemented, it sets attributes on the decorated 
function.  It's the most logical and obvious place to store a piece of 
information about a test case -- right on the test case itself.

Similarly, we've got tests that we annotate as being dependent on itunes 
being reachable, depending on certain data existing in the database(**), 
being too slow to want to run all the time(**), etc.

(*) and please don't tell me that tests shouldn't depend on external 
services.

(**) see first footnote
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-11 Thread Ian Kelly
On Thu, Dec 11, 2014 at 6:55 PM, Roy Smith r...@panix.com wrote:

 In article mailman.16880.1418342293.18130.python-l...@python.org,
  Ian Kelly ian.g.ke...@gmail.com wrote:

  I never said that functions can't be used as namespaces. I said that
  functions are *bad* namespaces, and I gave reasons why I think this is
true.

 An excellent example of functions acting as namespaces is nosetest's
 @attr() decorator.  We use this, for example, to tag certain test cases
 as being reliant on facebook being up(*):

 @attr('facebook', 'services')
 def test_some_facebook_thing():
 # whatever

 this lets us turn all those tests on or off with a single switch.  The
 way @attr() is implemented, it sets attributes on the decorated
 function.  It's the most logical and obvious place to store a piece of
 information about a test case -- right on the test case itself.

I agree this is a great use of function attributes, but this is an example
of tags or annotations, not namespaces. The purpose of namespaces is to
distinguish between different entities of the same name, e.g. 'pow' and
'math.pow' are two distinct functions that both happen to be named pow. We
can tell them apart because they're in separate namespaces.

In the nosetest example, the 'facebook' attribute always means the same
thing no matter which functions you apply it to. Conceptually speaking,
each instance of the attribute is the same annotation. The expression
test_some_facebook_thing.facebook doesn't perform the role of a namespace
because it's used to signify the presence of that annotation, not to
identify something.

A similar example from the standard library is the functools.lru_cache
decorator, which adds a cache_info function to the wrapped function.
However, the cache_info function always performs the same task in relation
to the particular function it decorates, so in this regard it acts more
like an object method than like a name in a namespace.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Steven D'Aprano
On Tue, 09 Dec 2014 21:44:54 -0500, Roy Smith wrote:

 In article 54878f8a$0$13010$c3e8da3$54964...@news.astraweb.com,
  Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote:
 
 I really think you guys are trying too hard to make this function seem
 more complicated than it is. If you find it so hard to understand a
 simple function with four short lines, one wonders how you would
 possibly cope with real code.
 
 Well, look at it this way.  You've got several folks here (I count
 Terry, Ned, Chris, and myself) all saying, We find this confusing, and
 you're saying, nobody should find this confusing.  I suppose one
 possible explanation for this dichotomy is that we're all incapable of
 dealing with real code.  Yeah, that must be it.

I know you guys deal with *much* more complicated, obfuscated and 
difficult code. Just look at the stuff posted here by newbies :-) But you 
insist that you're confused by while iters. I don't get it. I think 
you're protesting too much -- like the weightlifter who routinely bench-
presses 400 lbs, but then insists he can't take the trash out because 
it's too heavy. Or you're insisting on an unrealistic ideal of 
readable, that if it takes *any time at all* to think about the code, 
that makes it bad.

I've noticed this deep-seated conservatism in Python programmers before. 
Parts of the language are deeply under-utilised, because there are simple 
idioms that people refuse to use because they're confusing even though 
they are a trivial generalisation of things that we use all the time.

Or at least, they should be common idioms. They're not common idioms 
because nobody uses them, and nobody uses them because they're not common 
idioms.

Example: In the statistics module in Python 3.4, I added a `median` 
function to calculate the median by the traditional schoolbook algorithm. 
But that's only one out of a number of ways to calculate medium, and 
inspired by similar syntax from R I proposed adding additional methods to 
that `median` object:

median.low(alist)
median.high(alist)

etc. We all know that functions are first class objects with attributes 
and methods, they have a __dict__ so you can attach per-function data to 
them. And there's the last line of the Zen, about namespaces being 
awesome. Every instance with a __dict__ is a namespace.

This ought to be a no-brainer, but apparently nobody has any experience 
with callable attributes attached to instances:

math.sin(x)
some string.split(s)

We make it a point of pride that functions in Python are not just first 
class values but that they are objects with attributes and methods and a 
per-instance __dict__. But when somebody proposes making use of that, it 
is rejected. Not that I'm bitter :-)

But I digress.

I'm truly sorry that I have been unable to express in words just how 
elegant and beautiful the short version of the myzip() function is. It is 
vanishingly rare to have such a perfect match between an algorithm and 
real working code.

And if anyone has got the impression that I'm calling you a dummy because 
you don't see it my way, I'm not. I'm calling you nekulturny and somebody 
who can't recognise elegant code when it's staring you right in the 
face :-P



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


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Chris Angelico
On Wed, Dec 10, 2014 at 8:24 PM, Steven D'Aprano st...@pearwood.info wrote:
 And if anyone has got the impression that I'm calling you a dummy because
 you don't see it my way, I'm not. I'm calling you nekulturny and somebody
 who can't recognise elegant code when it's staring you right in the
 face :-P

I love it when a post sends me to a dictionary. Though in this case,
the word actually has Russian origin, so it ought perhaps to be
spelled некультурны. Good word, anyhow.

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


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Marko Rauhamaa
Steven D'Aprano st...@pearwood.info:

 I've noticed this deep-seated conservatism in Python programmers
 before. Parts of the language are deeply under-utilised, because there
 are simple idioms that people refuse to use because they're
 confusing even though they are a trivial generalisation of things
 that we use all the time.

IMO, large parts of the language are deservedly under-utilized. This
whole dunder thicket: __complex__(), __ifloordiv__(), __rxor__() etc. Or
this metaclass attribute. Or decorators.


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


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Ian Kelly
On Wed, Dec 10, 2014 at 2:24 AM, Steven D'Aprano st...@pearwood.info
wrote:

 Example: In the statistics module in Python 3.4, I added a `median`
 function to calculate the median by the traditional schoolbook algorithm.
 But that's only one out of a number of ways to calculate medium, and
 inspired by similar syntax from R I proposed adding additional methods to
 that `median` object:

 median.low(alist)
 median.high(alist)

 etc. We all know that functions are first class objects with attributes
 and methods, they have a __dict__ so you can attach per-function data to
 them. And there's the last line of the Zen, about namespaces being
 awesome. Every instance with a __dict__ is a namespace.

 This ought to be a no-brainer, but apparently nobody has any experience
 with callable attributes attached to instances:

 math.sin(x)
 some string.split(s)

 We make it a point of pride that functions in Python are not just first
 class values but that they are objects with attributes and methods and a
 per-instance __dict__. But when somebody proposes making use of that, it
 is rejected. Not that I'm bitter :-)

I don't particularly have a problem with functions having attributes, e.g.
I think itertools.chain.from_iterable is just peachy. There is a downside
though, which is that making those functions attributes of another function
rather than of the module defeats the dir() function for that module.
Likewise the generated help for the help() function, unless care is taken
to explicitly mention the existence of those functions in either the doc
string for the module or the standard median function. Thirdly, IDEs with
code completion features may simply fail to notice that these alternate
versions of the function exist. For these reasons I think that this is an
anti-pattern and is best avoided in the standard library. Having to type _
instead of . is only one extra key stroke.

Now if there were an established pattern to such function attributes, e.g.
if f.len(parrots) was a standard alternative to len(f(parrots)) for
functions that return sequences, then that could be a useful feature,
especially if such properties could be chained arbitrarily. But one-offs
like median.low would only serve to make the function more difficult to
find than necessary.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Terry Reedy

On 12/10/2014 11:46 AM, Ian Kelly wrote:


I don't particularly have a problem with functions having attributes,
e.g. I think itertools.chain.from_iterable is just peachy. There is a
downside though, which is that making those functions attributes of
another function rather than of the module


Itertools.chain is a class, not a function.  Chain.from_iterable is an 
*alternate constructor* for instances of the class, just as 
dict.fromkeys is an alternate constructor for dict instances.


It is true that if itertools were written in python, chain and the other 
classes would likely be written as generator functions, so tha analogy 
would not apply.



defeats the dir() function for that module.


And it is also true that dir(__builtins__) does not show dict.fromkeys.


Likewise the generated help for the help() function,
unless care is taken to explicitly mention the existence of those
functions in either the doc string for the module


help(it.chain) lists
 |  from_iterable(...) from builtins.type
 |  chain.from_iterable(iterable) -- chain object
 |
 |  Alternate chain() contructor taking a single iterable argument
 |  that evaluates lazily.

help(dict)
 |  fromkeys(iterable, value=None, /) from builtins.type
 |  Returns a new dict with keys from iterable and values equal to 
value.



Thirdly, IDEs with code completion features may simply fail to
notice that these alternate versions of the function exist.


I don't know what you mean here.  In Idle
 it.chain.  -- completion box with from_iterable
 dict.   -- completion box that includes fromkeys

None of the above is to say that alternate constructors for classed are 
not a bit awkward.  I believe this was recognized when dict.fromkeys was 
added.  But it was considered less awkward than the alternatives: 1. do 
not add the functionality; 2. further overload the main (__init__) 
constructor; 3. add a independent function.  Anyway, they exist, are an 
accepted pattern among core devs, and will probably increase in number.


--
Terry Jan Reedy

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


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Ian Kelly
On Wed, Dec 10, 2014 at 10:48 AM, Terry Reedy tjre...@udel.edu wrote:
 Likewise the generated help for the help() function,
 unless care is taken to explicitly mention the existence of those
 functions in either the doc string for the module


 help(it.chain) lists
  |  from_iterable(...) from builtins.type
  |  chain.from_iterable(iterable) -- chain object
  |
  |  Alternate chain() contructor taking a single iterable argument
  |  that evaluates lazily.

As you point out, chain is a class, not a function. When you invoke help()
on a function, it lists the members, of which a static alternate
constructor would be one. Try it with a function, though:

 import statistics
 statistics.median.low = statistics.median_low
 help(statistics.median)
Help on function median in module statistics:

median(data)
Return the median (middle value) of numeric data.

When the number of data points is odd, return the middle data point.
When the number of data points is even, the median is interpolated by
taking the average of the two middle values:

 median([1, 3, 5])
3
 median([1, 3, 5, 7])
4.0

It only prints out the doc string, nothing about the attributes. So the doc
string for median would need to explicitly document the existence of
median.low and median.high in addition to its own usage.

 Thirdly, IDEs with code completion features may simply fail to
 notice that these alternate versions of the function exist.

 I don't know what you mean here.  In Idle
  it.chain.  -- completion box with from_iterable
  dict.   -- completion box that includes fromkeys

So Idle gets it right. At least for static methods of classes, which isn't
very surprising. Does it complete a function attribute of a function? I
don't have it installed to test.

Having used Komodo IDE for a number of years and been occasionally
frustrated by its code completion, it would not surprise me in the least if
it failed to pull attributes of functions into its completion database.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Steven D'Aprano
On Wed, 10 Dec 2014 09:46:55 -0700, Ian Kelly wrote:

 I don't particularly have a problem with functions having attributes,
 e.g. I think itertools.chain.from_iterable is just peachy. There is a
 downside though, which is that making those functions attributes of
 another function rather than of the module defeats the dir() function
 for that module. 

I think you are missing the point of namespaces :-)

When I call dir(os), I see os.path, but I don't see the names in os.path. 
That is working as designed.


 Likewise the generated help for the help() function,
 unless care is taken to explicitly mention the existence of those
 functions in either the doc string for the module or the standard median
 function. 

Yes.

I don't have a problem with saying See also ``median.low`` in the 
docstring for median.

I would also have raised a feature request (and/or patch) for having 
help() automatically display functions and methods attached to a 
function, much as it already does for classes.

As the designer of the module I thought that median_low and median_high 
were important enough to include but not important enough to stick in the 
main module namespace. I wanted to delegate them to secondary status by 
putting them in a separate lightweight namespace, without going all the 
way to making statistics be a package. I even considered doing the same 
for the population variance and standard deviation, but decided against 
it as most people are familiar with them from scientific calculators and 
school.

Python gives us the functionality to include multiple namespaces in a 
single module. Despite the Zen of Python, hardly anyone makes use of the 
fact that functions are namespaces. I wish to rail against that :-)


 Thirdly, IDEs with code completion features may simply fail to
 notice that these alternate versions of the function exist.

Hmmm, that would be a bad bug in the IDE then. If I type

foo.


I would expect the IDE to complete on whatever attributes foo has, 
regardless of whether it is a function or some other object.

I have tab completion enabled in Python 2.7, and it works for me:

py def spam(): pass
... 
py spam.
Display all 31 possibilities? (y or n)
spam.__call__(  spam.__globals__spam.__str__(
spam.__class__( spam.__hash__(  spam.__subclasshook__(
spam.__closure__spam.__init__(  spam.func_closure
spam.__code__   spam.__module__ spam.func_code
spam.__defaults__   spam.__name__   spam.func_defaults
spam.__delattr__(   spam.__new__(   spam.func_dict
spam.__dict__   spam.__reduce__(spam.func_doc
spam.__doc__spam.__reduce_ex__( spam.func_globals
spam.__format__(spam.__repr__(  spam.func_name
spam.__get__(   spam.__setattr__(   
spam.__getattribute__(  spam.__sizeof__(


I expect that most IDEs will Just Work in this case.

[...]
 Now if there were an established pattern to such function attributes,

But there's the rub. That's the conservatism I'm referring to. Somebody 
has to be the first to do such a thing, and if we reject it because 
nobody does it, nobody will do it because when they try they're always 
rejected.

We know namespaces are great and useful. We use namespaces in the form of 
modules, packages and classes, and the Zen says we should use more of 
them. But we don't, because we're trapped in a vicious circle where 
attempts to use more namespaces get dismissed because nobody uses them, 
and nobody uses them because when they try it gets dismissed :-(


Oh well, there's always my own non-stdlib modules :-)



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


Re: Python Iterables struggling using map() built-in

2014-12-10 Thread Terry Reedy

On 12/10/2014 3:32 PM, Ian Kelly wrote:


So Idle gets it right. At least for static methods of classes, which
isn't very surprising. Does it complete a function attribute of a
function?


 def f(): pass

 f.a='attr'
 f.  box with 'a' as possible completion.


Having used Komodo IDE for a number of years and been occasionally
frustrated by its code completion, it would not surprise me in the least
if it failed to pull attributes of functions into its completion database.


I do not think Idle has a static database.  But I have not looked at the 
completion code yet to see exactly how it works (and how it might be 
improved).


--
Terry Jan Reedy

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


Re: Python Iterables struggling using map() built-in

2014-12-09 Thread Terry Reedy

On 12/9/2014 12:03 AM, Terry Reedy wrote:

Roy Smith wrote:


Chris Angelico wrote:



def myzip(*args):
 iters = map(iter, args)
 while iters:
 res = [next(i) for i in iters]
 yield tuple(res)


Ugh.  When I see while foo, my brain says, OK, you're about to see a
loop which is controlled by the value of foo being changed inside the
loop.


What is nasty to me is that to understand the loop, one must do a whole 
program analysis to determine both that 'iters' is not rebound and that 
the list it is bound to is not mutated.  To do the later, one must not 
only read the loop body, but also preceding code to make sure the list 
is not aliased.


 iters is empty if and only if args is empty.

If args is empty, iters should not be created.

if args:
   iters = ...
   while True
 ... (return on exception)

makes the logic clear.


Once the logic is clear and 'localized', even a simple compiler like 
CPython's can see that this is a loop-forever construct and that the 
loop test is unnecessary.  So it can be removed.


 dis(while a: b+=1)
  1   0 SETUP_LOOP  20 (to 23)
3 LOAD_NAME0 (a)
  6 POP_JUMP_IF_FALSE   22
  9 LOAD_NAME1 (b)
 12 LOAD_CONST   0 (1)
 15 INPLACE_ADD
 16 STORE_NAME   1 (b)
 19 JUMP_ABSOLUTE3
   22 POP_BLOCK
   23 LOAD_CONST   1 (None)
 26 RETURN_VALUE

 dis(while True: b+=1)
  1   0 SETUP_LOOP  13 (to 16)
3 LOAD_NAME0 (b)
  6 LOAD_CONST   0 (1)
  9 INPLACE_ADD
 10 STORE_NAME   0 (b)
 13 JUMP_ABSOLUTE3
   16 LOAD_CONST   1 (None)
 19 RETURN_VALUE

'while 1' and 'while exception not raised' are similarly condensed. 
This leaves only the initial test of the argument.


--
Terry Jan Reedy

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


Re: Python Iterables struggling using map() built-in

2014-12-09 Thread Steven D'Aprano
Terry Reedy wrote:

 On 12/9/2014 12:03 AM, Terry Reedy wrote:
 Roy Smith wrote:

 Chris Angelico wrote:

 def myzip(*args):
  iters = map(iter, args)
  while iters:
  res = [next(i) for i in iters]
  yield tuple(res)

 Ugh.  When I see while foo, my brain says, OK, you're about to see a
 loop which is controlled by the value of foo being changed inside the
 loop.
 
 What is nasty to me is that to understand the loop, one must do a whole
 program analysis to determine both that 'iters' is not rebound and that
 the list it is bound to is not mutated.

When people say whole program analysis, they usually mean the entire
application including all its modules and libraries, not a four line
function, excluding the def header. (Or three lines if you get rid of the
unnecessary temporary variable 'res'.) Did it take you a long time to read
all two lines of the while loop to determine that iters is not modified or
rebound?

I really think you guys are trying too hard to make this function seem more
complicated than it is. If you find it so hard to understand a simple
function with four short lines, one wonders how you would possibly cope
with real code. Purely by coincidence, I have the source to the pyclbr
module from the standard library open in a text editor. I see a _readmodule
function that looks, in part, like this:


try:
 for tokentype, token, start, _end, _line in g:
 if ...
 while ...
 elif ...
 while..
 if ...
 if ...
 if ...
 else ...
 elif ...
 while ...
 if ...
 if ...
 while ...
 if ...
 if ...
 else ...
 if ...
 if ...
 if ...
 if ...
 elif ...
 if ...
 elif ...
 elif ...


at which point I'm about halfway through the try block and I'm giving up.

https://hg.python.org/cpython/file/3.4/Lib/pyclbr.py

This, presumably, is good enough for the standard library, but the four line
version of zip is supposed to be too hard for mortal man to comprehend.
That's funny :-)


 To do the later, one must not 
 only read the loop body, but also preceding code to make sure the list
 is not aliased.

The preceding code is exactly *one* line, a single assignment binding the
name iters to the list. The while loop body is exactly two lines, one if
you dump the unnecessary 'res' temporary variable:

yield tuple([next(i) for i in iters])


Quite frankly Terry, I do not believe for a second that somebody like you
who can successfully maintain IDLE is struggling to understand this myzip()
function.

Wait... is this like the Four Yorkshire Men sketch from Monty Python, only
instead of complaining about how hard you had it as children, you're all
trying to outdo each other about how difficult you find it to read this
function? If so, well done, you really had me for a while.



 Once the logic is clear and 'localized', even a simple compiler like
 CPython's can see that this is a loop-forever construct and that the
 loop test is unnecessary.  So it can be removed.

Ah, now that's nice. You're suggesting that by moving the loop condition
outside of the while statement, the compiler can generate more efficient
byte code. It only needs to test iters once, not at the start of every
loop.

That is the first interesting argument I've seen so far!

On the one hand, as micro-optimizations go, it will be pretty micro.
Particularly compared to the cost of starting and stopping a generator, I
doubt that will save any meaningful time, at least not enough to make up
for the extra effort in having to read and comprehend one more line of
code.

On the other hand, *premature optimization*. In general, one shouldn't write
more complex code so the compiler can optimize it, one should write simpler
code and have a smarter compiler. If *we* are capable of recognising that
iters is not modified in the body of the loop, then the compiler should be
capable of it too. (If it isn't, it is because nobody has bothered to give
the compiler sufficient smarts, not because it can't be done.) So a good
compiler should be able to compile while iters into if iters: while
True so long as iters is not modified in the body of the loop. 

Still, that's a nice observation: sometimes more complex source code can
lead to simpler byte code.



-- 
Steven

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


Re: Python Iterables struggling using map() built-in

2014-12-09 Thread Chris Angelico
On Wed, Dec 10, 2014 at 11:10 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 On the other hand, *premature optimization*. In general, one shouldn't write
 more complex code so the compiler can optimize it, one should write simpler
 code and have a smarter compiler. If *we* are capable of recognising that
 iters is not modified in the body of the loop, then the compiler should be
 capable of it too. (If it isn't, it is because nobody has bothered to give
 the compiler sufficient smarts, not because it can't be done.) So a good
 compiler should be able to compile while iters into if iters: while
 True so long as iters is not modified in the body of the loop.

In general, one can't expect the boolification of a Python object to
be consistent, so the compiler can't optimize this. How can it be sure
the list will never become empty?

I'm still of the opinion that a while loop's header implies something
about the code; while iters: implies that iters might be able to
become false. Sure, you can verify easily enough that it never will...
but why should you have to verify at all?

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


Re: Python Iterables struggling using map() built-in

2014-12-09 Thread Mark Lawrence

On 10/12/2014 00:10, Steven D'Aprano wrote:


Wait... is this like the Four Yorkshire Men sketch from Monty Python, only
instead of complaining about how hard you had it as children, you're all
trying to outdo each other about how difficult you find it to read this
function? If so, well done, you really had me for a while.



The Four Yorkshiremen was not actually a Monty Python sketch.  Get it 
right, lad :)


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: Python Iterables struggling using map() built-in

2014-12-09 Thread Roy Smith
In article 54878f8a$0$13010$c3e8da3$54964...@news.astraweb.com,
 Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote:

 I really think you guys are trying too hard to make this function seem more
 complicated than it is. If you find it so hard to understand a simple
 function with four short lines, one wonders how you would possibly cope
 with real code.

Well, look at it this way.  You've got several folks here (I count 
Terry, Ned, Chris, and myself) all saying, We find this confusing, and 
you're saying, nobody should find this confusing.  I suppose one 
possible explanation for this dichotomy is that we're all incapable of 
dealing with real code.  Yeah, that must be it.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-08 Thread Steven D'Aprano
On Mon, 08 Dec 2014 11:35:36 +1100, Chris Angelico wrote:

 On Mon, Dec 8, 2014 at 11:27 AM, Roy Smith r...@panix.com wrote:
 Although, to be honest, I'm wondering if this is more straight-forward
 (also not tested):

 def myzip37(*args):
 if not args:
 return
 iters = list(map(iter, args))
 
 Yes, I prefer this too. It's explicit and clear that passing no
 arguments will yield no values.

The first version is explicit and clear too. I'm sorry to say this, but 
it is true: if you (generic you) don't recognise that

while iters:
...


skips the while block if iters is an empty list, then *you* have a 
problem, not the code. You're simply not fluent with the language. (Or 
you have a strange blind-spot, in which case you have my sympathy but 
that's your problem to deal with, not mine.) This is not an obscure 
corner of some rarely-used library that has a strange and weird API, it 
is a fundamental part of Python.

I *guarantee* that there are people who will bitch and moan about your 
example too, and insist that the only right way to write it is to be 
explicit that an empty tuple is falsey:

if not args == ():
return

and there will be some who are convinced that operator precedence is too 
implicit:

if not (args == ()):   # args != () is okay too
return


Dumbing down code is an anti-pattern, because there's always somebody 
just a little less fluent in the language who will complain that it isn't 
dumbed down enough.

Sometimes we have this meme that if you can't take in code at a glance 
and instantly understand it, it's bad code. But that meme is untrue, and 
we all know that it is untrue: real world code is often hard to 
understand because it can't be any simpler. Try understanding the code 
for a web server, or code that calculates the square root of a number, or 
code for importing a module.

Occasionally, very occasionally, we have a combination of programming 
language and algorithm which combines in such a way that you can actually 
implement a useful algorithm in a simple, elegant, minimalist way. As 
programmers, we all know how fecking rare this is: we start with an 
elegant five line function, and by the time we cover all the corners and 
fix the bugs it's twenty lines and you can't tell what it does any more 
without studying it for ten minutes. zip() is an exception, you can write 
zip() in Python beautifully. It breaks my heart that there are people who 
think that it is improved by adding unnecessary guard clauses to it.



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


Re: Python Iterables struggling using map() built-in

2014-12-08 Thread Chris Angelico
On Mon, Dec 8, 2014 at 8:40 PM, Steven D'Aprano st...@pearwood.info wrote:
 The first version is explicit and clear too. I'm sorry to say this, but
 it is true: if you (generic you) don't recognise that

 while iters:
 ...


 skips the while block if iters is an empty list, then *you* have a
 problem, not the code.

Of course it skips the body if iters starts out empty. The argument is
whether or not it makes sense to use this to mean if iters: while
True: because iters will never be changed. Is it abusing syntax or a
valid way to spell that condition?

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


Re: Python Iterables struggling using map() built-in

2014-12-08 Thread Roy Smith
In article 5485721c$0$2817$c3e8da3$76491...@news.astraweb.com,
 Steven D'Aprano st...@pearwood.info wrote:

 On Mon, 08 Dec 2014 11:35:36 +1100, Chris Angelico wrote:
 
  On Mon, Dec 8, 2014 at 11:27 AM, Roy Smith r...@panix.com wrote:
  Although, to be honest, I'm wondering if this is more straight-forward
  (also not tested):
 
  def myzip37(*args):
  if not args:
  return
  iters = list(map(iter, args))
  
  Yes, I prefer this too. It's explicit and clear that passing no
  arguments will yield no values.
 
 The first version is explicit and clear too. I'm sorry to say this, but 
 it is true: if you (generic you) don't recognise that
 
 while iters:
 ...
 
 
 skips the while block if iters is an empty list, then *you* have a 
 problem, not the code.

The problem is not that the while body is skipped if iters is falsey.  
Thats quite clear.

The problem is that the looping termination isn't actually controlled by 
the control variable being exhausted.  It's controlled by an exception 
getting thrown.  I'm OK with breaking out of a loop by throwing an 
exception, but if that's what you're going to do, then make it clear 
that's the case by doing while 1 or while True.  Those screams out, 
Hey, look at me, I'm an infinite loop, which is your clue that there's 
something else going on.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-08 Thread Steven D'Aprano
Roy Smith wrote:

 Chris Angelico wrote:
  I'm actually glad PEP 479 will break this kind of code. Gives a good
  excuse for rewriting it to be more readable.
 
 Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote:
 What kind of code is that? Short, simple, Pythonic and elegant? :-)
 
 Here's the code again, with indentation fixed:
 
 
 def myzip(*args):
 iters = map(iter, args)
 while iters:
 res = [next(i) for i in iters]
 yield tuple(res)
 
 Ugh.  When I see while foo, my brain says, OK, you're about to see a
 loop which is controlled by the value of foo being changed inside the
 loop.

Yes. Me too. 99% of the time when you see while foo, that's what you'll
get, so it's the safe assumption. But it's only an assumption, not a
requirement. When you read a bit more of the code and see that iters isn't
being modified, your reaction ought to be closer oh wow, that's neat
than oh noes it's different from what I expected.

while foo is logically equivalent to if foo: while foo:. The if is
completely redundant.


 That's not at all what's happening here, so my brain runs into a 
 wall.

I hope you are exaggerating for effect, because if you genuinely mean that
reading that code causes major mental trauma (perhaps the equivalent of a
mental BSOD) then you've probably picked the wrong industry to be working
in. Imagine how you would cope reading genuinely obfuscated code. You would
probably have a nervous breakdown :-)

It's okay to read code which forces you to reevaluate your initial
assumption about the code. People, especially (allegedly) smart people like
programmers, are intelligent and flexible. If you can't do that, you're
going to hate Python:

- Python has no repeat N times loop, we have to use for i in range(...)
instead, so seeing a for-loop doesn't necessarily mean that the loop
variable will be used. Sometimes it isn't.


- I cannot count the number of times I've read, or written, a method that
doesn't use self, but doesn't bother to declare it as a staticmethod.


- The official way to get a single arbitrary value from a set without
removing it is:

for value in the_set:
return value

GvR recently gave an example of how to process a single element in a
possibly-empty iterator:

for x in it:
print(x)
break
else:
print('nothing')


so there are two examples of using a for-loop to *not* loop over something.


- Ducktyping. Just because some code is using a goose, doesn't mean that a
goose is required. Perhaps a duck is required but a goose is close enough.


 Next problem, what the heck is res?  We're not back in the punch-card
 days.  We don't have to abbreviate variable names to save columns.

*shrug* I didn't pick the name. But res is a standard abbreviation
for result or resource, and from context it clearly should be result.


[...]
 I think this function makes a good test to separate the masters from the
 apprentices.
 
 The goal of good code is NOT to separate the masters from the
 apprentices.  The goal of good code is to be correct 

So far I agree.


 and easy to 
 understand by the next guy who comes along to maintain it.

No. That is *one secondary goal*. Efficiency is another secondary goal.
Sometimes education is even more important, and in this specific case the
function is being used to teach people.

Another secondary goal ought to be beauty and elegance over ugliness. It's
not often a beautiful function actually is good enough for production use.
It's usually surrounded by an inelegant if not downright ugly pile of code
testing arguments, checking for error conditions, handling corner cases,
etc. That ugliness can obfuscate the underlying algorithm and make the
function harder to understand. If it isn't *necessary*, take it out.

Perfection is achieved, not when there is nothing more to add, 
but when there is nothing left to take away.

While ease of maintenance is an important goal, think about what we are
discussing. zip() is a primitive function. Once you have decided on the
public API, it will probably never need any maintenance. It's not like the
requirements will change -- yeah, we used to want to zip the items
together, but now we need to add a 9% superannuation surcharge to them
first.

The beauty of this code is that it is so simple that it cannot fail to be
bug-free. (Am I wrong? Have I missed a corner-case?) There's no unnecessary
code in the function that can hide bugs and obfuscate what it does.

And it is so simple that it's hard to see anything written in pure Python
being more efficient. So as far as maintenance goes, that's irrelevant.
That function is simply finished, done, complete. Anything you do to
improve it can only make it slower, buggier, or uglier.

(And that's a rare and beautiful thing in code.)


 If you can read this function and instantly tell how it works, that it is
 bug-free and duplicates the behaviour of the built-in zip(), you're
 probably Raymond Hettinger. If 

Re: Python Iterables struggling using map() built-in

2014-12-08 Thread Terry Reedy

On 12/8/2014 9:50 PM, Steven D'Aprano wrote:

Roy Smith wrote:


Chris Angelico wrote:



def myzip(*args):
 iters = map(iter, args)
 while iters:
 res = [next(i) for i in iters]
 yield tuple(res)


Ugh.  When I see while foo, my brain says, OK, you're about to see a
loop which is controlled by the value of foo being changed inside the
loop.


Yes. Me too. 99% of the time when you see while foo, that's what you'll
get, so it's the safe assumption. But it's only an assumption, not a
requirement. When you read a bit more of the code and see that iters isn't
being modified, your reaction ought to be closer oh wow, that's neat


To me it is a code smell.  iters is empty if and only if args is empty. 
 If args is empty, iters should not be created.


if args:
  iters = ...
  while True
... (return on exception)

makes the logic clear.

--
Terry Jan Reedy

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


Re: Python Iterables struggling using map() built-in

2014-12-08 Thread Steven D'Aprano
On Tue, 09 Dec 2014 00:03:33 -0500, Terry Reedy wrote:

 On 12/8/2014 9:50 PM, Steven D'Aprano wrote:
 Roy Smith wrote:

 Chris Angelico wrote:
 
 def myzip(*args):
  iters = map(iter, args)
  while iters:
  res = [next(i) for i in iters]
  yield tuple(res)

 Ugh.  When I see while foo, my brain says, OK, you're about to see
 a loop which is controlled by the value of foo being changed inside
 the loop.

 Yes. Me too. 99% of the time when you see while foo, that's what
 you'll get, so it's the safe assumption. But it's only an assumption,
 not a requirement. When you read a bit more of the code and see that
 iters isn't being modified, your reaction ought to be closer oh wow,
 that's neat
 
 To me it is a code smell.  iters is empty if and only if args is empty.
   If args is empty, iters should not be created.
 
 if args:
iters = ...
while True
  ... (return on exception)
 
 makes the logic clear.

When you understand why this body of code is inelegant and ugly, you 
should understand why the above is inelegant and ugly:

def double(x):
if x != 0:
x *= 2
return x




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


Python Iterables struggling using map() built-in

2014-12-07 Thread Ivan Evstegneev
Hello everyone, 

 

I'm currently in the process of self-study journey, so I have some questions
arisen from time to time.

Today I would like to talk about iterables and iterators,(ask for your help
actually ^_^).

 

Before I'll continue, just wanted to draw your attention  to the fact that I
did some RTFM before posting.

Links:

1.   map() built-in definitions:

https://docs.python.org/3/library/functions.html#map -for Python 3.X

https://docs.python.org/2.6/library/functions.html#map - for Python 2.6.X

 

2.   Glossary definitions of iterable  and iterator:

https://docs.python.org/3/glossary.html?highlight=glossary

 

3.   Iterator Types:

https://docs.python.org/2/library/stdtypes.html#typeiter

 

4.   iter() definition:

https://docs.python.org/2/library/functions.html#iter

 

 

5.   Some StackOverflow links, related to the topic:

http://stackoverflow.com/questions/13054057/confused-with-python-lists-are-t
hey-or-are-they-not-iterators

http://stackoverflow.com/questions/9884132/understanding-pythons-iterator-it
erable-and-iteration-protocols-what-exact

http://stackoverflow.com/questions/19523563/python-typeerror-int-object-is-n
ot-iterable

http://stackoverflow.com/questions/538346/iterating-over-a-string

 

6.   And of course, re-read  couple of times  a relevant parts of the
book ('Learning Python by Mark Lutz).

 

But the questions  still persist, maybe because those examples look too
esoteric though.

 

Another warning: Despite all my attempts to make my questions as short as
possible it still looks huge. My apologies. 

 

 

 

The problem:

 

Here is the book's example:

 

Consider the following clever alternative coding for this chapter's zip
emulation examples, 

adapted from one in Python's manuals at the time I wrote these words:

 

def myzip(*args):

iters = map(iter, args)

while iters:

res = [next(i) for i in iters]

yield tuple(res)

 

Because this code uses iter and next, it works on any type of iterable. Note
that there

is no reason to catch the StopIteration raised by the next(it) inside the
comprehension

here when any one of the arguments' iterators is exhausted-allowing it to
pass ends

this generator function and has the same effect that a return statement
would. The

while iters: suffices to loop if at least one argument is passed, and avoids
an infinite

loop otherwise (the list comprehension would always return an empty list).

 

This code works fine in Python 2.X as is:

 

 list(myzip('abc', 'lmnop'))

[('a', 'l'), ('b', 'm'), ('c', 'n')]

 

But it falls into an infinite loop and fails in Python 3.X, because the 3.X
map returns a

one-shot iterable object instead of a list as in 2.X. In 3.X, as soon as
we've run the list

comprehension inside the loop once, iters will be exhausted but still True
(and res will

be []) forever. To make this work in 3.X, we need to use the list built-in
function to

create an object that can support multiple iterations:

 

def myzip(*args):

iters = list(map(iter, args)) # Allow multiple scans

...rest as is...

 

Run this on your own to trace its operation. The lesson here: wrapping map
calls in

list calls in 3.X is not just for display!

 

*END OF THE BOOK EXAMPLE

 

 

 

According to the book , in order to get things work properly in Python 3.X,
I should write this code:

 

 def myzip(*args):

iters = list(map(iter, args))

while iters:

res = [next(i) for i in iters]

yield tuple(res)

 

And all seemed to be clear here, but, when I tried to run this thing:

 

 k= myzip(1, 2, 3, 4)

 next(k)

 

I got this result: 

 

Traceback (most recent call last):

  File pyshell#73, line 1, in module

next(k)

  File pyshell#65, line 2, in myzip

iters = list(map(iter, args))

TypeError: 'int' object is not iterable

 

Finding the problem:

 

I started to investigate further in order to catch the bug:

 

What I've tried?

 

1.  L = [1, 2, 3, 4]

 

 iter(L) is L

False ---  According to the theory it's OK, because list doesn't have
self iterators.

 

 k = iter(L)

 

print(k)

list_iterator object at 0x03233F90

 

 next(k)

1

 

 print(list(map(iter, L)))

Traceback (most recent call last):

  File pyshell#88, line 1, in module

print(list(map(iter, L)))

TypeError: 'int' object is not iterable

 

2. I went to strings:

 

 S = 'spam'

 iter(S) is S

False  ---Same about strings 

 

 string = iter(S)

 

 string

str_iterator object at 0x02E24F30

 

 next(string)

's'

 

and so on.

 

And then just tried this one:

 

 print(list(map(iter, S)))

[str_iterator object at 0x02E24FF0, 

str_iterator object at 0x02E24CF0, 

str_iterator object at 0x02E24E10,

str_iterator object at 0x02E24DF0] - At this moment   Wrecking
Ball song played in my head %)))

 

 

 k = list(map(iter,S))

 next(k[0])

's'

 next(k[0])

Traceback (most recent call 

Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Sun, Dec 7, 2014 at 3:44 AM, Ivan Evstegneev webmailgro...@gmail.com wrote:
 (quoting from the book)
 Because this code uses iter and next, it works on any type of iterable. Note
 that there
 is no reason to catch the StopIteration raised by the next(it) inside the
 comprehension
 here when any one of the arguments’ iterators is exhausted—allowing it to
 pass ends
 this generator function and has the same effect that a return statement
 would.

I'd just like to point out something here that's about to change. In
future versions of Python (starting with 3.5 with a governing
directive, and always happening in 3.7), it *will* be important to
catch the StopIteration. You can read more about it in PEP 479:

https://www.python.org/dev/peps/pep-0479/

By the way, your email would have been far better sent as plain text.
It's a bit messy here.

 k= myzip(1, 2, 3, 4)

 next(k)



 I got this result:



 Traceback (most recent call last):

   File pyshell#73, line 1, in module

 next(k)

   File pyshell#65, line 2, in myzip

 iters = list(map(iter, args))

 TypeError: 'int' object is not iterable

We'll get back to this later.

 1.  L = [1, 2, 3, 4]
 iter(L) is L

 False ---  According to the “theory” it’s OK, because list doesn’t have
 self iterators.

That's because L is a list, not an iterator. A list is iterable, but
since you can iterate over it more than once, it returns a separate
list_iterator whenever you call iter() on it.

 S = 'spam'

 iter(S) is S

 False  ---Same about strings

Again, strings are iterable, they are not iterators.

L = [1, 2, 3, 4]

 k = iter(L)

That's calling iter on L

  list(map(iter, L))

That's calling iter on *every element of* L.

 2. Why strings are allowed “to become” an iterators(self-iterators)?
 Maybe because of files(reading from file) ?

Because a string is a sequence of characters. You can iterate over a
string to work with its characters.

 Why the infinite loop would be there and why should list() to make it
 finite?  o_0

If you play around with it, you'll see why the infinite loop happens.
Actually, even simpler: try calling that zip function with no
arguments, and see what happens.

I would suggest not trying to rewrite zip(), but develop your own
iterator workings, in order to better understand what's going on. In
fact, you can completely avoid map(), using only a longhand form of
generator expression; and if you do that, you can easily switch in a
list comprehension and see exactly what it's doing.

But regardless of the above, I suggest reading through the section
Explanation of generators, iterators, and StopIteration in PEP 479.
It explains some important concepts which are best kept separate in
your head, as conflating them will only confuse.

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


RE: Python Iterables struggling using map() built-in

2014-12-07 Thread Ivan Evstegneev
Awesome Ned,

Believe it or not, but I was browsing web for the answer about a half an
hour ago.

Guess what? I found your web page with the explanations you provided there.
))) 

Finally, I was ready to send this question to you directly, cause I didn't
know that you subscribed to this mailing list too. ^_^

But, you was a bit faster.  What a surprise. ))) ))) )))

Thanks a lot for your answer.

Ivan.
  

-Original Message-
From: Python-list
[mailto:python-list-bounces+webmailgroups=gmail@python.org] On Behalf Of
Ned Batchelder
Sent: Sunday, December 7, 2014 17:29
To: python-list@python.org
Subject: Re: Python Iterables struggling using map() built-in

On 12/6/14 11:44 AM, Ivan Evstegneev wrote:
 And as I've promised the question section:

 1.What actually map() trying to do in Python 3.X?

 I mean, why is this works fine:

L = [1, 2, 3, 4]

 k = iter(L)

 next(k)

 1

 and so on.

 But not this:

   list(map(iter, L))

 Traceback (most recent call last):

 File pyshell#88, line 1, in module

   print(list(map(iter, L)))

 TypeError: 'int' object is not iterable

Let's unpack the code.  You are running:

 map(iter, L)

which is equivalent to:

 map(iter, [1, 2, 3, 4])

which executes:

 iter(1), iter(2), iter(3), iter(4)

If you try iter(1), you get the error you are seeing.  Integers are not
iterable.  What values would it produce?

You ask what this is doing in Python 3, but it happens in any Python
version, because integers are not iterable.


 2.Why strings are allowed to become an iterators(self-iterators)?
 Maybe because of files(reading from file) ?

 I mean why, is this possible:

 print(list(map(iter, S)))

 [str_iterator object at 0x02E24FF0,

 str_iterator object at 0x02E24CF0,

 str_iterator object at 0x02E24E10,

 str_iterator object at 0x02E24DF0]


This is a confusing thing in Python: strings are iterable, they produce a
sequence of 1-character strings:

  list(hello)
 ['h', 'e', 'l', 'l', 'o']

This isn't because of reading from files.  Open files are iterable, they
produce a sequence of strings, one for each line in the file.  This is why
you can do:

 for line in file:
 process(line)

Many times, it would be more convenient if strings were not iterable, but
they are, and you need to keep it in mind when writing general-purpose
iteration.


 3.The last question

 Author says:

  /But it falls into an infinite loop and fails in Python 3.X, because
 the 3.X map returns a /

 /one-shot iterable object instead of a list as in 2.X. In 3.X, as soon
 as we've run the list /

 /comprehension inside the loop once, iters will be exhausted but still
 True/. /To make this /

 /work in 3.X, we need to use the list built-in function to create an
 object that can support /

 /multiple iterations. /(Like:Wat?! ^_^)//

 Why the infinite loop would be there and why should list() to make it
 finite?  o_0


OK, let's go slowly.  There are a few foundational concepts to get under 
our belt.

*** CONCEPTS

1. An iterable is an object that you can pass to iter() to get an 
iterator.

2. An iterator is an object that can provide you with values one after 
the other, by using next().  next() will either return the next value, 
or raise a StopIteration exception, indicating that there are no more 
values.

3. The only operation supported on iterators is next().  You cannot 
start them over, you cannot ask if there will be more values, you cannot 
find out how many values there will be, you can't ask what the last 
value was, etc.  By supporting only one operation, they allow the 
broadest possible set of implementations.

4. You can ask an iterator for an iterator, and it will return itself. 
That is:  iter(some_iterator) is some_iterator.

5. The for NAME in EXPR construct is equivalent to this:

 expr_iter = iter(EXPR)
 try:
 while True:
 NAME = next(expr_iter)
 ..DO_SOMETHING..
 except StopIteration:
 pass

6. In Python 2, map() produces a list of values. Lists are iterable. In 
Python 3, map() produces a map object, which is an iterator.

*** PYTHON 2 EXECUTION

OK, now, here is the code in question:

 1. def myzip(*args):
 2. iters = map(iter, args)
 3. while iters:
 4. res = [next(i) for i in iters]
 5. yield tuple(res)

Let's cover the Python 2 execution first.  At line 2, map produces a 
list of iterators.  Line 3 will loop forever.  Nothing ever changes the 
list.  In fact, this is a very confusing part of this code.  The code 
should have said while True here, because it would work exactly the same.

At line 4, we loop over our list of iterators, and pull the next value 
from each one.  HERE'S THE IMPORTANT PART: because iters is a list, it 
is an iterable, and the for loop on this line will make a new list 
iterator to visit each iterator in turn.  Every time this line is 
executed, every iterator in the list will be next'ed.

Now res

Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Mon, Dec 8, 2014 at 2:29 AM, Ned Batchelder n...@nedbatchelder.com wrote:
 3. The only operation supported on iterators is next().  You cannot start
 them over, you cannot ask if there will be more values, you cannot find out
 how many values there will be, you can't ask what the last value was, etc.
 By supporting only one operation, they allow the broadest possible set of
 implementations.

Technically, this is one of only two operations *guaranteed to be*
supported on iterators (the other being that `iter(iterator) is
iterator`). There are plenty of iterators which do more than that, but
all iterators are guaranteed to support next() and nothing more. (For
instance, a generator object is an iterator, and it supports a lot
more operations.)

 5. The for NAME in EXPR construct is equivalent to this:

 expr_iter = iter(EXPR)
 try:
 while True:
 NAME = next(expr_iter)
 ..DO_SOMETHING..
 except StopIteration:
 pass

Small subtlety: The body of the for block is _not_ guarded by the
try/except. It's more like this:

expr_iter = iter(EXPR)
while True:
try: NAME = next(expr_iter)
except StopIteration: break
..DO_SOMETHING..

 NOTE: THIS EXAMPLE IS HORRIBLE.  This code is crazy-confusing, and should
 never have been used as an example of iteration. It layers at least three
 iterations on top of each other, making it very difficult to see what is
 going on.  It uses while iters where while True would do exactly the
 same thing (iters will never be false).

There's one way for iters to be false, and that's if you give it no
arguments at all. I've only just noticed this now, as I responded
earlier with a suggestion to try passing it no args, which won't work
because of that (or at least, won't work in Py2; in Py3, iters will
indeed never be false, unless you use list() to coalesce the map).
This is something which definitely ought to have been given a comment.
Or, more usefully, a guarding 'if' before the loop, rather than
needlessly checking at every iteration - if you want an infinite loop
guarded by a precondition, write it as such so the subsequent reader
can see that that's your intention.

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Ian Kelly
On Dec 7, 2014 8:31 AM, Ned Batchelder n...@nedbatchelder.com wrote:
 NOTE: THIS EXAMPLE IS HORRIBLE.  This code is crazy-confusing, and should
never have been used as an example of iteration. It layers at least three
iterations on top of each other, making it very difficult to see what is
going on.  It uses while iters where while True would do exactly the
same thing (iters will never be false).

That's not quite correct; the while iters actually guards against the
case where the passed args are empty. With no sub-iterators, no
StopIteration would ever be raised, and the result would be an infinite
generator of empty tuples. The while iters makes it return immediately
instead.

So it seems this example is even more confusing than you thought.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Mon, Dec 8, 2014 at 5:27 AM, Ian Kelly ian.g.ke...@gmail.com wrote:
 On Dec 7, 2014 8:31 AM, Ned Batchelder n...@nedbatchelder.com wrote:
 NOTE: THIS EXAMPLE IS HORRIBLE.  This code is crazy-confusing, and should
 never have been used as an example of iteration. It layers at least three
 iterations on top of each other, making it very difficult to see what is
 going on.  It uses while iters where while True would do exactly the
 same thing (iters will never be false).

 That's not quite correct; the while iters actually guards against the case
 where the passed args are empty. With no sub-iterators, no StopIteration
 would ever be raised, and the result would be an infinite generator of empty
 tuples. The while iters makes it return immediately instead.

 So it seems this example is even more confusing than you thought.

I'm actually glad PEP 479 will break this kind of code. Gives a good
excuse for rewriting it to be more readable.

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Steven D'Aprano
Chris Angelico wrote:

 On Mon, Dec 8, 2014 at 5:27 AM, Ian Kelly ian.g.ke...@gmail.com wrote:
 On Dec 7, 2014 8:31 AM, Ned Batchelder n...@nedbatchelder.com wrote:
 NOTE: THIS EXAMPLE IS HORRIBLE.  This code is crazy-confusing, and
 should never have been used as an example of iteration. It layers at
 least three iterations on top of each other, making it very difficult to
 see what is
 going on.  It uses while iters where while True would do exactly the
 same thing (iters will never be false).

 That's not quite correct; the while iters actually guards against the
 case where the passed args are empty. With no sub-iterators, no
 StopIteration would ever be raised, and the result would be an infinite
 generator of empty tuples. The while iters makes it return immediately
 instead.

 So it seems this example is even more confusing than you thought.
 
 I'm actually glad PEP 479 will break this kind of code. Gives a good
 excuse for rewriting it to be more readable.

What kind of code is that? Short, simple, Pythonic and elegant? :-)

Here's the code again, with indentation fixed:


def myzip(*args):
iters = map(iter, args)
while iters:
res = [next(i) for i in iters]
yield tuple(res)


That is *beautiful code*. It's written for Python 2, where map returns a
list, so the while iters line is morally equivalent to:

while iters != [] and True


It would be even more beautiful if we get rid of the unnecessary temporary
variable:

def myzip(*args):
iters = map(iter, args)
while iters:
yield tuple([next(i) for i in iters])


I think this function makes a good test to separate the masters from the
apprentices. No offence intended to Ned, who is a master, anyone can have a
bad day or a blind spot. 

If you can read this function and instantly tell how it works, that it is
bug-free and duplicates the behaviour of the built-in zip(), you're
probably Raymond Hettinger. If you can tell what it does but you have to
think about it for a minute or two before you understand why it works, you
can call yourself a Python master. If you have to sit down with the
interactive interpreter and experiment for a bit to understand it, you're
doing pretty well.

I do not believe that good code must be obviously right. It's okay for code
to be subtly right. Either is better than complicated code which contains
no obvious bugs.

How would we re-write this to work in the future Python 3.7? Unless I have
missed something, I think we could write it like this:

def myzip37(*args):
iters = list(map(iter, args))
while iters:
try:
yield tuple([next(i) for i in iters])
except StopIteration:
return


which I guess is not too horrible. If Python had never supported the current
behaviour, I'd probably be happy with this. But having seen how elegant
generators *can* be, the post-PEP 479 version will always look bloated and
clumsy to me, like Garfield next to a cheetah.




-- 
Steven

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Mon, Dec 8, 2014 at 10:33 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 How would we re-write this to work in the future Python 3.7? Unless I have
 missed something, I think we could write it like this:

 def myzip37(*args):
 iters = list(map(iter, args))
 while iters:
 try:
 yield tuple([next(i) for i in iters])
 except StopIteration:
 return

 which I guess is not too horrible.

It's not horrible, and there are other ways it could be written too,
which also aren't horrible. Yes, it's not quite as short as the other
version; but more importantly, it's explicit about how StopIteration
affects it. It's clear that this exception, if raised by _any_ of the
iterators (even after consuming values from some of them, perhaps),
will silently terminate the generator.

The current behaviour favours a handful of cases like this, although
personally I think the termination of zip() is simply this is what
happens if we have no code here, so let's document it rather than
being something inherently ideal; the new behaviour favours the
debugging of many obscure cases and some less-obscure ones as well.
Most importantly, if you run the old version of myzip on Python 3.7,
you'll get an immediate and noisy RuntimeError when it terminates, and
you'll know exactly where to go fix stuff; if you run a buggy
generator on Python 3.4, you simply see no more results, without any
explanation of why. I'd rather debug the RuntimeError that can be
easily and trivially fixed in 99% of cases.

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Roy Smith
Chris Angelico wrote:
  I'm actually glad PEP 479 will break this kind of code. Gives a good
  excuse for rewriting it to be more readable.

Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote:
 What kind of code is that? Short, simple, Pythonic and elegant? :-)
 
 Here's the code again, with indentation fixed:
 
 
 def myzip(*args):
 iters = map(iter, args)
 while iters:
 res = [next(i) for i in iters]
 yield tuple(res)

Ugh.  When I see while foo, my brain says, OK, you're about to see a 
loop which is controlled by the value of foo being changed inside the 
loop.  That's not at all what's happening here, so my brain runs into a 
wall.

Next problem, what the heck is res?  We're not back in the punch-card 
days.  We don't have to abbreviate variable names to save columns.  
Variable names are supposed to describe what they hold, and thus help 
you understand the code.  I have no idea what res is supposed to be.  
Residue?  Result?  Rest_of_items?  Response?  None of these make much 
sense here, so I'm just left befuddled.

 It would be even more beautiful if we get rid of the unnecessary temporary
 variable:
 
 def myzip(*args):
 iters = map(iter, args)
 while iters:
 yield tuple([next(i) for i in iters])

Well, that's one way to solve the mystery of what res means, but it 
doesn't actually make it easier to understand.

 I think this function makes a good test to separate the masters from the
 apprentices.

The goal of good code is NOT to separate the masters from the 
apprentices.  The goal of good code is to be correct and easy to 
understand by the next guy who comes along to maintain it.
 
 If you can read this function and instantly tell how it works, that it is
 bug-free and duplicates the behaviour of the built-in zip(), you're
 probably Raymond Hettinger. If you can tell what it does but you have to
 think about it for a minute or two before you understand why it works, you
 can call yourself a Python master. If you have to sit down with the
 interactive interpreter and experiment for a bit to understand it, you're
 doing pretty well.

That pretty much is the point I'm trying to make.  If the code is so 
complicated that masters can only understand it after a couple of 
minutes of thought, and those of us who are just doing pretty well 
need to sit down and puzzle it out in the REPL, then it's too 
complicated for most people to understand.  KISS beats elegant.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Roy Smith
In article mailman.16689.1417996247.18130.python-l...@python.org,
 Chris Angelico ros...@gmail.com wrote:

 On Mon, Dec 8, 2014 at 10:33 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
  How would we re-write this to work in the future Python 3.7? Unless I have
  missed something, I think we could write it like this:
 
  def myzip37(*args):
  iters = list(map(iter, args))
  while iters:
  try:
  yield tuple([next(i) for i in iters])
  except StopIteration:
  return

I'm still not liking this use of while.  Yes, of course, it handles the 
special case of no arguments, but I'd be in-your-face about that (not 
tested):

def myzip37(*args):
iters = list(map(iter, args))
if not iters:
return None
while True:
try:
yield tuple([next(i) for i in iters])
except StopIteration:
return

This makes it really obvious that there's something going on inside the 
loop other than exhausting the control variable to cause it to exit.

Although, to be honest, I'm wondering if this is more straight-forward 
(also not tested):

def myzip37(*args):
if not args:
return
iters = list(map(iter, args))
while True:
try:
yield tuple(map(next, iters))
except StopIteration:
return
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Mon, Dec 8, 2014 at 11:12 AM, Roy Smith r...@panix.com wrote:
 Ugh.  When I see while foo, my brain says, OK, you're about to see a
 loop which is controlled by the value of foo being changed inside the
 loop.  That's not at all what's happening here, so my brain runs into a
 wall.

I agree, with the caveat that this kind of thing makes a fine infinite loop:

while No exception raised:
# do stuff that can raise an exception
while Playing more games:
play_game()
if not still_playing: break
reset_game_board()

Nobody expects a string literal to actually become false inside the
loop. With a local name, yes, I would expect it to at least have a
chance of becoming false.

 Next problem, what the heck is res?  We're not back in the punch-card
 days.  We don't have to abbreviate variable names to save columns.
 Variable names are supposed to describe what they hold, and thus help
 you understand the code.  I have no idea what res is supposed to be.
 Residue?  Result?  Rest_of_items?  Response?  None of these make much
 sense here, so I'm just left befuddled.

I take it as result, which makes plenty of sense to me. It's the
thing that's about to be yielded. Given that there's not much else you
can say in a meta-function like zip(), I have no problem with that.
Here's a slightly different example:

def mark_last(it):
it = iter(it)
lastres = sentinel = object()
while more values coming:
res = next(it, sentinel)
if lastres is not sentinel: yield (lastres, res is sentinel)
if res is sentinel: return
lastres = res

Use of res for result and lastres to mean res as of the
previous iteration of the loop seems fine to me.

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Mon, Dec 8, 2014 at 11:27 AM, Roy Smith r...@panix.com wrote:
 Although, to be honest, I'm wondering if this is more straight-forward
 (also not tested):

 def myzip37(*args):
 if not args:
 return
 iters = list(map(iter, args))

Yes, I prefer this too. It's explicit and clear that passing no
arguments will yield no values.

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Terry Reedy

On 12/7/2014 7:12 PM, Roy Smith wrote:

Chris Angelico wrote:

I'm actually glad PEP 479 will break this kind of code. Gives a good
excuse for rewriting it to be more readable.


Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote:

What kind of code is that? Short, simple, Pythonic and elegant? :-)

Here's the code again, with indentation fixed:


def myzip(*args):
 iters = map(iter, args)
 while iters:
 res = [next(i) for i in iters]
 yield tuple(res)


Ugh.  When I see while foo, my brain says, OK, you're about to see a
loop which is controlled by the value of foo being changed inside the
loop.  That's not at all what's happening here, so my brain runs into a
wall.


I agree.  Too tricky.  The code should have been

def myzip(*args):
if args:
iters = map(iter, args)
while True:
res = [next(i) for i in iters]
yield tuple(res)

However, this 'beautiful' code has a trap.  If one gets rid of the 
seemingly unneeded temporary list res by telescoping the last two lines 
into a bit too much into


yield tuple(next(i) for i in iters)

we now have an infinite generator, because tuple() swallows the 
StopIteration raised as a side-effect of next calls.


def myzip(*args):
if args:
iters = map(iter, args)
while True:
try:
result = [next(i) for i in iters]
except StopIteration
return
yield tuple(res)

makes the side-effect dependence of stopping clearer.  Putting
 yield tuple([next(i) for i in iters])
in the try would also work.


--
Terry Jan Reedy

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Roy Smith
In article mailman.16690.1417998873.18130.python-l...@python.org,
 Chris Angelico ros...@gmail.com wrote:

  Next problem, what the heck is res?  We're not back in the punch-card
  days.  We don't have to abbreviate variable names to save columns.
  Variable names are supposed to describe what they hold, and thus help
  you understand the code.  I have no idea what res is supposed to be.
  Residue?  Result?  Rest_of_items?  Response?  None of these make much
  sense here, so I'm just left befuddled.
 
 I take it as result, which makes plenty of sense to me.

OK, so spell it out.  Three more keystrokes (well, plus another three 
when you use it on the next line).  And one of them is a vowel; they 
don't even cost much.  The next guy who has to read your code will thank 
you for it.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Chris Angelico
On Mon, Dec 8, 2014 at 11:45 AM, Roy Smith r...@panix.com wrote:
 I take it as result, which makes plenty of sense to me.

 OK, so spell it out.  Three more keystrokes (well, plus another three
 when you use it on the next line).  And one of them is a vowel; they
 don't even cost much.  The next guy who has to read your code will thank
 you for it.

Maybe. Personally, I don't mind the odd abbreviation; they keep the
code small enough to eyeball, rather than spelling everything out
everywhere. Using cur (or curr) for current, next for next,
prev for previous, as prefixes to a short word saying *what* they're
the current/next/previous of, is sufficiently obvious IMO to justify
the repeated use of the abbreviation. Why does Python have int and
str rather than integer and string? Or, worse,
arbitrary_precision_integer and unicode_codepoint_string? Common
words get shortened - it's a legit form of Huffman compression.

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Ned Batchelder

On 12/7/14 7:12 PM, Roy Smith wrote:

Chris Angelico wrote:

I'm actually glad PEP 479 will break this kind of code. Gives a good
excuse for rewriting it to be more readable.


Steven D'Aprano steve+comp.lang.pyt...@pearwood.info wrote:

What kind of code is that? Short, simple, Pythonic and elegant? :-)

Here's the code again, with indentation fixed:


def myzip(*args):
 iters = map(iter, args)
 while iters:
 res = [next(i) for i in iters]
 yield tuple(res)


Ugh.  When I see while foo, my brain says, OK, you're about to see a
loop which is controlled by the value of foo being changed inside the
loop.  That's not at all what's happening here, so my brain runs into a
wall.

Next problem, what the heck is res?  We're not back in the punch-card
days.  We don't have to abbreviate variable names to save columns.
Variable names are supposed to describe what they hold, and thus help
you understand the code.  I have no idea what res is supposed to be.
Residue?  Result?  Rest_of_items?  Response?  None of these make much
sense here, so I'm just left befuddled.


It would be even more beautiful if we get rid of the unnecessary temporary
variable:

def myzip(*args):
 iters = map(iter, args)
 while iters:
 yield tuple([next(i) for i in iters])


Well, that's one way to solve the mystery of what res means, but it
doesn't actually make it easier to understand.


I think this function makes a good test to separate the masters from the
apprentices.


The goal of good code is NOT to separate the masters from the
apprentices.  The goal of good code is to be correct and easy to
understand by the next guy who comes along to maintain it.


If you can read this function and instantly tell how it works, that it is
bug-free and duplicates the behaviour of the built-in zip(), you're
probably Raymond Hettinger. If you can tell what it does but you have to
think about it for a minute or two before you understand why it works, you
can call yourself a Python master. If you have to sit down with the
interactive interpreter and experiment for a bit to understand it, you're
doing pretty well.


That pretty much is the point I'm trying to make.  If the code is so
complicated that masters can only understand it after a couple of
minutes of thought, and those of us who are just doing pretty well
need to sit down and puzzle it out in the REPL, then it's too
complicated for most people to understand.  KISS beats elegant.



Now that I understand all the intricacies (thanks everyone!), this is 
how I would write it:


def zip(*args):
if not args:
return
iters = list(map(iter, args))
while True:
try:
result = [next(it) for it in iters]
except StopIteration:
return
yield tuple(result)

The implicit use of StopIteration to end the entire generator is far too 
implicit for my taste.  This code expresses the intent much better.


And good call on not being able to use:

tuple(next(it) for it in iters)

Again, the tricky implicit hidden StopIterations are confusing.

One last tweak: why do we use map to make iters, but a list 
comprehension to make result?  OK, let's try this:


result = map(next, iters)

Oops, another infinite loop on Py3, right, because map is lazy, so the 
StopIteration doesn't happen until the tuple() call.  OK, try this:


result = list(map(next, iters))

Nope, still infinite because now list() consumes the StopIteration 
again. Ugh.


--
Ned Batchelder, http://nedbatchelder.com

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread MRAB

On 2014-12-08 01:00, Chris Angelico wrote:

On Mon, Dec 8, 2014 at 11:45 AM, Roy Smith r...@panix.com wrote:

I take it as result, which makes plenty of sense to me.


OK, so spell it out.  Three more keystrokes (well, plus another three
when you use it on the next line).  And one of them is a vowel; they
don't even cost much.  The next guy who has to read your code will thank
you for it.


Maybe. Personally, I don't mind the odd abbreviation; they keep the
code small enough to eyeball, rather than spelling everything out
everywhere. Using cur (or curr) for current, next for next,
prev for previous, as prefixes to a short word saying *what* they're
the current/next/previous of, is sufficiently obvious IMO to justify
the repeated use of the abbreviation. Why does Python have int and
str rather than integer and string? Or, worse,
arbitrary_precision_integer and unicode_codepoint_string? Common
words get shortened - it's a legit form of Huffman compression.


Not to mention len, def, iter, etc.
--
https://mail.python.org/mailman/listinfo/python-list


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Gregory Ewing

Steven D'Aprano wrote:

I do not believe that good code must be obviously right. It's okay for code
to be subtly right.


If you write code as subtly as you can, you're not
subtle enough to debug it...

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


Re: Python Iterables struggling using map() built-in

2014-12-07 Thread Gregory Ewing

Terry Reedy wrote:
However, this 'beautiful' code has a trap.  If one gets rid of the 
seemingly unneeded temporary list res by telescoping the last two lines 
into a bit too much into


yield tuple(next(i) for i in iters)

we now have an infinite generator, because tuple() swallows the 
StopIteration raised as a side-effect of next calls.


An excellent example of the kind of thing that PEP 479
is designed to catch! There couldn't be a better
advertisement for it. :-)

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