What is the Python equivalent of static variables inside a function?

前端 未结 26 2577
天命终不由人
天命终不由人 2020-11-22 00:45

What is the idiomatic Python equivalent of this C/C++ code?

void foo()
{
    static int counter = 0;
    counter++;
          


        
相关标签:
26条回答
  • 2020-11-22 01:22

    I personally prefer the following to decorators. To each their own.

    def staticize(name, factory):
        """Makes a pseudo-static variable in calling function.
    
        If name `name` exists in calling function, return it. 
        Otherwise, saves return value of `factory()` in 
        name `name` of calling function and return it.
    
        :param name: name to use to store static object 
        in calling function
        :type name: String
        :param factory: used to initialize name `name` 
        in calling function
        :type factory: function
        :rtype: `type(factory())`
    
        >>> def steveholt(z):
        ...     a = staticize('a', list)
        ...     a.append(z)
        >>> steveholt.a
        Traceback (most recent call last):
        ...
        AttributeError: 'function' object has no attribute 'a'
        >>> steveholt(1)
        >>> steveholt.a
        [1]
        >>> steveholt('a')
        >>> steveholt.a
        [1, 'a']
        >>> steveholt.a = []
        >>> steveholt.a
        []
        >>> steveholt('zzz')
        >>> steveholt.a
        ['zzz']
    
        """
        from inspect import stack
        # get scope enclosing calling function
        calling_fn_scope = stack()[2][0]
        # get calling function
        calling_fn_name = stack()[1][3]
        calling_fn = calling_fn_scope.f_locals[calling_fn_name]
        if not hasattr(calling_fn, name):
            setattr(calling_fn, name, factory())
        return getattr(calling_fn, name)
    
    0 讨论(0)
  • 2020-11-22 01:22

    Sure this is an old question but I think I might provide some update.

    It seems that the performance argument is obsolete. The same test suite appears to give similar results for siInt_try and isInt_re2. Of course results vary, but this is one session on my computer with python 3.4.4 on kernel 4.3.01 with Xeon W3550. I have run it several times and the results seem to be similar. I moved the global regex into function static, but the performance difference is negligible.

    isInt_try: 0.3690
    isInt_str: 0.3981
    isInt_re: 0.5870
    isInt_re2: 0.3632
    

    With performance issue out of the way, it seems that try/catch would produce the most future- and cornercase- proof code so maybe just wrap it in function

    0 讨论(0)
  • 2020-11-22 01:23

    This answer builds on @claudiu 's answer.

    I found that my code was getting less clear when I always had to prepend the function name, whenever I intend to access a static variable.

    Namely, in my function code I would prefer to write:

    print(statics.foo)
    

    instead of

    print(my_function_name.foo)
    

    So, my solution is to :

    1. add a statics attribute to the function
    2. in the function scope, add a local variable statics as an alias to my_function.statics
    from bunch import *
    
    def static_vars(**kwargs):
        def decorate(func):
            statics = Bunch(**kwargs)
            setattr(func, "statics", statics)
            return func
        return decorate
    
    @static_vars(name = "Martin")
    def my_function():
        statics = my_function.statics
        print("Hello, {0}".format(statics.name))
    

    Remark

    My method uses a class named Bunch, which is a dictionary that supports attribute-style access, a la JavaScript (see the original article about it, around 2000)

    It can be installed via pip install bunch

    It can also be hand-written like so:

    class Bunch(dict):
        def __init__(self, **kw):
            dict.__init__(self,kw)
            self.__dict__ = self
    
    0 讨论(0)
  • 2020-11-22 01:24

    Building on Daniel's answer (additions):

    class Foo(object): 
        counter = 0  
    
    def __call__(self, inc_value=0):
        Foo.counter += inc_value
        return Foo.counter
    
    foo = Foo()
    
    def use_foo(x,y):
        if(x==5):
            foo(2)
        elif(y==7):
            foo(3)
        if(foo() == 10):
            print("yello")
    
    
    use_foo(5,1)
    use_foo(5,1)
    use_foo(1,7)
    use_foo(1,7)
    use_foo(1,1)
    

    The reason why I wanted to add this part is , static variables are used not only for incrementing by some value, but also check if the static var is equal to some value, as a real life example.

    The static variable is still protected and used only within the scope of the function use_foo()

    In this example, call to foo() functions exactly as(with respect to the corresponding c++ equivalent) :

    stat_c +=9; // in c++
    foo(9)  #python equiv
    
    if(stat_c==10){ //do something}  // c++
    
    if(foo() == 10):      # python equiv
      #add code here      # python equiv       
    
    Output :
    yello
    yello
    

    if class Foo is defined restrictively as a singleton class, that would be ideal. This would make it more pythonic.

    0 讨论(0)
  • 2020-11-22 01:26

    A bit reversed, but this should work:

    def foo():
        foo.counter += 1
        print "Counter is %d" % foo.counter
    foo.counter = 0
    

    If you want the counter initialization code at the top instead of the bottom, you can create a decorator:

    def static_vars(**kwargs):
        def decorate(func):
            for k in kwargs:
                setattr(func, k, kwargs[k])
            return func
        return decorate
    

    Then use the code like this:

    @static_vars(counter=0)
    def foo():
        foo.counter += 1
        print "Counter is %d" % foo.counter
    

    It'll still require you to use the foo. prefix, unfortunately.

    (Credit: @ony)

    0 讨论(0)
  • 2020-11-22 01:26

    Soulution n +=1

    def foo():
      foo.__dict__.setdefault('count', 0)
      foo.count += 1
      return foo.count
    
    0 讨论(0)
提交回复
热议问题