简体   繁体   中英

Multiple calls of a function inside other functions

I have the following functions:

def f(x):
    return x

def f2(x):
    y = f(x)
    return y + x + 1

def f3(x):
    y = f(x)
    return y + x + 2

def DoAll(x):
    i2 = f2(x)
    i3 = f3(x)
    return i2, i3

print(DolAll(2))

Even when this code runs, it seems very inefficient since I call f(x) multiple times. How can I solve this problem without defining an f2(x, y) and f3(x, y) ? I would like to use something similar to

def f(x):
    return x

def f2(x):
    nonlocal y
    return y + x + 1

def f3(x):
    nonlocal y
    return y + x + 2

def DoAll(x):
    y = f(x)
    f2 = f2(x)
    f3 = f3(x)
    return f2, f3

print(DolAll(2))

Of course, the code shown here does not work.

You can use caching to avoid redoing the computation. Python provides such functionality out-of-the-box with functools.lru_cache :

from functools import lru_cache

@lru_cache()
def f(x):
    ...

This is more general than just making the value globally available, since it will allow multiple computations to stash values simultaneously.

If your functions are deterministic and without side-effects, you can short-circuit out the computation of nested calls entirely by caching all the intermediate results too:

@lru_cache()
def f(x):
    print(f'Called f({x})')
    return x

@lru_cache()
def f2(x):
    print(f'Called f2({x})')
    y = f(x)
    return y + x + 1

@lru_cache()
def f3(x):
    print(f'Called f3({x})')
    y = f(x)
    return y + x + 2

@lru_cache()
def DoAll(x):
    print(f'Called DoAll({x})')
    i2 = f2(x)
    i3 = f3(x)
    return i2, i3

>>> DoAll(1)
Called DoAll(1)
Called f2(1)
Called f(1)
Called f3(1)
(3, 4)

>>> DoAll(2)
Called DoAll(2)
Called f2(2)
Called f(2)
Called f3(2)
(5, 6)

>>> DoAll(1)
(3, 4)

Notice that the expensive computation only gets performed once for each new input.

You missed one nonlocal declaration: in DoAll itself. (You could use global as well, since the only non-local scope in this example is the global scope.) This would cause the y variable to be defined in a scope visible to f2 and f3 .

def DoAll(x):
    nonlocal y
    y = f(x)
    f2 = f2(x)
    f3 = f3(x)
    return f2, f3

However, this should start to resemble a crude attempt at defining a class that encapsulates y for all its methods to share.

class Foo:

    def f(self, x):
        return x

    def f2(self, x):
        return self.y + x + 1

    def f3(self, x):
        return self.y + x + 2

    def DoAll(self, x):
        self.y = self.f(x)
        f2 = self.f2(x)
        f3 = self.f3(x)
        return f2, f3

foo = Foo()
print(foo.DoAll(2))

(This in itself is a fairly awkward class design, as the y attribute gets defined on an ad-hoc basis, but it should hint at the use of the object itself providing the "scope" from which other methods can access a shared, non-local value y .)

In the end, Foo itself is basically providing a context in which the return value of Foo.f is cached for use by Foo.f2 and Foo.f3 . There are much cleaner ways to implement such a cache, so I'll direct you to Mad Physicist's answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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