0

I want to keep astructure saved over iterations of a function, in order to keep data to use it for later purpose. There is a simple way to do it, in using a global variable.

The thing is, anyone could access the variable. So, is there a way to create a variable accessing only by the function, but that would not be erased when running this function?

Ex :

mylist = []

def test():
    global mylist

    if mylist:
        my_list.append(1)

    other_stuff, using my_list

Is exactly what I want to do, but my_list could be accessed by anyone else.

(ok I know, the example is completely dumb)

Sorry for the formulation, but I could not come with something simpler

EDIT : Ok, so with so different (and all interesting) solutions. I'll quickly explain the idea of what I want to do :)

Let's imagine I want to calculate is a number is a prime. This is usually computationnaly coslty.

So I got a is_prime(value) function, returning False or True.

value can be any number, but there are chances (in my case) that value takes several times the same value (litteraly ^^).

In this case, I could use a (not to long ) list of primes I have already found to quickly check and save computations. But my prime_list is no use in a function returning true/false.

So here is my question :).

Hope to clarify some minds (including me! ).

jlengrand
  • 12,152
  • 14
  • 57
  • 87
  • Why isn't this structure an argument to the function? Wouldn't that be a lot simpler? – S.Lott Jan 31 '12 at 21:47
  • Potential duplicate of http://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function – Mitch Lindgren Jan 31 '12 at 21:49
  • Yes and no. As shown in my example, this variable could be/or not be present. If an argument, this list will be present somewhere else in the code. Which I would like to avoid – jlengrand Jan 31 '12 at 21:50
  • My apologies, but I'm not sure if I understand what you mean. If your goal is to make a sort of global variable with limited access, you can't really do that. Even by giving the function a member variable as in the linked question and my answer, anyone who knows the name of that variable can still modify. Python's attitude towards this is a bit different than other OO languages, though: the idea is that we're all consenting adults, so carefully hiding data from other programmers is unnecessary. – Mitch Lindgren Jan 31 '12 at 21:55
  • Based on your edit, it sounds like you are trying to memoize your function. All the answers below should work for you; try searching for "function memoization" for more information on the topic. – chepner Jan 31 '12 at 22:11
  • thanks ! I'll definitely look at that ! – jlengrand Jan 31 '12 at 22:20

3 Answers3

3

Here is a case where a list as the default value of a parameter can come in handy:

def test(myList=[]):

    myList.append(1)
    other_stuff, using myList

I leave the if myList off, since it will exist (it was created when test was defined). (Plus, if myList tests if the list is non-empty, not defined).

myList, as a function parameter, is local to test. When test is defined, it is initialized to a list. The value of the list persists for the life of your program.

Compare to the standard advice of

def test(myList = None):
    if myList is None:
        myList = []

which is necessary when you want a fresh empty list inside test at each call if no list is provided by the call.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    This is very smart. I've to say though that this is a sort of "abuse" on Python meaning that is *possible* the OP is doing something that maybe could be done better in another way. – Rik Poggi Jan 31 '12 at 22:09
  • What makes you classify it as abuse? I am torn between decorators and default dictionary parameters being the best idiom for memoization in Python. – Russell Borogove Feb 01 '12 at 01:04
  • I agree that it is a slight abuse, as it's a function parameter that is never intended to be used except for its default value. One problem is that you can't use this trick if `*args` is in your function signature, as myList will receive one of the arguments before `args` receives any. – chepner Feb 01 '12 at 14:17
1

You can add a "static variable" to the function as follows:

>>> def fun(x):
...   fun.counter += x
...   print fun.counter
>>> fun.counter = 0
>>> fun(1)
1
>>> fun(5)
6
Mitch Lindgren
  • 2,120
  • 1
  • 18
  • 36
  • `fun.counter` is still accessible from outside fun, as demonstrated by your initialization. – chepner Jan 31 '12 at 21:52
  • shame, because I liked the idea though :) – jlengrand Jan 31 '12 at 21:56
  • 1
    Yes, but this does avoid potential collisions in the global namespace, which I see as the main problem of just using a global variable. As I commented on the OP, though, I admit that I may be misunderstanding the question. Generally in Python one doesn't bother to carefully hide data from other programmers... but obviously you don't want to pollute the global namespace so people can accidentally overwrite your variables. – Mitch Lindgren Jan 31 '12 at 21:57
1

One solution is to wrap it in another function:

def make_tester():
    mylist = []

    def test():
        if mylist:
            my_list.append(1)

        # other_stuff, using my_list
    return test

test = make_tester()

This incurs a small overhead in that you have to first call tester to create the function that will be called later. OTOH, it has the possible benefit that you can establish multiple contexts, each with their own instance of mylist:

test_a = make_tester()
test_b = make_tester()
Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365