5

Following other posts here, I have a function that prints out information about a variable based on its name. I would like to move it into a module.

#python 2.7
import numpy as np
def oshape(name):
    #output the name, type and shape/length of the input variable(s)
    #for array or list
    x=globals()[name]
    if type(x) is np.array or type(x) is np.ndarray:
        print('{:20} {:25} {}'.format(name, repr(type(x)), x.shape))
    elif type(x) is list:
        print('{:20} {:25} {}'.format(name, repr(type(x)), len(x)))
    else:
        print('{:20} {:25} X'.format(name, type(t)))

a=np.array([1,2,3])
b=[4,5,6]
oshape('a')
oshape('b')

Output:

a                    <type 'numpy.ndarray'>    (3,)
b                    <type 'list'>             3

I would like to put this function oshape() into a module so that it can be reused. However, placing in a module does not allow access to the globals from the main module. I have tried things like 'import __main__ ' and even storing the function globals() and passing it into the submodule. The problem is that globals() is one function, which specifically returns the globals of the module it is called from, not a different function for each module.

import numpy as np
import olib

a=np.array([1,2,3])
b=[4,5,6]

olib.oshape('a')
olib.oshape('b')

Gives me:

KeyError: 'a'

Extra information: The goal is to reduce redundant typing. With a slight modification (I took it out to make it simpler for the question), oshape could report on a list of variables, so I could use it like:

oshape('a', 'b', 'other_variables_i_care_about')

So solutions that require typing the variable names in twice are not really what I am looking for. Also, just passing in the variable does not allow the name to be printed. Think about using this in a long log file to show the results of computations & checking variable sizes.

SolverWorld
  • 605
  • 1
  • 5
  • 12
  • 1
    Why not just pass in the arrays? – Padraic Cunningham Sep 19 '16 at 13:37
  • why not pass the variable directly and use it as is? – njzk2 Sep 19 '16 at 13:38
  • Because I want to print out the name of the variable. I was doing things like print('a', type(a), a.shape) but that gets tedious. – SolverWorld Sep 19 '16 at 14:16
  • @dangrunberg, but you already know the name so just pass it in with the array. If you are trying to do something more complicated then add that info to your question as right now you seem to be going a very roundabout way to do a simple thing. – Padraic Cunningham Sep 19 '16 at 14:20
  • How come you only want to be able to pass globals to this function? Do you typically define a lot of global data objects? FWIW, you could reduce the typing involved in passing both the name & object to the function by using keyword args, eg `def oshape(**kwargs):`, and call it like `oshape(a=a, b=b)`. – PM 2Ring Sep 19 '16 at 17:04

2 Answers2

2

The actual problem you have here is a namespace problem.

You could write your method this way:

def oshape(name, x):
    # output the name, type and shape/length of the input variable(s)
    # for array or list
    if type(x) in (np.array, np.ndarray):
        print('{:20} {:25} {}'.format(name, repr(type(x)), x.shape))
    elif type(x) is list:
        print('{:20} {:25} {}'.format(name, repr(type(x)), len(x)))
    else:
        print('{:20} {:25} X'.format(name, type(x)))

and use it like this:

import numpy as np
import olib

a=np.array([1,2,3])
b=[4,5,6]

olib.oshape('a', a)
olib.oshape('b', b)

but it's looks very redundant to have the variable and its name in the arguments.

Another solution would be to give the globals() dict to the method and keep your code.

Have a look at this answer about the visibility of the global variables through modules.

Community
  • 1
  • 1
Frodon
  • 3,684
  • 1
  • 16
  • 33
  • As you say, the first solution is redundant typing. And I want to be able to say things like oshape('a','b','other_vars_i_care_about') with a slight modification. Your second solution has the same problem - redundant typing to pass globals() in on each call. This is where C style macros are handy. – SolverWorld Sep 19 '16 at 14:20
1

Your logic is way over complicated, you should just pass the arrays themselves as you are also already passing the variable name as a string so you are not looking up something you don't have access to. But if you wanted to make your code work exactly as is you could set an attibute on the module:

import numpy as np
import olib

a = np.array([1, 2, 3])
b = [4, 5, 6]
olib.a = a
olib.b = b
olib.oshape('a')
olib.oshape('b')

This will take any args and search the module the code is run from for the attrs:

import numpy as np
import sys
from os.path import basename
import imp

def oshape(*args):
    # output the name, type and shape/length of the input variable(s)
    # for array or list
    file_name = sys.argv[0]
    mod = basename(file_name).split(".")[0]
    if mod  not in sys.modules:
        mod = imp.load_source(mod, file_name)
        for name in args:
            x = getattr(mod, name)
            if type(x) is np.array or type(x) is np.ndarray:
                print('{:20} {:25} {}'.format(name, repr(type(x)), x.shape))
            elif type(x) is list:
                print('{:20} {:25} {}'.format(name, repr(type(x)), len(x)))
            else:
                print('{} {} X'.format(name, type(x)))

Just pass the variable name strings:

:~/$ cat t2.py 
import numpy as np
from olib import oshape

a = np.array([1, 2, 3])
b = [4, 5, 6]
c = "a str"

oshape("a", "b", "c")


:$ python t2.py 
a                    <type 'numpy.ndarray'>    (3,)
b                    <type 'list'>             3
c <type 'str'> X
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • This is redundant code for each variable I want to print out. Interesting thought, though. I could do olib.globals=globals() one time, and access that from the module, but I couldn't create any new variables after that call if wanted to see them. – SolverWorld Sep 19 '16 at 14:23
  • There are ways to automate the process if you ran it through a function or wrapped olib to avoid typing it out for each. – Padraic Cunningham Sep 19 '16 at 14:33
  • That sounds great, can you give a simple example? If it requires code to be added in main, it would defeat the purpose of using a module and I might as well just use the code I posted in the original question. – SolverWorld Sep 19 '16 at 15:46
  • @dangrunberg, the edit should do what you were originally trying to do. – Padraic Cunningham Sep 19 '16 at 22:29
  • It does work! But it doesn't seem to work on a second call to oshape(), I think because the mod is already in sysmodules. I am not sure how to get it out if it is already there. I will accept your answer shortly, as it does what asked, but it would be great if there was a simple fix that let it work more than 1 time. thanks. – SolverWorld Sep 20 '16 at 19:19
  • i see the solution: Just need an else: mod=sys.modules[mod] Thanks! Not sure of etiquette - should I edit your answer? – SolverWorld Sep 20 '16 at 19:24
  • @dangrunberg, you can edit away, I think I edited the logic out somewhere along the way – Padraic Cunningham Sep 20 '16 at 21:35