Что такое Python-эквивалент статических переменных внутри функции? - PullRequest
539 голосов
/ 11 ноября 2008

Что такое идиоматический эквивалент Python этого кода C / C ++?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

в частности, как реализовать статический член на уровне функций, а не на уровне класса? И что-нибудь меняет размещение функции в классе?

Ответы [ 26 ]

1 голос
/ 26 января 2016

Конечно, это старый вопрос, но я думаю, что могу предоставить какое-то обновление.

Кажется, что аргумент производительности устарел. Похоже, один и тот же набор тестов дает схожие результаты для siInt_try и isInt_re2. Конечно, результаты могут отличаться, но это один сеанс на моем компьютере с python 3.4.4 на ядре 4.3.01 с Xeon W3550. Я запускал его несколько раз, и результаты кажутся похожими. Я переместил глобальное регулярное выражение в статическую функцию, но разница в производительности незначительна.

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

Если учесть проблему с производительностью, кажется, что try / catch будет генерировать код с наибольшей вероятностью на будущее и заглавные буквы, поэтому, возможно, просто оберните его в функцию

1 голос
/ 01 декабря 2015

Другой (не рекомендуется!) Поворот на вызываемом объекте, например https://stackoverflow.com/a/279598/916373,, если вы не возражаете против использования подписи в стиле фанк-вызова, можно сделать

class foo(object):
    counter = 0;
    @staticmethod
    def __call__():
        foo.counter += 1
        print "counter is %i" % foo.counter

>>> foo()()
counter is 1
>>> foo()()
counter is 2
1 голос
/ 13 января 2015

Статическая переменная внутри метода Python

class Count:
    def foo(self):
        try: 
            self.foo.__func__.counter += 1
        except AttributeError: 
            self.foo.__func__.counter = 1

        print self.foo.__func__.counter

m = Count()
m.foo()       # 1
m.foo()       # 2
m.foo()       # 3
1 голос
/ 08 июня 2012

Я лично предпочитаю следующее декораторам. Каждому свое.

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 голосов
/ 20 августа 2018

Опираясь на ответ Даниила (дополнения):

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)

Причина, по которой я хотел добавить эту часть, состоит в том, что статические переменные используются не только для приращения на какое-то значение, но и для проверки того, равно ли статическое значение var некоторому значению, как пример из реальной жизни.

Статическая переменная по-прежнему защищена и используется только в рамках функции use_foo ()

В этом примере вызов функций foo () точно так же (относительно соответствующего эквивалента в c ++):

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

если класс Foo ограничительно определен как одноэлементный класс, это было бы идеально. Это сделало бы его более питоническим.

0 голосов
/ 18 марта 2018

Этот ответ основан на ответе @claudiu.

Я обнаружил, что мой код становился менее четким, когда у меня всегда было перед именем функции всякий раз, когда я собираюсь получить доступ к статической переменной.

А именно, в своем коде функции я бы предпочел написать:

print(statics.foo)

вместо

print(my_function_name.foo)

Итак, мое решение:

  1. добавить атрибут statics к функции
  2. в области действия функции добавьте локальную переменную statics в качестве псевдонима к 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))

Примечание

Мой метод использует класс с именем Bunch, который является словарем, который поддерживает доступ в стиле атрибута, а-ля JavaScript (см. оригинальную статью об этом, около 2000)

Может быть установлен через pip install bunch

Это также может быть написано от руки так:

class Bunch(dict):
    def __init__(self, **kw):
        dict.__init__(self,kw)
        self.__dict__ = self
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...