5

I have a function. Inside that I'm maintainfing a dictionary of values. I want that dictionary to be maintained between different function calls

Suppose the dic is:

a = {'a': 1, 'b': 2, 'c': 3}

At first call, say, I changed a['a'] to 100.

Dict becomes a = {'a': 100, 'b': 2, 'c': 3}.

At another call, I changed a['b'] to 200.

I want that dic to be a = {'a': 100, 'b': 200, 'c': 3}

But in my code a['a'] doesn't remain 100. It changes to initial value 1.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
user46646
  • 153,461
  • 44
  • 78
  • 84
  • Please include the code which you are using. Your description is too vague without code. Please include code. Code helps. – S.Lott Jan 07 '09 at 11:11
  • I'm assuming that you mean a['a'] not a[a]. >>> a = {'a': 1, 'b': 2, 'c': 3} >>> a[a] Traceback (most recent call last): File "", line 1, in TypeError: dict objects are unhashable >>> a['a'] 1 – Tim Kersten Jan 07 '09 at 12:37

7 Answers7

18

You might be talking about a callable object.

class MyFunction( object ):
    def __init__( self ):
        self.rememberThis= dict()
    def __call__( self, arg1, arg2 ):
        # do something
        rememberThis['a'] = arg1
        return someValue

myFunction= MyFunction()

From then on, use myFunction as a simple function. You can access the rememberThis dictionary using myFunction.rememberThis.

tzot
  • 92,761
  • 29
  • 141
  • 204
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • I edited your answer because MyFunction() wouldn't call the function, it would create an instance with no visible side-effects and it would be confusing. – tzot Jan 07 '09 at 11:32
  • -1 for using a class and and magic functions to do something trivial – too much php Jan 07 '09 at 20:52
  • I'm on trying to be aggressive, but your implementation just seems far more complicated than it needs to be ... – too much php Jan 07 '09 at 20:53
  • 1
    By "magic functions", do you mean special method names?(http://www.python.org/doc/2.5.2/ref/specialnames.html) Or are you referring to something else? – S.Lott Jan 07 '09 at 20:54
  • Yes I'm talking about __call__() and the use of a custom object to replace the standard 'function' type. – too much php Jan 08 '09 at 00:27
16

You could use a static variable:

def foo(k, v):
  foo.a[k] = v
foo.a = {'a': 1, 'b': 2, 'c': 3}

foo('a', 100)
foo('b', 200)

print foo.a
Community
  • 1
  • 1
too much php
  • 88,666
  • 34
  • 128
  • 138
  • @ΤΖΩΤΖΙΟΥ neither are stateful callables. I don't see much benefit in using a class to create a stateful function other than separating initialization from use. – Aaron Maenpaa Jan 07 '09 at 15:36
  • The reason this is "class work" is that you might want two functionally identical functions, but with different data. – gnud Jan 07 '09 at 21:45
  • @Peter: a class instance is a binding of data and code. Exactly what the OQ asked for. You obviously have the right to support your suggested answer, but voting down other solutions that *are* helpful shows inflexibility to say the least. – tzot Jan 08 '09 at 16:13
8

Rather than forcing globals on the code base (that can be the decision of the caller) I prefer the idea of keeping the state related to an instance of the function. A class is good for this but doesn't communicate well what you are trying to accomplish and can be a bit verbose. Taking advantage of closures is, in my opinion, a lot cleaner.

def function_the_world_sees():
    a = {'a':1,'b':2,'c':3}

    def actual_function(arg0, arg1):
        a[arg0] = arg1
        return a

    return actual_function
stateful_function = function_the_world_sees()

stateful_function("b", 100)    
stateful_function("b", 200)

The main caution to keep in mind is that when you make assignments in "actual_function", they occur within "actual_function". This means you can't reassign a to a different variable. The work arounds I use are to put all of my variables I plan to reassign into either into a single element list per variable or a dictionary.

Ed.
  • 449
  • 2
  • 6
  • 9
7

If 'a' is being created inside the function. It is going out of scope. Simply create it outside the function(and before the function is called). By doing this the list/hash will not be deleted after the program leaves the function.

a = {'a':1,'b':2,'c':3}

# call you funciton here
J.J.
  • 4,856
  • 1
  • 24
  • 29
  • I tried this several different ways, all of which seem to work :) And best of all, it's very simple! – Miquella Jan 07 '09 at 07:01
  • @nosklo: He asked for a solution. Without the code we don't know that our list is being generated globally. He could have all this already wrapped inside another function, or class. – J.J. Jan 07 '09 at 15:35
4

This question doesn't have an elegant answer, in my opinion. The options are callable objects, default values, and attribute hacks. Callable objects are the right answer, but they bring in a lot of structure for what would be a single "static" declaration in another language. Default values are a minor change to the code, but it's kludgy and can be confusing to a new python programmer looking at your code. I don't like them because their existence isn't hidden from anyone who might be looking at your API.

I generally go with an attribute hack. My preferred method is:

def myfunct():
    if not hasattr(myfunct, 'state'): myfunct.state = list()
    # access myfunct.state in the body however you want

This keeps the declaration of the state in the first line of the function where it belongs, as well as keeping myfunct as a function. The downside is you do the attribute check every time you call the function. This is almost certainly not going to be a bottleneck in most code.

Nick
  • 21,555
  • 18
  • 47
  • 50
  • I argue that default values are generally a feature in other languages as well. The idea of adding an attribute to a function, though, might be confusing to a new python programmer looking at your code. – Bobort Nov 29 '18 at 17:23
3

You can 'cheat' using Python's behavior for default arguments. Default arguments are only evaluated once; they get reused for every call of the function.

>>> def testFunction(persistent_dict={'a': 0}):
...     persistent_dict['a'] += 1
...     print persistent_dict['a']
...
>>> testFunction()
1
>>> testFunction()
2

This isn't the most elegant solution; if someone calls the function and passes in a parameter it will override the default, which probably isn't what you want.

If you just want a quick and dirty way to get the results, that will work. If you're doing something more complicated it might be better to factor it out into a class like S. Lott mentioned.

EDIT: Renamed the dictionary so it wouldn't hide the builtin dict as per the comment below.

Steve Losh
  • 19,642
  • 2
  • 51
  • 44
  • This works (and it's very useful to know about, too) but the Paranoid Programmer inside me worries that it's too easy to replace the dictionary by providing an extra argument. – too much php Jan 07 '09 at 20:55
  • That kind of practice is just asking for obscure bugs to sneak into your program. – Algorias Jan 07 '09 at 23:11
  • @Peter - that can be a feature. you have one interface for modifying state as well as resetting it. depends on the goals of the code... – Daniel Naab Jan 08 '09 at 21:15
3

Personally, I like the idea of the global statement. It doesn't introduce a global variable but states that a local identifier actually refers to one in the global namespace.

d = dict()
l = list()
def foo(bar, baz):
    global d
    global l
    l.append(bar, baz)
    d[bar] = baz

In python 3.0 there is also a "nonlocal" statement.

Alex R
  • 2,201
  • 1
  • 19
  • 32