10

Say I have several variables or objects in Python, a, b, c, ...

How can I easly dump these variables into a namespace in Python and restore them at a later time? (e.g. in the same way argparse wraps various variables into a namespace).

Here are two examples of how I would like to dump things to and from a namespace:

Dumping local variables into a namespace

function (bar):
   # We start with a, b and c
   a = 10
   b = 20
   c = "hello world"

   # We can dump anything we want into e, by just passing things as arguments:
   e = dump_into_namespace(a, b, c) 
   del a, b, c

   print (e.a + e.b) # Prints 30
   return e  # We can return e if we want. This is just a use case scenario

Dumping local variables from a namespace e

# We start with e, which for example was built with a call to 
# dump_into_namespace(a,b,c) somewhere else in the program,
# in which case e would hold a, b and c 

# We may receive e through a function call or load it from disk, e.g.:

function foo(e):

   # The following call creates the variables a,b and c
   # or updates their values if the already exist in memory
   dump_from_namespace(e) 
   del e

   print(a + b) # Prints 30
   print(c) # Prints hello world

My first question is: Is this possible at all in Python? (note that the method dump_into_namespace does not directly receive the names of the variables, at least as far as I can tell).

If the answer to the above is no, how could I do it with an interface like this?

e = dump_into_namespace('a', 'b', 'c')

Also, how would this be done this with a dictionary instead of a namespace?

There are a few threads that seem relevant addressing a dot-access of dynamically-defined variables, but I don't think they address the problem of dumping variables:

See also

Are there any libraries that facilitate this type of access through dot notation?

Update:

It looks like there is a library that supports dot-accessible dictionaries in Python, called Bunch, but I am not sure it would support easily dumping as I defined it.

Community
  • 1
  • 1
