Каков идиоматический эквивалент Python этого кода C / C ++?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
в частности, как реализовать статический член на уровне функций, а не на уровне классов? И что-то меняет размещение функции в классе?
_
префиксом.Ответы:
Немного наоборот, но это должно работать:
Если вы хотите, чтобы код инициализации счетчика находился сверху, а не снизу, вы можете создать декоратор:
Затем используйте код как это:
К
foo.
сожалению, вам все равно потребуется использовать префикс.(Кредит: @ony )
источник
if "counter" not in foo.__dict__: foo.counter = 0
в качестве первых строкfoo()
. Это поможет избежать кода вне функции. Не уверен, что это было возможно еще в 2008 году. PS Нашел этот ответ при поиске возможности создания статических переменных функции, поэтому этот поток все еще "жив" :)foo
иfoo.counter =
тесно связаны между собой . тем не менее, я в конечном итоге предпочитаю подход декоратора, так как декоратор не будет вызван, и семантически более очевидно, что он делает (@static_var("counter", 0)
это проще для меня и имеет больше смысла для моих глаз, чемif "counter" not in foo.__dict__: foo.counter = 0
, особенно в последнем случае, когда вы должны использовать имя функции (дважды), которое может измениться).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Вы можете добавить атрибуты в функцию и использовать ее в качестве статической переменной.
В качестве альтернативы, если вы не хотите устанавливать переменную вне функции, вы можете использовать,
hasattr()
чтобы избежатьAttributeError
исключения:В любом случае статические переменные довольно редки, и вы должны найти лучшее место для этой переменной, скорее всего, внутри класса.
источник
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
следует сделать то же самое, используя вместо этого исключения.try
стоимости? Просто любопытно.Можно также рассмотреть:
Обоснование:
if
ответвления (подумайте об исключении StopIteration )источник
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
для этого не проще, а также менее эффективно.Другие ответы продемонстрировали, как вы должны это сделать. Вот способ, которым вы не должны:
Значения по умолчанию инициализируются только при первой оценке функции, а не при каждом ее выполнении, поэтому вы можете использовать список или любой другой изменяемый объект для хранения статических значений.
источник
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
я нахожу это хорошо читаемым. Еще один хороший момент - легкая функция переименования.types.SimpleNamespace
его,def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
не задавая специальный класс.Многие уже предложили протестировать hasattr, но есть более простой ответ:
Нет попытки / кроме, нет тестирования hasattr, просто getattr по умолчанию.
источник
try
/except
основанным подходом ravwojdyla довольно бессмысленна. Простойipython
%%timeit
микробенчмарк дал стоимостьtry
/except
при 255 нс на вызов против 263 нс дляgetattr
решения на основе. Да,try
/except
быстрее, но это не совсем "выигрышные комбинации"; это крошечная микрооптимизация. Пишите любой код, который кажется более понятным, не беспокойтесь о разнице в производительности.Вот полностью инкапсулированная версия, которая не требует внешнего вызова инициализации:
В Python функции являются объектами, и мы можем просто добавить или исправить патч переменные-члены к ним через специальный атрибут
__dict__
. Встроенныйvars()
возвращает специальный атрибут__dict__
.РЕДАКТИРОВАТЬ: Обратите внимание, в отличие от альтернативного
try:except AttributeError
ответа, при таком подходе переменная всегда будет готова для логики кода после инициализации. Я думаю, чтоtry:except AttributeError
альтернатива следующему будет менее СУХОЙ и / или иметь неловкий поток:РЕДАКТИРОВАТЬ 2: Я рекомендую вышеупомянутый подход только тогда, когда функция будет вызываться из нескольких мест. Если вместо этого функция вызывается только в одном месте, лучше использовать
nonlocal
:источник
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
вместоnot X in Y
(или советуйте использовать, если вы просто использовали его для более похожего сравнения между этим иhasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
Python не имеет статических переменных, но вы можете подделать его, определив вызываемый объект класса, а затем используя его как функцию. Также посмотрите этот ответ .
Обратите внимание, что
__call__
делает экземпляр класса (объекта) вызываемым по его собственному имени. Вот почему вызовfoo()
выше вызывает метод класса__call__
. Из документации :источник
foo
который предоставляется как синглтон.Используйте функцию генератора для генерации итератора.
Тогда используйте это как
Если вы хотите верхний предел:
Если итератор завершается (как в примере выше), вы также можете зацикливаться на нем напрямую, например
Конечно, в этих простых случаях лучше использовать xrange :)
Вот документация о доходности .
источник
Другие решения прикрепляют к функции атрибут counter, обычно с запутанной логикой для обработки инициализации. Это не подходит для нового кода.
В Python 3 правильным способом является использование
nonlocal
инструкции:См. PEP 3104 для уточнения
nonlocal
заявления.Если счетчик предназначен для частного модуля, он должен быть назван
_counter
вместо.источник
global counter
оператора вместоnonlocal counter
(nonlocal
просто позволяет писать в закрытое состояние во вложенной функции). Причина, по которой люди присоединяют атрибут к функции, заключается в том, чтобы не загрязнять глобальное пространство имен для состояния, характерного для этой функции, поэтому вам не нужно совершать даже более хакерские действия, когда двум функциям требуются независимыеcounter
s. Это решение не масштабируется; Атрибуты на функцию делают. Ответ КДБ - какnonlocal
помочь, но это добавляет сложности.nonlocal
болееglobal
точно , как вы отмечаете - он работает в строго больше обстоятельств.Использование атрибута функции в качестве статической переменной имеет некоторые потенциальные недостатки:
Идиоматический питон для второй проблемы, вероятно, будет именовать переменную с начальным подчеркиванием, чтобы сигнализировать, что к ней нельзя обращаться, сохраняя ее доступной после факта.
Альтернативой может быть шаблон, использующий лексические замыкания, которые поддерживаются
nonlocal
ключевым словом в python 3.К сожалению, я не знаю способа инкапсулировать это решение в декоратор.
источник
Подобно приведенному выше коду Винсента, он будет использоваться в качестве декоратора функции, и к статическим переменным нужно обращаться с именем функции в качестве префикса. Преимущество этого кода (хотя по общему признанию любой может быть достаточно умен, чтобы понять это) состоит в том, что вы можете иметь несколько статических переменных и инициализировать их более обычным способом.
источник
Чуть более читабельным, но более многословным (Zen of Python: явный лучше, чем неявный):
Смотрите здесь для объяснения того, как это работает.
источник
foo()
должен повторно инициализировать словарь в значение, указанное в определении функции (то есть с ключом счетчика, имеющим значение 0). Почему это не так?Python обычно использует подчеркивания для обозначения приватных переменных. Единственная причина в C объявить статическую переменную внутри функции - это скрыть ее за пределами функции, что на самом деле не является идиоматическим Python.
источник
Попробовав несколько подходов, я в итоге использую улучшенную версию ответа @ warvariuc:
источник
Идиоматический способ заключается в использовании класса , который может иметь атрибуты. Если вам нужно, чтобы экземпляры не были разделены, используйте синглтон.
Есть несколько способов, которыми вы могли бы подделать или изменить «статические» переменные в Python (один из них не упоминался до сих пор - иметь изменяемый аргумент по умолчанию), но это не Python, идиоматический способ сделать это. Просто используйте класс.
Или, возможно, генератор, если ваш шаблон использования подходит.
источник
default
аргумент является наиболее элегантным.В ответ на этот вопрос я могу представить другую альтернативу, которая может быть более приятной для использования и будет выглядеть одинаково как для методов, так и для функций:
Если вам нравится использование, вот реализация:
источник
Другой (не рекомендуется!) Поворот вызываемого объекта, например https://stackoverflow.com/a/279598/916373 , если вы не возражаете против использования подписи в стиле фанк-вызова, это сделать
источник
Вместо создания функции, имеющей статическую локальную переменную, вы всегда можете создать так называемый «объект функции» и присвоить ей стандартную (нестатическую) переменную-член.
Поскольку вы привели пример, написанный на C ++, я сначала объясню, что такое «функциональный объект» в C ++. «Функциональный объект» - это просто любой класс с перегруженным
operator()
. Экземпляры класса будут вести себя как функции. Например, вы можете написать,int x = square(5);
даже еслиsquare
это объект (с перегруженнымoperator()
), а не технически не «функция». Вы можете дать объекту-функции любую из функций, которые вы могли бы дать объекту класса.В Python мы также можем перегрузить,
operator()
за исключением того, что метод вместо этого называется__call__
:Вот определение класса:
Вот пример используемого класса:
Вывод на консоль выводится:
Если вы хотите, чтобы ваша функция принимала входные аргументы, вы также можете добавить их в
__call__
:источник
Soulution n + = 1
источник
Глобальная декларация обеспечивает эту функциональность. В приведенном ниже примере (Python 3.5 или выше, чтобы использовать «f»), переменная counter определяется вне функции. Определение его как глобального в функции означает, что «глобальная» версия вне функции должна быть доступна для функции. Таким образом, каждый раз, когда функция запускается, она изменяет значение вне функции, сохраняя его вне функции.
источник
Статическая переменная внутри метода Python
источник
Я лично предпочитаю следующее декораторам. Каждому свое.
источник
Этот ответ основан на ответе @claudiu.
Я обнаружил, что мой код становится менее понятным, когда мне всегда приходилось добавлять имя функции всякий раз, когда я собираюсь получить доступ к статической переменной.
А именно, в моем коде функции я бы предпочел написать:
вместо
Итак, мое решение состоит в том, чтобы:
statics
атрибут в функциюstatics
в качестве псевдонимаmy_function.statics
замечание
Мой метод использует класс с именем
Bunch
, который является словарем, который поддерживает доступ стиля атрибута, как JavaScript (см. Оригинальную статью об этом, около 2000 г.)Может быть установлен через
pip install bunch
Это также может быть написано от руки так:
источник
types.SimpleNamespace
(доступно начиная с версии 3.3) поддерживает это поведение «из коробки» (и реализовано в C на CPython, поэтому оно работает настолько быстро, насколько это возможно).Опираясь на ответ Даниила (дополнения):
Причина, по которой я хотел добавить эту часть, состоит в том, что статические переменные используются не только для приращения на какое-то значение, но и для проверки того, равно ли статическое значение var некоторому значению, как пример из реальной жизни.
Статическая переменная все еще защищена и используется только в рамках функции use_foo ()
В этом примере вызов функции foo () точно такой же (по отношению к соответствующему эквиваленту c ++):
если класс Foo определен ограниченно как класс синглтона, это было бы идеально. Это сделало бы это более питоническим.
источник
Конечно, это старый вопрос, но я думаю, что мог бы предоставить некоторые обновления.
Кажется, что аргумент производительности устарел. Похоже, один и тот же набор тестов дает схожие результаты для siInt_try и isInt_re2. Конечно, результаты могут отличаться, но это один сеанс на моем компьютере с python 3.4.4 на ядре 4.3.01 с Xeon W3550. Я запускал его несколько раз, и результаты кажутся похожими. Я переместил глобальное регулярное выражение в статическую функцию, но разница в производительности незначительна.
С учетом проблемы с производительностью кажется, что try / catch будет генерировать код, наиболее подходящий для будущего и заглавных букв, поэтому, возможно, просто оберните его в функцию
источник