When transliterating C, Perl &c to Python, one often misses idioms such as "if((x=foo())" or "while((x=foo())" -- but, it's easy to get them back, with one small utility class!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # In Python, you can't code "if x=foo():" -- assignment is a statement, thus
# you can't fit it into an expression, as needed for conditions of if and
# while statements, &c. No problem, if you just structure your code around
# this. But sometimes you're transliterating C, or Perl, or ..., and you'd
# like your transliteration to be structurally close to the original.
#
# No problem, again! One tiny, simple utility class makes it easy...:
class DataHolder:
def __init__(self, value=None): self.value = value
def set(self, value): self.value = value; return value
def get(self): return self.value
# optional but handy, if you use this a lot, either or both of:
setattr(__builtins__,'DataHolder',DataHolder)
setattr(__builtins__,'data',DataHolder())
# and now, assign-and-set to your heart's content: rather than Pythonic
while 1:
line = file.readline()
if not line: break
process(line)
# or better in modern Python, but quite far from C-like idioms:
for line in file.xreadlines():
process(line)
# you CAN have your C-like code-structure intact in transliteration:
while data.set(file.readline()):
process(data.get())
|
In Python, assignment is not an expression. In particular, therefore, you cannot assign the result that you are testing in an if, elif, while, and so on. This is most often OK -- you just structure your code so as to avoid the need to assign-while-testing. However, often you may be coding in Python the transliteration of existing code in C, Perl, or other languages that do support assignment-as-expression (for example, in a first Python version of an algorithm for which a 'reference implementation' is supplied, an algorithm taken from a book, and so on). In such cases, having the structure of your transliteration close to that of the code you're transcribing is often preferable. Fortunately, Python offers (by far!-) enough power to make it pretty trivial to satisfy this requirement.
We cannot redefine assignment, but we CAN have a method (or function) that saves its argument "somewhere" AND also returns that argument so it can be tested. The "somewhere" is most naturally an attribute of an object, so that a method is a more natural choice than a function. We might of course just retrieve the attribute directly (i.e., method .get is redundant), but it just looks nicer to have symmetry between data.set(...) and data.get().
Special-purpose solutions, such as the .xreadlines() method of file objects or the similar decorator-function in module xreadlines, are obviously preferable for the purposes for which they've been designed -- but sometimes even those can be seen as an undesired deviation from the structure of the algorithm being transliterated, and in any case they're not sufficiently general. On the other hand, 'data.set(whatever)' can be seen as little more than syntax sugar for 'data.value=whatever' _with_ the "added-value" of BEING acceptable as an expression, and therefore it's the "one obviously right way" to satisfy the requirement for a reasonably faithful transliteration -- the only (minor) difference being the syntax-sugar variation needed.
list comprehension abuse is an alternative. just for the record,
is a close Python transliteration of C's:
Pretty obscure, though.
Alternative: Function Attributes. Similar results can be obtained using function attributes instead of a class, since functions are first class objects in Python. One advantage being that it requires one less explict function call in the loop. For example:
On the other hand, the class approach has the advantage that it is possible to use it in a way that would handle nested loops properly.
You say:
"This is most often OK -- you just structure your code so as to avoid the need to assign-while-testing."
What is the correct python paradigm for this "pseudocode"?
if ($_ eq "keyword1") ... elsif ($_ eq "keyword2") ... elsif (/(some re)/) { $variable = $1; ...