繁体   English   中英

具有可选返回值的Python函数中的延迟存储和代码执行

[英]Lazy store and code execution in Python functions with optional return values

如果我有一个返回多个输出的函数,有时不需要这些输出,那么我发现用下划线“忽略”这些输出是一种惯例,例如

a, _ = fn_with_two_outputs()

直到现在,我还假定该值刚刚被删除,并且天真地希望编译器足够聪明以至于不执行与该值关联的任何代码。 但是在最近的调试会议中,我注意到该方法实际上使用输出创建了一个变量_ ,这浪费了内存,应该阻止使用下划线约定,不是吗?

我现在倾向于这种变体:

a = fn_with_two_outputs()[0]

我发现了“忽略输出”的其他方式 ,因为据我测试,至少它不会将输出fn_with_two_outputs()[1]作为变量存储在调用函数的环境中 (它存储在功能环境本身)。 为了避免讨论:要获得多个返回值而不调用两次,可以使用切片,例如fn_with_two_outputs()[0:4]

但是 :返回值-不管是否被“忽略”-似乎总是被评估。 我用以下代码测试了三种“忽略”它的变体。

def fn_with_two_outputs():
    a = [1, 2, 3]
    b = [0, 0, 0]
    return a, needless_fn(b)

def needless_fn(b):
    print("\"Be so good they can't ignore you.\" - Steve Martin")
    return b

# I want to ignore output b (wrapped by needless_fn)
a, _  = fn_with_two_outputs()   # stores _ = b, needless_fn was executed
a, *b = fn_with_two_outputs()   # suggested for Python3, stores a list b = [b], needless_fn was executed
a = fn_with_two_outputs()[0]    # b nowhere to be found, but needless_fn was executed

是否有一种方便的方法实际上忽略函数本身的输出,从而导致可选输出不占用任何内存并导致代码执行? 我的第一个想法是,给fn()一个参数以在两个return语句之间进行分支,一个返回值“ lazy”,另一个返回值-但这似乎是一种解决方法,而不是一种优雅的解决方案。

功能应更改。 它要么做两个独立的事情,要么第二件事取决于第一件事。 如果它们是独立的,那么这些东西应该是两个功能。

如果它们相互依赖,则使其具有两个功能,如下所示:

a = step_a()
b = step_b(a)

如果只想要a ,则只需写第一行。 如果要使昂贵的计算产生b ,则调用第二个函数。 这样,用户只需为他们想要计算的东西付费。

如果依赖关系是另一种解决方法,那么无论如何您将始终必须进行昂贵的计算。

有没有(易)的方式来影响从主叫被叫功能,无需通知所期望的行为的表示功能-简单地说,你的return a, needles_fn(b)将执行needless_fn(b)它甚至得到机会之前返回结果给呼叫者。

您可以做的是创建一个局部函数调用,并在需要结果时让外部调用者进行调用,例如:

import functools

def fn_with_two_outputs():
    a = [1, 2, 3]
    b = [0, 0, 0]
    return a, functools.partial(needless_fn, b)

def needless_fn(b):
    print("\"Be so good they can't ignore you.\" - Steve Martin")
    return b

print(fn_with_two_outputs()[0])  # needless_fn(b) doesn't get called, [0, 0, 0] is dropped
# [1, 2, 3] 

a, b = fn_with_two_outputs()  # needless_fn(b) doesn't get called, [0, 0, 0] is in memory
print(a)
# [1, 2, 3]
print(b())  # needless_fn(b) is finally evaluated
# "Be so good they can't ignore you." - Steve Martin
# [0, 0, 0]

注意,在第二种情况下,如上所述,只要引用b[0, 0, 0]仍保留在内存中。

可悲的是,除非您在谈论可以使用魔术__getattr__()实例属性,否则您需要显式调用b()而不是仅调用b进行评估。

显然,您不能忽略所调用函数的一部分,因为您是使用者。 每个指令都需要评估。 但是,您可以通过垃圾回收手动引用的变量来摆脱返回的数据。

即使在像Haskell这样的纯函数式编程语言中,也只能确定函数不会仅使用返回值来更改内存,在这种情况下,它也确实会更改内存。

处理函数的唯一方法是通过输入数据,因此您不能影响其正常执行。

回到Python垃圾收集器如何处理它,就您所调用的函数而言,它在内存中具有引用。 了解更多检查

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM