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

时间:2022-03-30 13:46:27

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 have the following code structure:

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

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

where something() functions are compute-intensive (I mean that using the function and then doing it again to assign value to a variable in case the first one was true can't be done), what should I write instead in Python? Add 7 more variables instead of 1?

其中something()函数是计算密集型的(我的意思是使用该函数,然后再次将它赋值给变量,以防第一个函数无法完成),我应该在Python中编写什么?再添加7个变量而不是1个?

8 个解决方案

#1


10  

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

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

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

so the if/elif tree can become:

所以if / elif树可以成为:

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.

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

#2


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

#3


3  

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:

如果某些函数接受参数,则可以使用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:
    # ...

#4


2  

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

#5


1  

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.

我可能会遗漏一些东西,但是你不能将顶层if语句中的每个分支都分解为单独的函数,创建一个测试到操作元组的列表并循环它们吗?您应该能够应用此模式来模仿if(value = condition()){} else if(value = other_condition()){}样式逻辑。

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.

这实际上是redglyph响应的扩展,并且可能被压缩为迭代器,一旦达到真值就会引发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  

You could use a decorator like this memorise for those functions - assuming they always return the same value. Notice that you can call expensive_foo and expensive_bar as many times as you like and the function body only ever gets executed once

你可以使用这个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"

#7


1  

I would solve this the same way I solve several other problems of tricky flow control: move the tricky bit into a function, and then use return to cause an early exit from the function. Like so:

我会解决这个问题,就像我解决其他一些棘手的流控制问题一样:将棘手的位移到函数中,然后使用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()

The major other "tricky problem of flow control" for which I do this is breaking out of multiple nested loops:

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

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

You can also solve the stated question by writing a for loop that will iterate only once, and use "break" for the early exit, but I prefer the function-with-return version. It's less tricky, and clearer what is going on; and the function-with-return is the only clean way to break out of multiple loops in Python. (Putting "if break_flag: break" in each of the for loops, and setting break_flag when you want to break, is not IMHO clean.)

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

#8


0  

This is possible if we working with strings - because we can convert string to list and use method extends for list that logically make inline appending one string to another (in list format):

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

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

Here we inside if 'added to the original string' new data and trying to find it. Maybe this is ugly but this is assignment in an expression like:

在这里我们内部如果'添加到原始字符串'新数据并试图找到它。也许这很难看,但这是一个表达式中的赋值:

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

#1


10  

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

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

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

so the if/elif tree can become:

所以if / elif树可以成为:

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.

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

#2


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

#3


3  

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:

如果某些函数接受参数,则可以使用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:
    # ...

#4


2  

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

#5


1  

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.

我可能会遗漏一些东西,但是你不能将顶层if语句中的每个分支都分解为单独的函数,创建一个测试到操作元组的列表并循环它们吗?您应该能够应用此模式来模仿if(value = condition()){} else if(value = other_condition()){}样式逻辑。

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.

这实际上是redglyph响应的扩展,并且可能被压缩为迭代器,一旦达到真值就会引发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  

You could use a decorator like this memorise for those functions - assuming they always return the same value. Notice that you can call expensive_foo and expensive_bar as many times as you like and the function body only ever gets executed once

你可以使用这个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"

#7


1  

I would solve this the same way I solve several other problems of tricky flow control: move the tricky bit into a function, and then use return to cause an early exit from the function. Like so:

我会解决这个问题,就像我解决其他一些棘手的流控制问题一样:将棘手的位移到函数中,然后使用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()

The major other "tricky problem of flow control" for which I do this is breaking out of multiple nested loops:

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

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

You can also solve the stated question by writing a for loop that will iterate only once, and use "break" for the early exit, but I prefer the function-with-return version. It's less tricky, and clearer what is going on; and the function-with-return is the only clean way to break out of multiple loops in Python. (Putting "if break_flag: break" in each of the for loops, and setting break_flag when you want to break, is not IMHO clean.)

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

#8


0  

This is possible if we working with strings - because we can convert string to list and use method extends for list that logically make inline appending one string to another (in list format):

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

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

Here we inside if 'added to the original string' new data and trying to find it. Maybe this is ugly but this is assignment in an expression like:

在这里我们内部如果'添加到原始字符串'新数据并试图找到它。也许这很难看,但这是一个表达式中的赋值:

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