0

I have two modules:

#module_a

def make_a(cb = None):

    global a

    a = 1 # It takes a very long time to make "a", so it is stored as a global in module_a.

    if cb != None:

        cb()

And,

#module_b

import module_a

def make_b():

    global b

    #In order to make "b", we need module_a.a, however there is no guarantee it has been made.
    if 'a' not in dir(module_a):

        module_a.make_a(make_b)

        return

    else:

        b = module_a.a + 1

make_b()

print(b) # 2

In the above code, the function make_b in module_b makes something named b, which depends on something named a in module_a. In order to satisfy its dependency, it calls make_a with a callback to make_b. Once a is made, then make_b is called again and b is made.

This pattern makes sense to me, but I am wondering if this is a generally accepted approach to doing this or if it is an anti-pattern.

Is there a more canonical approach to satisfying such a dependency in Python i.e., the Pythonic way?

  • I would consider the use of global variables an anti-pattern, yes. The "canonical" way is to provide arguments to your functions and return values from them. – juanpa.arrivillaga Mar 08 '19 at 23:12
  • I cannot do that because it takes a long time to make "a". –  Mar 08 '19 at 23:12
  • Sure you can. Global variables are not the usual way of implementing caching to begin with – juanpa.arrivillaga Mar 08 '19 at 23:13
  • If I did that, then it would merely be a getter - I would still need for make_b to execute after a is made. –  Mar 08 '19 at 23:14
  • I need for each module to cache its resources. I want to keep the framework very simple. Each module creates a resource that is referenced by other modules. –  Mar 08 '19 at 23:18
  • I removed the word "cache" and changed it to "store" in order to make the question more accurate. –  Mar 08 '19 at 23:30
  • Look, you can do whatever you want to do. You asked what was pythonic, and mutable global state *is not pythonic*. The language goes out of it's way to discourage you, e.g. assignments are automatically local. – juanpa.arrivillaga Mar 08 '19 at 23:53
  • @juanpa.arrivillaga How should "a" be stored (beyond the lifetime of the function call) in the module if not as a global? –  Mar 08 '19 at 23:56
  • 1
    Your current approach looks like a leaky abstraction, because every client that wants to access `a` must include that ugly attribute-check and callback. A much cleaner approach would be to eliminate the callback and put the attribute-check in `module_a`. Clients should then call `make_a`, which would cache and return the required value. This would also eliminate the need for a mutable module attribute, which as others have noted, is also an anti-pattern in python. – ekhumoro Mar 08 '19 at 23:58
  • PS: the approach I am suggesting is the same as a calculated property used with classes. Unfortunately, python doesn't really support module properties at the moment, so you either have to use a function, or [fake it](https://stackoverflow.com/q/880530/984421). – ekhumoro Mar 09 '19 at 00:14
  • @ekhumoro That makes sense. Thank you. –  Mar 09 '19 at 00:53

1 Answers1

0

To do this in a "Pythonic" way you should introduce an Observer pattern that emits an event once a is computed and b should subscribe to a in order to be called after a is finished.

Here you have an observer example

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Pablo Martinez
  • 449
  • 2
  • 6