我应该使用什么而不是在Python中赋值in-expression?

时间:2009-10-03 10:33:34

标签: python

根据this page,不能使用像

这样的代码
if variable = something():
#do something with variable, whose value is the result of something() and is true

所以如果我想拥有以下代码结构:

if a = something():
#do something with a
elif a = somethingelse():
#...
#5 more elifs

其中something()函数是计算密集型的(我的意思是使用该函数然后再次执行它以便在第一个函数为真时无法为变量赋值),我应该写什么呢?蟒蛇?再添加7个变量而不是1?

9 个答案:

答案 0 :(得分:10)

我在2001年就遇到过这个问题 - 因为我从C语言中的参考算法转换为Python,它使用了大量的分配和测试,我很想为初稿保留类似的结构(然后重构后来一旦正确性得到很好的测试)。所以我在食谱中写了recipe(另见here),归结为......:

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

因此if / elif树可以变为:

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

DataHolder课程可以通过各种方式明确地进行修饰(并且在在线和书籍版本中都有所修饰),但这是它的要点,并且足以回答你的问题。

答案 1 :(得分:3)

你可以这样做:

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

或者,在函数内部,您可以在每个匹配的情况下使用return限制嵌套级别:

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

答案 2 :(得分:3)

另一种提供灵活性的替代方案:

# 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

或者你可以明确地比较函数:

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:
    # ...

如果某些函数接受参数,则可以使用lambda来保持函数范例:

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:
    # ...

答案 3 :(得分:2)

让自己成为一个简单的可调用对象,保存其返回值:

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

现在使用它:

# 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

打印:

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

答案 4 :(得分:1)

你可以使用这个memorize这样的装饰器 - 假设它们总是返回相同的值。请注意,您可以根据需要多次调用expensive_foo和expensive_bar,并且函数体只能执行一次

def memoize(f):
    mem = {}
    def inner(*args):
        if args not in mem:
            mem[args] = f(*args)
        return mem[args]
    return inner

@memoize
def expensive_foo():
    print "expensive_foo"
    return False

@memoize
def expensive_bar():
    print "expensive_bar"
    return True

if expensive_foo():
    a=expensive_foo()
    print "FOO"
elif expensive_bar():
    a=expensive_bar()
    print "BAR"

答案 5 :(得分:1)

我可能会遗漏一些内容,但您无法将顶级if语句中的每个分支都分解为单独的函数,创建一个 test 列表到动作元组并循环遍历它们?您应该能够应用此模式来模仿if (value=condition()) {} else if (value=other_condition()) {}样式逻辑。

这实际上是redglyph's response的扩展,可能会被压缩到iterator,一旦达到真值,就会提升StopIteration

#
# 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)
)

答案 6 :(得分:1)

我会解决这个问题,就像我解决其他一些棘手的流量控制问题一样:将棘手的位移到函数中,然后使用return来提前退出函数。像这样:

def do_correct_something():
    a = something()
    if a:
        # do something with a
        return a

    a = somethingelse()
    if a:
        # do something else with a
        return a

    # 5 more function calls, if statements, do somethings, and returns

    # then, at the very end:
    return None

a = do_correct_something()

我执行此操作的另一个主要的“流量控制棘手问题”是打破多个嵌套循环:

def find_in_3d_matrix(matrix, x):
    for plane in matrix:
        for row in plane:
            for item in row:
                if test_function(x, item):
                    return item
    return None

你也可以通过编写一个只迭代一次的for循环解决所述问题,并使用“break”进行早期退出,但我更喜欢函数返回版本。它不那么棘手,而且更加清晰; function-with-return是打破Python中多个循环的唯一干净方法。 (在每个for循环中放置“if break_flag: break”,并在想要中断时设置break_flag,不是恕我直言。)

答案 7 :(得分:0)

如果我们使用字符串,这是可能的 - 因为我们可以将字符串转换为列表并使用方法extends作为列表,逻辑上使内联将一个字符串附加到另一个字符串(以列表格式):

>>> my_str = list('xxx')
>>> if not my_str.extend(list('yyy')) and 'yyy' in ''.join(my_str):
...     print(True)
True

这里我们将if&#39;添加到原始字符串&#39;新数据并试图找到它。也许这很难看,但这是在表达式中的赋值:

if my_str += 'yyy' and 'yyy' in my_str:

答案 8 :(得分:-1)

从Python 3.8.0a1 +开始,我们可以使用assignment expression语法。

例如:

>>> if a := 0:
...     print('will not be printed')
... elif a := 1:
...     print('print value of a: %d, a should be 1' % a)
... else:
...     print('will not be printed')
...
print value of a: 1, a should be 1