Reputation: 1516
I was wondering if code that is similar to the code sketched below would be legal in Python and safe to use:
# Example usage of a lambda which executes a statement and returns the changed operand
skip_first = lambda iterator: (next(iterator), iterator)[1]
Is it safe to use such a construct and expect the lambda's return value to be an iterator which starts on the original iterator's second element (if no exception is thrown)?
Or might it be legal for f.i. Cython to optimize away invoking next
for some reason (i.e., because it discards the resulting value because the order of execution in a tuple is not defined and it can figure out that we discard the first value either way?)
f = iter([0, 1, 2, 3, 4])
print(list(skip_first(f)))
Upvotes: 3
Views: 218
Reputation: 531075
Yes, the expressions in the expression list are guaranteed to be evaluated from left to right. From the Section 6.15 of the language documentation:
expression_list ::= expression ("," expression)* [","] starred_list ::= starred_item ("," starred_item)* [","] starred_expression ::= expression | (starred_item ",")* [starred_item] starred_item ::= assignment_expression | "*" or_expr
Except when part of a list or set display, an expression list containing at least one comma yields a tuple. The length of the tuple is the number of expressions in the list. The expressions are evaluated from left to right.
In your specific example, the call to next
cannot be optimized away, because it may (and does) have a side effect (namely, it modifies the iterator's internal state). Only in a language without mutable values can you eliminate a function call whose return value isn't used. If a function could mutate a value, you have to assume that it does and allow the call to occur. Otherwise, you are altering the semantics of the code as written.
Upvotes: 5