Skip to content Skip to sidebar Skip to footer

What Should I Use Instead Of Assignment-in-an-expression In Python?

according to this page one can't use code like if variable = something(): #do something with variable, whose value is the result of something() and is true So in case I want to ha

Solution 1:

I had this problem years ago, in 2001 -- since I was transliterating to Python from a reference-algorithm in C which used assign-and-test heavily, I was keen to keep a similar structure for the first draft (then refactor later once correctness was well tested). So I wrote a recipe in the Cookbook (see also here), which boils down to...:

class DataHolder(object):
    def set(self, value): self.value = value; return value

so the if/elif tree can become:

dh = DataHolder()
if dh.set(something()):
  # do something with dh.value
elif dh.set(somethingelse()):
  # ...

the DataHolder class can clearly be embellished in various ways (and is so embellished in both the online and book versions), but this is the gist of it, and quite sufficient to answer your question.


Solution 2:

Another alternative which offers some flexibility:

# Functions to be tested (can be expanded):
tests = [something, somethingelse, yetsomethingelse, anotherfunction, another]
for i, f in enumerate(tests):
    a = f()
    if a:
        if i == 0:
            # do something with a
        elif 1 <= i <= 3:
            # do something else with a
        else:
            # ...
        break

Or you can explicitly compare to the function:

tests = [something, somethingelse, yetsomethingelse, anotherfunction, another]
for i, f in enumerate(tests):
    a = f()
    if a: break
if not a:
    # no result
elif f == something:
    # ...
elif f == somethingelse:
    # ...

If some of the functions take arguments, you can use lambda to keep the function paradigm:

tests = [lambda: something(args), somethingelse, lambda: something(otherargs)]
for i, f in enumerate(tests):
    a = f()
    if a: break
if not a:
    # no result
elif i == 0:
    # ...
elif i == 1:
    # ...

Solution 3:

You could do this:

a = something()
if a:
    #do something with a
else:
    a = somethingelse()
    if a:
        #...
    else:
        #5 more nested ifs

Or, inside a function you can limit the nesting level with a return in each matching case:

def f():
    a = something()
    if a:
        #do something with a
        return
    a = somethingelse()
    if a:
        #...
        return
    #5 more ifs

Solution 4:

Make yourself a simple callable object that saves its returned value:

class ConditionValue(object):
    def __call__(self, x):
        self.value = x
        return bool(x)

Now use it like this:

# example code
makelower = lambda c : c.isalpha() and c.lower()
add10 = lambda c : c.isdigit() and int(c) + 10

test = "ABC123.DEF456"
val = ConditionValue()
for t in test:
    if val(makelower(t)):
        print t, "is now lower case ->", val.value
    elif val(add10(t)):
        print t, "+10 ->", val.value
    else:
        print "unknown char", t

Prints:

A is now lower case -> a
B is now lower case -> b
C is now lower case -> c
1 +10 -> 11
2 +10 -> 12
3 +10 -> 13
unknown char .
D is now lower case -> d
E is now lower case -> e
F is now lower case -> f
4 +10 -> 14
5 +10 -> 15
6 +10 -> 16

Solution 5:

I might be missing something, but couldn't you factor each of the branches in your top-level if statement into separate functions, create a list of test to action tuples and loop over them? You should be able to apply this pattern to mimic if (value=condition()) {} else if (value=other_condition()) {} style logic.

This is really an extension of redglyph's response and can probably be compressed down to a iterator that raises StopIteration once it has hit a truth value.

#
# These are the "tests" from your original if statements. No
# changes should be necessary.
#
def something():
    print('doing something()')
    # expensive stuff here
def something_else():
    print('doing something_else()')
    # expensive stuff here too... but this returns True for some reason
    return True
def something_weird():
    print('doing something_weird()')
    # other expensive stuff

#
# Factor each branch of your if statement into a separate function.
# Each function will receive the output of the test if the test
# was selected.
#
def something_action(value):
    print("doing something's action")
def something_else_action(value):
    print("doing something_else's action")
def something_weird_action(value):
    print("doing something_weird's action")

#
# A simple iteration function that takes tuples of (test,action). The
# test is called. If it returns a truth value, then the value is passed
# onto the associated action and the iteration is stopped.
#
def do_actions(*action_tuples):
    for (test,action) in action_tuples:
        value = test()
        if value:
            return action(value)

#
# ... and here is how you would use it:
#
result = do_actions(
             (something, something_action),
             (something_else, something_else_action),
             (something_weird, something_weird_action)
)

Post a Comment for "What Should I Use Instead Of Assignment-in-an-expression In Python?"