Amelio Vazquez-Reina
  • 91,494
  • 132
  • 359
  • 564
  • I've thought a lot about the solutions and implications of your question. I have an answer to write but my time is busy for the moment. - The second case is easier than the first one. By the way it must not be called "Dumping local variables from a namespace" but "Load items of another namespace into a local namespace". I use the term 'item' because it's the only one that fits for elements of a namespace that are couples (identifier,object). – eyquem Feb 16 '13 at 18:19
  • 1
    By the way, you MUST stop to use the word 'variable' that is very confusing in Python because a reader never knows if, in the mind of the writer, it designates an identifier, or a Python object, or a variable in the pure sense of this word ('chunk of memory whose content can changed'). The third possiblity being heretic in Python since all is object in Python and Python objects don't act as pure variables. – eyquem Feb 16 '13 at 18:22
  • 1
    "Load items of another namespace into a local namespace" is easy, it consists to update the calling namespace the same way a dictionary is updated. But there is an ambiguity. The word _namespace_ may designate a real namespace, as global and local namespaces are. Or _namespace_ may designates the so-called namespace of an object ("In a sense the set of attributes of an object also form a namespace." http://docs.python.org/2/tutorial/classes.html ) – eyquem Feb 16 '13 at 18:31
  • Anyway, it doesn't matter which case it is, because in the two cases, the update consists to update a dictionary. For global namespace it's ``global()['blah'] = obj_1`` . For local namespace, as far as I know, it is impossible to update. And for the namespace of an object it's ``objo.__dict__.update(loaded_namespace)`` . The details of the last kind of update depends on the object who is loading the content of another namespace. For modules that are objects, look also the function ``__import__`` that makes special actions, but I don't know precisely how it acts. – eyquem Feb 16 '13 at 18:45

4 Answers4

6

The solution below provides syntax very close to your requirement, the only difference is that you have to pass to the function environment where the variables are defined explicitly:

x = 10
y = 20

class dump_into_namespace:
    def __init__(self, env, *vars):
        self.vars = dict([(x, env[x]) for v in vars for x in env if v is env[x]])
    def __getattr__(self, name): return self.vars[name]

o = dump_into_namespace(locals(), x, y)
print o.x, o.y

You can then 'dump' back the variables to your locals (say, in a different function):

>>> locals().update(o.vars)
>>> x
10

EDIT:

Thanks to the suggestion of eyquem this can be even shorter. The idea is to put variables into self.__dict__ of the 'dump' object (note: syntax of update changes here):

class dump_into_namespace:
    def __init__(self, env, *vs):
        vars(self).update(dict([(x, env[x]) for v in vs for x in env if v is env[x]]))

def f():
    x = 10
    y = 20
    return dump_into_namespace(locals(), x, y)

o = f() 
print o.x, o.y 
globals().update(vars(o))
print x
Amelio Vazquez-Reina
  • 91,494
  • 132
  • 359
  • 564
piokuc
  • 25,594
  • 11
  • 72
  • 102
  • This looks great! It seems to support the easiest (and most difficult) interface that I defined in the OP. How could I dump the result from `o` back into locals? – Amelio Vazquez-Reina Feb 15 '13 at 21:49
  • Well, the variables you dump into that thing are still in the `locals()`, too, or do you want to dump them into a different `locals()`? – piokuc Feb 15 '13 at 21:51
  • Say I move `o` around (e.g. pickle and unpickle it, pass it to a function, etc.), and that I want to dump the variables from `o` back into my local namespace (creating the variables if necessary). Is this possible? – Amelio Vazquez-Reina Feb 15 '13 at 21:56
  • @user273158 @piokuc Looping first in ``vars`` leads to a mandatory second loop in ``env`` : then the loop in ``env`` is repeated for each element in ``vars`` while it's not necessary (see my answer) - Also, use of name ``vars`` as a parameter's name is confusing. Moreover , you use the same name ``vars`` for two different parameters in two different functions. - Not good, I think – eyquem Feb 15 '13 at 23:18
  • I don't agree with the 'name confusion' argument, one has to see where scopes begin and end, but OK, if this is an issue one can rename vars to something else. – piokuc Feb 15 '13 at 23:27
  • 1
    @user273158 @piokuc Additionally, the objects **x** and **y** aren't real attributes of the object **o**: their names doesn't belongs to the namespace of **o**, that is to say to ``o.__dict__`` as it's easy to verify with instructions ``print "x" in o.__dict__``. In fact the real and only attribute of **o** is a dictionary of name ``vars`` and value being a dictionary of keys ``"x"`` and ``"y"``. The overriding of ``__getattr__`` is there to mimic a namespace in which ``"x"`` and ``"y"`` would be the real keys (as in my code) – eyquem Feb 15 '13 at 23:33
  • @user273158 The idea of piokuc to pass the environnement as an argument is good. – eyquem Feb 15 '13 at 23:36
  • @eyquem excellent idea to update `self.__dict__`, thanks, updated my answer – piokuc Feb 15 '13 at 23:40
  • 1
    @user273158 improved the answer taking into account eyquem's idea – piokuc Feb 15 '13 at 23:41
  • Thanks! For `dump_from_namespace()` you chose to call `globals().update(vars(o))`. From what I understand, that would dump the variables into the namespace of global variables of the script. Did you choose this option because one cannot easily dump vriables into the local namespace of the calling function? (assuming that I call `dump_from_namespace` from a function). Just want to make sure I understand what this code does. – Amelio Vazquez-Reina Feb 15 '13 at 23:46
  • I used globals just as an example, to show that you can also 'dump' the variables there, no other particular reason. The global environment is just like any other local one with regards to what we are doing here – piokuc Feb 15 '13 at 23:49
5

You have several options to create your 'namespace'. The easiest two are:

  • Create a quick custom class:

    class Namespace(object):
        def __init__(self, **kw):
            self.__dict__.update(kw)
    
    def dump_into_namespace(**kw):
        return Namespace(**kw)
    

    Call with dump_into_namespace(a='a', b='b', c='c'); this takes an arbitrary number of keyword arguments.

  • Use a collections.namedtuple class:

    from collections import namedtuple
    
    Namespace = namedtuple('Namespace', 'a b c')
    
    def dump_into_namespace(a, b, c):
        return Namespace(a, b, c)
    

    Call with dump_into_namespace('a', 'b', 'c'); this only takes a fixed number of arguments, but your dump_into_namespace() function could provide defaults.

What you call 'dot notation' is really just attribute access.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
4

To be honest, the easiest way is just to assign them:

e.a = a
e.b = b
e.c = c

You can't really do it more dynamically, because a variable doesn't know its own name. You would have to pass them as keyword arguments, in which case you can just update the namespace's __dict__ directly:

def dump_into_namespace(e, **kwargs):
    e.__dict__.update(kwargs)

and you would have to call it as:

dump_into_namespace(e, a=a, b=b, c=c)
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
2

EDIT:

piokuc's answer inspired me to take account of the environnement

My solution updates self._dict_ so that there is no need to define a special function __getattr__ : the objects passed to the function become real attributes, their names belongs to the created object's dictionary.

def dump_into_ns(env,*x):
    class A:
        def __init__(self,*y):
            vars(self).update((n,o) for n,o in env.items()
                              if o in y)
    return A(*x)


a = 19
b = 'Monday'
c = 'Wednesday'


def ftry(x,y):
    palat = 'obastey'
    a = x -1
    b = y +100
    c = x*y -8
    return dump_into_ns(locals(),a,b,c)



h = dump_into_ns(globals(),a,b,c)
print "h.__dict__ ==",h.__dict__
print '"a" in h.__dict__ ==',"a" in h.__dict__,"  h.a ==",h.a
print '"b" in h.__dict__ ==',"b" in h.__dict__,"  h.b ==",h.b
print '"c" in h.__dict__ ==',"c" in h.__dict__,"  h.c ==",h.c
print

e = ftry(20,50)
print "e.__dict__ ==",e.__dict__
print '"a" in e.__dict__ ==',"a" in e.__dict__,"  e.a ==",e.a
print '"b" in e.__dict__ ==',"b" in e.__dict__,"  e.b ==",e.b
print '"c" in e.__dict__ ==',"c" in e.__dict__,"  e.c ==",e.c
print

print 'h.a == e.a  : ',h.a==e.a
print 'h.b == e.b  : ',h.b==e.b
print 'h.c == e.c  : ',h.c==e.c

result

h.__dict__ == {'a': 19, 'c': 'Wednesday', 'b': 'Monday'}
"a" in h.__dict__ == True   h.a == 19
"b" in h.__dict__ == True   h.b == Monday
"c" in h.__dict__ == True   h.c == Wednesday

e.__dict__ == {'a': 19, 'c': 992, 'b': 150}
"a" in e.__dict__ == True   e.a == 19
"b" in e.__dict__ == True   e.b == 150
"c" in e.__dict__ == True   e.c == 992

h.a == e.a  :  True
h.b == e.b  :  False
h.c == e.c  :  False

In a sense the set of attributes of an object also form a namespace.

http://docs.python.org/2/tutorial/classes.html

eyquem
  • 26,771
  • 7
  • 38
  • 46
  • Thanks! Can one build `dump_from_ns` using the same mechanism? – Amelio Vazquez-Reina Feb 15 '13 at 23:23
  • This is a great answer, and I am definitely interested in your it. You raised good points in your comments to @pyokuc's answer. One thing that seems to be missing in this answer (and partly why I personally haven't accepted it) is because it doesn't provide an equivalent and compatible `dump_from_ns(e)` (i.e. dumping variables **from** a namespace back into the **calling** namespace). Perhaps you can elaborate on this point? – Amelio Vazquez-Reina Feb 16 '13 at 00:09
  • @user273158 I used the function ``ftry()`` in my code to show that writing ``dump_into_ns(locals(),a,b,c)`` in this function was creating a dumping object with the local objects **a**,**b**,**c** passed to the parameters ``a``,``b``,``c`` and I put the ``return`` instruction to send the dumping outside the function. It's a kind of dumping object factory to create such an object with objects of an inner (local) namespace, not the global namespace. – eyquem Feb 16 '13 at 00:26
  • @user273158 But if you want to use the dumping object inside the function ``ftry()`` just put the instruction ``local_e = dump_into_ns(globals(),a,b,c)``. Does this answer to your question ? I don't entirely perceive the point you raised. – eyquem Feb 16 '13 at 00:28
  • I have updated the OP to clarify what I mean by "dump from" vs "dump into". Note the deletions in the OP too. – Amelio Vazquez-Reina Feb 16 '13 at 00:37
  • @user273158 Maybe I understand. If one puts the isntruction ``dump_into_ns(globals(),a,b,c)`` inside the function ``ftry()``, it doesn't work because the object of name ``b`` passed to the function ``dump_into_ns()`` is the object designated by the local identifier ``b`` whose value is 150, and this object of value 150 lands in the object **y** inside the ``__init__`` function: the test ``if o in y`` , where object **o** belongs to ``globals()`` where it has value ``'Monday'`` is therefore False for the identifier ``b`` – eyquem Feb 16 '13 at 00:39
  • @user273158 And what you would like, you, is a function ``dump_into_ns()`` that would give a correct result when ````dump_into_ns(globals(),a,b,c)`` would be written inside ``ftry()`` and importing the global objects **a**,**b**,**c** inside the local namespace of ``ftry()`` ?? – eyquem Feb 16 '13 at 00:42
  • @user273158 OK, I think I've understood. In ``e = dump_into_namespace(a, b, c)`` , the identifier ``e`` isn't there to say that an object of name ``e`` should be created ? But on the contrary , what ``e`` designates preexists and you want to make some objects to go in it ?? – eyquem Feb 16 '13 at 00:45
  • Exactly. `e` is a container that I can dump things into and from. Let me know if the OP is not clear. – Amelio Vazquez-Reina Feb 16 '13 at 00:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/24600/discussion-between-eyquem-and-user273158) – eyquem Feb 16 '13 at 00:52