Spatial Digger
Spatial Digger

Reputation: 1993

Returning a dictionary from a module to main

How do I return a python dictionary from a module?

I include a stripped out version of my code.

I have main.py the file which runs the program, this imports * from another file import_gdb.py.

from copa_import import *



def get_data(self):
    gdb_path = "<some file path>"
    import_gdb(gdb_path)

In the imported copa_import.py I have a dictionary built within import_gdb().

imports ...
layers = {}

def import_gdb(gdb_path):
    global layers

    try:
        <code to make the dictionary>
        layers = <dictionary>
    except:
        print('failed to make dictionary')
    return layers

I've assigned a global variable, but I assume that is only accessible locally within the module? I've returned layers how is this used/accessed within main.py?

I've been googling for a few days trying to find a clear explanation of how to do this. I probably lack some fundamental understanding, I welcome direction.

Upvotes: 1

Views: 882

Answers (2)

JJ Hassan
JJ Hassan

Reputation: 395

How I'd usually do what I think you're trying to do:

It's been pointed out, but what you're trying to do would usually be done by returning a dictionary from a module level function.

# simple example
def import_gdb(gdb_path):
  with open(gdb_path, 'r') as f:
     layers = parse_layers_from_file(f)
  return layers

Now if you were for some reason wanting to call this multiple times and save the value, perhaps make this the method of a new class that appends to a dictionary member of that class. Objects of this class type would "save" layers so far in the way it looks like you might have been trying to do with your global var.

# This class would let you access the aggregated dict through layers member, and
# get new layers from the import gdb method
class LayerStore:
  def __init__(self):
    self.layers = {}
  
  def import_gdb(self, gdb_path):
    with open(gdb_path, 'r') as f:
      new_layers = parse_layers_from_file(f)
    self.layers.update(new_layers)
    return new_layers

Bonus: Why what you were doing didn't seem to work

Lets simplify your copa_import file slightly

#copa_import.py
layers = {"foo": "bar"}

def import_gdb(path_to_gdb):
    global layers
    layers = {"bar": "baz"}
    return layers

and write a main file using your first import method that shows what's happening with that global

# main_with_star_import.py

from copa_import import *

print("After import")
print(layers)
print("Return value from import_gdb")
print(import_gdb(""))
print("After running import_gdb")
print(layers)
print("Re-import")
from copa_import import *
print(layers)

## Output:
#After import
{'foo': 'bar'}
#Return value from import_gdb
{'bar': 'baz'}
#After running import_gdb
{'foo': 'bar'}
#Re-import
{'bar': 'baz'}

Notice how we get the updated global dictionary only after we re-import the module? That's because the module is a namespace and object in its own right. Global namespace = module level namespace. The behaviour you see above is because a from module import * writes values to the namespace in the scope in which that statement is called. Even if you call the import_gdb method which reassigns the global layers you won't see the new value until you re-import copa_import.

What about if we use a different kind of import?

# main_with_module_import.py
import copa_import as c_i

print("After import")
print(c_i.layers)
print("Return value from import_gdb")
print(c_i.import_gdb(""))
print("After running import_gdb")
print(c_i.layers)
print("Re-import")
from copa_import import *
print(c_i.layers)

## Output
# After import
{'foo': 'bar'}
# Return value from import_gdb
{'bar': 'baz'}
# After running import_gdb
{'bar': 'baz'}
# Re-import
{'bar': 'baz'}

using this method, to access layers you have to do it via the module object. When you call import_gdb the value of the module object's layers variable is overwritten so the next time you access it you get the new value.

This kind of shows why using globals can be confusing for people using/debugging your code, because different kinds of import have different kinds of behaviour.

The method I mentioned first (using a class and/or functions) is better because the scope of variables and how to access them is much clearer.

Upvotes: 1

bherbruck
bherbruck

Reputation: 2226

You don't need to use global, as the comments above outline. You should be fine using normal imports:

gdb.py

# don't put layers at the global level here and modify its value later!!! WILL cause bugs later
# Make ALL_CAPS if you want it to look like default constant but still don't modify its value

def import_gdb(gdb_path):
    # dummy logic to populate layers
    layers = {
        'layer1': 1,
        'layer2': 2,
        'layer3': 3,
        'layer4': 4,
        'layer5': 5,
    }
    return layers

main.py

import gdb

layers = gdb.import_gdb('my/path')

print(layers)

Output:

{'layer1': 1, 'layer2': 2, 'layer3': 3, 'layer4': 4, 'layer5': 5}

Upvotes: 1

Related Questions