Welcome, guest | Sign In | My Account | Store | Cart

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!

Python, 27 lines
 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.

3 comments

Alex Martelli (author) 21 years, 10 months ago  # | flag

list comprehension abuse is an alternative. just for the record,

if [ x for x in [ whatever() ] if cond(x) ] : process(x)

is a close Python transliteration of C's:

if( cond(x=whatever()) ) process(x);

Pretty obscure, though.

Martin Miller 21 years, 1 month ago  # | flag

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:

def TestResult(value):
    TestResult.theValue = value
    return value

while TestResult(file.readline()):
    process(TestResult.theValue)

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.

Charles Smith 11 years, 12 months ago  # | flag

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; ...

Created by Alex Martelli on Mon, 16 Jul 2001 (PSF)
Python recipes (4591)
Alex Martelli's recipes (27)
lsloan's faves (8)

Required Modules

  • (none specified)

Other Information and Tasks