I'm not really a big Pythoner - but I found this syntax once, forgot where from, so I thought I'd document it:
If you use sys.stdout.write
instead of print
(the difference being, sys.stdout.write
takes arguments as a function, in parentheses - whereas print
doesn't), then for a one-liner, you can get away with inverting the order of the command and the for
, removing the semicolon, and enclosing the command in square brackets, i.e.:
python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
I have no idea how this syntax would be called in Python :)
These square brackets in one-liners are "list comprehensions"; note this in Python 2.7:
STR=abc
echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); print a"
Output:
<generator object <genexpr> at 0xb771461c>
So the command in round brackets/parenthesis is seen as a "generator object"; if we "iterate" through it by calling next()
- then the command inside the parenthesis will be executed (note the "abc" in the output):
echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); a.next() ; print a"
Output:
abc
<generator object <genexpr> at 0xb777b734>
If we now use square brackets - note that we don't need to call next()
to have the command execute, it executes immediately upon assignment; however, later inspection reveals that a
is None
:
echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; print a"
Output:
abc
[None]
This doesn't leave much information to look for - for the square brackets case - but I stumbled upon this page which I think explains:
Python Tips And Tricks – First Edition - Python Tutorials | Dream.In.Code:
If you recall, the standard format of a single line generator is a kind of one line 'for' loop inside brackets. This will produce a 'one-shot' iterable object which is an object you can iterate over in only one direction and which you can't re-use once you reach the end.
A 'list comprehension' looks almost the same as a regular one-line generator, except that the regular brackets - ( ) - are replaced by square brackets - [ ]. The major advantage of alist comprehension is that produces a 'list', rather than a 'one-shot' iterable object, so that you can go back and forth through it, add elements, sort, etc.
And indeed it is a list - it's just its first element becomes none as soon as it is executed:
echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin].__class__"
Output:
abc
<type 'list'>
echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin][0]"
Output:
abc
None
List comprehensions are otherwise documented in 5. Data Structures: 5.1.4. List Comprehensions — Python v2.7.4 documentation as "List comprehensions provide a concise way to create lists"; presumably, that's where the limited "executability" of lists comes into play in one-liners.
Well, hope I'm not terribly too off the mark here ...
And here is a one-liner command line with two non-nested for-loops; both enclosed within "list comprehension" square brackets:
echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; b=[sys.stdout.write(str(x)) for x in range(2)] ; print a ; print b"
Output:
abc
01[None]
[None, None]
Notice that the second "list" b
now has two elements, since its for loop explicitly ran twice; however, the result of sys.stdout.write()
in both cases was (apparently) None
.