함수 내에서 정적 변수와 동등한 파이썬은 무엇입니까?


630

이 C / C ++ 코드와 같은 관용적 인 파이썬은 무엇입니까?

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

특히 클래스 수준이 아닌 함수 수준에서 정적 멤버를 어떻게 구현합니까? 그리고 함수를 클래스에 배치하면 아무것도 변경됩니까?


22
내가 두려워하는 것은 없습니다 . 함수 속성으로 데코레이터 해킹을 수행하더라도 외부에서 변수에 액세스 할 수 있습니다. 또한 함수에 함수 이름을 하드 코딩해야하므로 매우 성가시다. 일반적인 _접두사 대신 클래스 또는 모듈 전역 변수를 사용하는 것이 좋습니다 .
lpapp

8
비 C 프로그래머의 경우 [ stackoverflow.com/questions/5033627 / ... 함수 내의 정적 변수는 해당 함수 범위 내에서만 볼 수 있지만 수명은 프로그램의 전체 수명이며 한 번만 초기화됩니다). 기본적으로 함수 호출 사이에 존재하는 영구 카운터 또는 스토리지 변수.
smci

2
@lpapp : 거기에 일종의 클래스 멤버가 있습니다. 다른 코드가 보거나 변경하는 것을 막을 수 없다는 것이 맞습니다.
smci

답변:


681

약간 역전되었지만 작동해야합니다.

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

카운터 초기화 코드를 하단 대신 상단에 표시하려면 데코레이터를 만들 수 있습니다.

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

그런 다음 다음과 같은 코드를 사용하십시오.

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

foo.불행히도 여전히 접두사 를 사용해야합니다 .

(크레딧 : @ony )


23
foo 인스턴스는 하나뿐입니다.이 하나의 함수입니다. 모든 호출은 동일한 변수에 액세스합니다.
Claudiu

121
이것을 파고해서 미안하지만, if "counter" not in foo.__dict__: foo.counter = 0의 첫 줄에 넣을 것 입니다 foo(). 이것은 함수 외부의 코드를 피하는 데 도움이됩니다. 이것이 2008 년에 가능했는지 확실하지 않습니다. PS 정적 함수 변수를 생성 할 가능성을 찾는 동안이 답변을 찾았
으므로이

8
@ binaryLV : 아마도 첫 번째 접근 방식보다 선호합니다. 첫 번째 방법의 문제는 그 즉시 명확하지 않다이다 foofoo.counter = 밀접하게 관련되어 있습니다. 그러나, 나는 데코레이터가 호출되지 않을 방법이 없으며 의미 적으로 의미가 더 분명하기 때문에 데코레이터 접근법을 선호합니다 . 특히 후자의 경우 @static_var("counter", 0)보다 더 쉽습니다 if "counter" not in foo.__dict__: foo.counter = 0변경 될 수있는 기능 이름 (두 번)).
Claudiu

6
@lpapp : 정적 변수의 포인트에 따라 다릅니다. 나는 그것이 여러 함수 호출에서 동일한 값이 될 것이라고 항상 생각했다. 나는 당신이 말했듯이 변수 숨기기에 대해 결코 생각하지 않았습니다.
Claudiu

3
def foo(): if not hasattr(foo,"counter"): foo.counter=0 foo.counter += 1
Erik Aronesty

221

함수에 속성을 추가하고 정적 변수로 사용할 수 있습니다.

def myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

또는 함수 외부에서 변수를 설정하지 않으려 hasattr()AttributeError경우 예외 를 피하기 위해 사용할 수 있습니다 .

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

어쨌든 정적 변수는 다소 드물며 클래스 내 에서이 변수에 대한 더 좋은 곳을 찾아야합니다.


6
if 문 대신 시도해 보시겠습니까?
ravwojdyla

12
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1대신 예외를 사용하여 동일한 작업을 수행해야합니다.
sleblanc

예외적 인 상황, 예를 들어, 성공적으로 열었던 입력 파일과 같이 프로그래머가 예상하지 못한 상황에는 예외를 사용해야합니다. 이것은 예상되는 상황이며 if 문이 더 합리적입니다.
Hack Saw

11
@Hack_Saw : 음, 이것은 Pythonic입니다 (권한보다 용서를 구하는 것이 더 낫습니다). 파이썬 최적화 기술에서는 if의 비용을 절약하기 때문에 실제로 권장됩니다 (그러나 조기 최적화를 권장하지는 않지만). 예외적 인 경우에 대한 규칙 : 1. 실패는 여기서 예외적 인 경우입니다. 한 번만 발생합니다. 2. 나는 규칙이 예외를 사용하는 것에 관한 것이라고 생각한다. 이것은 대부분의 언어에서 일반적으로 작동하지만 백업 작업이 예상되는 작업에 대한 예외를 포착합니다.
leewz

@leewangzhong : 예외를 일으키지 않는 블록을 묶는 데 try비용이 추가됩니까? 그냥 궁금해서
trss

201

또한 다음을 고려할 수 있습니다.

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

추리:

  • 많은 pythonic ( "허용을 요구하지 않습니다")
  • if분기 대신 예외를 사용하십시오 ( StopIteration 예외를 생각하십시오 )

11
나는 오랫동안 파이썬을 사용하지 않았지만, 이것은 언어의 암시 적 통행 중 하나를 만족시킵니다. (공평하게) 쉽지 않은 경우, 잘못하고 있습니다 .
ZX9

"self.foo.counter = 1"클래스 메소드에서 즉시 작동하지 않았으므로 AttributeError가 다시 발생합니다.
villasv

16
이것은 올바른 해결책이며 모듈을 실행할 때 또는 모듈에서 무언가를 가져올 때가 아니라 함수가 호출 될 때 초기화 코드가 실행되기 때문에 허용되는 대답이어야합니다. 현재 허용되는 답변 Python 데코레이터 함수 실행을 참조하십시오 . 거대한 라이브러리 모듈이 있으면 가져 오지 않은 함수를 포함하여 모든 데코레이터가 실행됩니다.
Nils Lindemann

3
더 간단한 접근법 : def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
TheCuriousOne

5
@MANU 이것을 사용 hasattr()하는 것은 더 간단하지 않고 덜 효율적입니다.
moooeeeep

48

다른 답변은 당신이 이것을 해야하는 방법을 보여주었습니다. 하지 말아야 할 방법은 다음과 같습니다.

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

기본값은 함수가 실행될 때마다가 아니라 함수가 처음 평가 될 때만 초기화되므로 목록 또는 기타 변경 가능한 객체를 사용하여 정적 값을 저장할 수 있습니다.


나는 그것을 시도했지만 어떤 이유로 함수 매개 변수가 0이 아닌 140으로 초기화되었습니다. 왜 그런 것입니까?
andrewdotnich 2013

1
@bouvard 정적 변수가 필요한 재귀 함수의 경우 실제로 잘 읽는 유일한 함수입니다.
lifebalance

1
나는 여러 가지 접근법을 시도했으며 이것이 파이썬으로 받아 들여지기를 바랍니다. 의미있는 이름으로 def foo(arg1, arg2, _localstorage=DataClass(counter=0))읽을 수 있습니다. 또 다른 좋은 점은 쉬운 기능 이름 바꾸기입니다.
VPfB

2
왜 그렇게하지 말아야한다고 말합니까? 나에게 완벽하게 보인다!
Konstantin

1
@VPfB는 : 일반적인 저장 장치의 경우, 사용할 수 types.SimpleNamespace그것을 만드는, def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):특별한 클래스를 정의 할 필요없이.
ShadowRanger

43

많은 사람들이 이미 'hasattr'테스트를 제안했지만 더 간단한 답변이 있습니다.

def func():
    func.counter = getattr(func, 'counter', 0) + 1

시도 / 제외, 테스트 hasattr 없음, 기본값으로 getattr.


2
def func () : def foo () : return 1112 func.counter = getattr (func, 'counter', foo ()) + 1 호출 할 때 함수를 넣을 때 getattr의 세 번째 매개 변수에주의하십시오 func, foo는 항상 호출됩니다!
Codefor

1
func이 호출 될 때마다 getattr에 대한 호출. 성능에 문제가 없다면 시도 / 제외하면 손을 fine 수 있습니다.
마크 로렌스

2
@MarkLawrence : 사실, 적어도 내 Windows x64 3.8.0 설치에서이 답변과 ravwojdyla의 동등한 try/ except기반 접근 방식 의 성능 차이 는 의미가 없습니다. 간단한 ipython %%timeit마이크로 벤치 마크는 호출 당 255ns try/ 의 비용을 기반 솔루션의 except경우 263ns에 비해 getattr제공했습니다. 그렇다. try/ except는 빠르지 만 정확히 "손을 win 다"는 것은 아니다. 작은 미세 최적화입니다. 더 명확한 코드를 작성하십시오. 이와 같은 사소한 성능 차이에 대해 걱정하지 마십시오.
ShadowRanger

벤치마킹 해 주셔서 감사합니다. 나는 MarkLawrence의 진술에 대해 2 년 동안 궁금해했으며, 당신이 연구를하게되어 매우 기쁩니다. 나는 당신의 최종 문장에 분명히 동의합니다- "모든 코드를 명확하게 작성하십시오"-이것이 바로이 답변을 쓴 이유입니다.
조나단

28

다음은 외부 초기화 호출이 필요하지 않은 완전히 캡슐화 된 버전입니다.

def fn():
    fn.counter=vars(fn).setdefault('counter',-1)
    fn.counter+=1
    print (fn.counter)

파이썬에서 함수는 객체이며 특별한 속성을 통해 멤버 변수를 추가하거나 원숭이 패치 할 수 있습니다 __dict__. 내장 vars()은 특수 속성 을 반환합니다 __dict__.

편집 : 참고, 대안 try:except AttributeError답변 과 달리이 접근법을 사용하면 변수는 초기화 후에 항상 코드 논리를 준비합니다. try:except AttributeError다음에 대한 대안은 덜 건조하고 어색한 흐름 이라고 생각합니다 .

def Fibonacci(n):
   if n<2: return n
   Fibonacci.memo=vars(Fibonacci).setdefault('memo',{}) # use static variable to hold a results cache
   return Fibonacci.memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2)) # lookup result in cache, if not available then calculate and store it

EDIT2 : 함수가 여러 위치에서 호출 될 때 위의 접근 방식 만 권장합니다. 대신 함수가 한 곳에서만 호출되면 다음을 사용하는 것이 좋습니다 nonlocal.

def TheOnlyPlaceStaticFunctionIsCalled():
    memo={}
    def Fibonacci(n):
       nonlocal memo  # required in Python3. Python2 can see memo
       if n<2: return n
       return memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2))
    ...
    print (Fibonacci(200))
    ...

2
이것에 대한 유일한 문제는 실제로 깔끔하지 않다는 것입니다.이 패턴을 사용하려면 코드를 잘라내어 붙여
Claudiu

2
아마 같은 것을 사용한다try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
endolith

2
X not in Y대신에 사용하십시오 not X in Y(또는 그와 비슷한 비교를 위해 그것을 사용하는 경우 사용하는 것이 좋습니다 hasattr)
Nick T

이 방법에 대해 : def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
TheCuriousOne

if 절이 불필요한 중첩을 추가하기 때문에 이상적이지 않습니다.이 상황에서는 setdefault를 선호합니다
Riaz Rizvi

27

파이썬에는 정적 변수가 없지만 호출 가능한 클래스 객체를 정의한 다음 함수로 사용하여 위조 할 수 있습니다. 이 답변도 참조하십시오 .

class Foo(object):
  # Class variable, shared by all instances of this class
  counter = 0

  def __call__(self):
    Foo.counter += 1
    print Foo.counter

# Create an object instance of class "Foo," called "foo"
foo = Foo()

# Make calls to the "__call__" method, via the object's name itself
foo() #prints 1
foo() #prints 2
foo() #prints 3

참고 __call__자신의 이름으로 클래스 (객체) 호출의 인스턴스를 만든다. 그래서 foo()위의 호출은 클래스의 __call__메소드를 호출하는 이유 입니다. 설명서에서 :

클래스에서 __call__()메소드를 정의하여 임의 클래스의 인스턴스를 호출 할 수 있습니다 .


15
함수는 이미 객체이므로 불필요한 레이어를 추가합니다.
DasIch

이것이 실제로 좋은 아이디어라는 긴 의견에 대해서는 SO 답변을 참조하십시오. stackoverflow.com/questions/460586 . 이러한 클래스를 단일 클래스로 만드는 것이 좋습니다 ( 아마도 this stackoverflow.com/questions/6760685 )도 좋은 생각입니다. @ S.Lott이 "... 카운터를 클래스 정의로 이동 ..."이라는 의미가 무엇인지 모르겠습니다. 이미 클래스 변수 위치에있는 것처럼 보입니다.
Reb. Cabin

1
필자의 연구에 따르면,이 클래스 기술은이 페이지에서 제시된 접근 방식 중 가장 "피 토닉"인 것으로 보이며 가장 적은 속임수를 사용합니다. 따라서 새로운 Python 개발자로서 함수에서 C 정적 변수와 같은 대체 변수로 채택 할 계획입니다.
Gabriel Staples

1
foo1 = Foo () 및 foo2 = Foo ()를 원하는 경우 어떻게됩니까?
마크 로렌스

@MarkLawrence 그런 다음 각각 고유 카운터를 가진 두 개의 호출 가능한 클래스 인스턴스가 있습니다. foo싱글 톤으로 제공되는 인스턴스 를 사용하지 않는 경우 정확히 무엇을 기대해야합니까 ?
Aaron McMillin

14

생성기 함수를 사용하여 반복자를 생성하십시오.

def foo_gen():
    n = 0
    while True:
        n+=1
        yield n

그런 다음처럼 사용하십시오.

foo = foo_gen().next
for i in range(0,10):
    print foo()

상한을 원하는 경우 :

def foo_gen(limit=100000):
    n = 0
    while n < limit:
       n+=1
       yield n

반복자가 종료되면 (위의 예처럼) 직접 반복 할 수도 있습니다.

for i in foo_gen(20):
    print i

물론,이 간단한 경우에는 xrange를 사용하는 것이 좋습니다 :)

yield 문 에 대한 문서는 다음과 같습니다 .


11

다른 솔루션은 카운터 속성을 함수에 연결하며 일반적으로 초기화를 처리하기 위해 복잡한 논리를 사용합니다. 새로운 코드에는 적합하지 않습니다.

Python 3에서 올바른 방법은 nonlocal명령문 을 사용하는 것입니다.

counter = 0
def foo():
    nonlocal counter
    counter += 1
    print(f'counter is {counter}')

사양에 대해서는 PEP 3104 를 참조하십시오 .nonlocal .

카운터가 모듈 전용 인 경우 _counter대신 이름을 지정해야합니다 .


Python 3 이전에도 항상 global counter대신 명령문 으로이 작업을 수행 할 수 있습니다 nonlocal counter( nonlocal중첩 함수에서 클로저 상태에 쓸 수 있습니다). 사람들이 함수에 속성을 첨부하는 이유는 함수와 관련된 상태에 대한 전역 네임 스페이스를 오염시키지 않기 때문에 두 함수가 독립적 인을 필요로 할 때 해커 일을 할 필요가 없습니다 counter. 이 솔루션은 확장되지 않습니다. 함수의 속성이 수행합니다. kdb의 대답 은 어떻게 nonlocal도움 이 될 수 있지만 복잡성을 더합니다.
ShadowRanger

Eh, 나는 이것을 많이하지 않으면 팩토리 함수 또는 데코레이터의 복잡성이 과도하다고 생각합니다.이 경우 디자인은 약간 냄새가납니다. 일회성으로, 로컬이 아닌 카운터를 추가하고 완료하십시오. 명명 규칙에 대한 답변에 약간을 추가했습니다. 또한, 내가 추천하는 이유 nonlocal이상은 global당신이 지적 정확히 - 그것은 엄격하게 많은 상황에서 작동합니다.
cbarrick

8

함수의 속성을 정적 변수로 사용하면 몇 가지 잠재적 인 단점이 있습니다.

  • 변수에 액세스하려고 할 때마다 함수의 전체 이름을 작성해야합니다.
  • 외부 코드는 변수에 쉽게 액세스하여 값을 엉망으로 만들 수 있습니다.

두 번째 문제에 대한 관용적 파이썬은 아마도 밑줄로 변수의 이름을 지정하여 변수에 액세스 할 수 없다는 것을 알리고 사실 후에 액세스 할 수 있도록 유지합니다.

대안은 nonlocal파이썬 3 의 키워드로 지원되는 어휘 클로저를 사용하는 패턴 입니다.

def make_counter():
    i = 0
    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter
counter = make_counter()

슬프게도이 솔루션을 데코레이터로 캡슐화 할 방법이 없습니다.


7
def staticvariables(**variables):
    def decorate(function):
        for variable in variables:
            setattr(function, variable, variables[variable])
        return function
    return decorate

@staticvariables(counter=0, bar=1)
def foo():
    print(foo.counter)
    print(foo.bar)

위의 빈센트 코드와 마찬가지로 이것은 함수 데코레이터로 사용되며 함수 이름을 접두어로 사용하여 정적 변수에 액세스해야합니다. 이 코드의 장점은 누구나 알아낼 수있을만큼 똑똑 할 수는 있지만 여러 정적 변수를 사용하여보다 일반적인 방식으로 초기화 할 수 있다는 것입니다.


7

조금 더 읽기 쉽지만 더 장황합니다 (Zen of Python : 명시 적이 암시 적보다 낫습니다).

>>> def func(_static={'counter': 0}):
...     _static['counter'] += 1
...     print _static['counter']
...
>>> func()
1
>>> func()
2
>>>

작동 방식에 대한 설명은 여기 를 참조 하십시오 .


이 코드가 왜 작동하는지 자세히 설명해 주시겠습니까? 두 번째 foo()는 사전을 함수 정의에 지정된 값으로 다시 초기화해야합니다 (따라서 카운터 키의 값은 0 임). 왜 그렇지 않습니까?
raffaem

3
@raffamaiden : 기본 인수는 함수가 정의 될 때 한 번만 평가되며 함수가 호출 될 때마다 평가되지 않습니다.
Daniel K.

6
_ 카운터 = 0
데프 foo () :
   글로벌 _ 카운터
   _ 카운터 + = 1
   '카운터는', _counter 인쇄

파이썬은 일반적으로 밑줄을 사용하여 개인 변수를 나타냅니다. C에서 함수 내부에 정적 변수를 선언하는 유일한 이유는 함수 외부에서 변수를 숨기려는 것입니다. 실제로 관용적 인 파이썬은 아닙니다.


4

여러 가지 접근법을 시도한 후에 @warvariuc의 답변의 향상된 버전을 사용하게되었습니다.

import types

def func(_static=types.SimpleNamespace(counter=0)):
    _static.counter += 1
    print(_static.counter)

3

관용적 방법은 사용하는 클래스 속성을 가질 수있다. 인스턴스를 분리하지 않으려면 싱글 톤을 사용하십시오.

"정적"변수를 파이썬에 가짜로 만들거나 숨길 수있는 방법에는 여러 가지가 있지만 (지금까지 언급되지 않은 것은 가변적 인 기본 인수를 갖는 것입니다), 이것은 파이썬적인 관용적 방법이 아닙니다. 그냥 수업을 사용하십시오.

또는 사용 패턴에 맞는 발전기 일 수도 있습니다.


독립형 재귀 함수의 경우 default인수가 가장 우아합니다.
lifebalance

3

이 질문에 의해 프롬프트 되면 사용하기에 조금 더 좋을 수있는 다른 대안을 제시 할 수 있으며 메소드와 함수 모두 동일하게 보일 것입니다.

@static_var2('seed',0)
def funccounter(statics, add=1):
    statics.seed += add
    return statics.seed

print funccounter()       #1
print funccounter(add=2)  #3
print funccounter()       #4

class ACircle(object):
    @static_var2('seed',0)
    def counter(statics, self, add=1):
        statics.seed += add
        return statics.seed

c = ACircle()
print c.counter()      #1
print c.counter(add=2) #3
print c.counter()      #4
d = ACircle()
print d.counter()      #5
print d.counter(add=2) #7
print d.counter()      #8    

사용법이 마음에 들면 구현은 다음과 같습니다.

class StaticMan(object):
    def __init__(self):
        self.__dict__['_d'] = {}

    def __getattr__(self, name):
        return self.__dict__['_d'][name]
    def __getitem__(self, name):
        return self.__dict__['_d'][name]
    def __setattr__(self, name, val):
        self.__dict__['_d'][name] = val
    def __setitem__(self, name, val):
        self.__dict__['_d'][name] = val

def static_var2(name, val):
    def decorator(original):
        if not hasattr(original, ':staticman'):    
            def wrapped(*args, **kwargs):
                return original(getattr(wrapped, ':staticman'), *args, **kwargs)
            setattr(wrapped, ':staticman', StaticMan())
            f = wrapped
        else:
            f = original #already wrapped

        getattr(f, ':staticman')[name] = val
        return f
    return decorator

3

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

3

정적 로컬 변수가있는 함수를 만드는 대신 항상 "함수 개체"라는 것을 만들고 표준 (비 정적) 멤버 변수를 제공 할 수 있습니다.

C ++로 작성된 예제를 제공 했으므로 먼저 C ++에 "함수 객체"가 무엇인지 설명하겠습니다. "함수 객체"는 단순히 오버로드 된 클래스입니다 operator(). 클래스의 인스턴스는 함수처럼 동작합니다. 예를 들어, 오버로드 된 객체 이고 기술적으로 "함수"가 아닌 int x = square(5);경우에도 쓸 수 있습니다 . 클래스 객체에 부여 할 수있는 모든 기능을 함수 객체에 제공 할 수 있습니다.squareoperator()

# C++ function object
class Foo_class {
    private:
        int counter;     
    public:
        Foo_class() {
             counter = 0;
        }
        void operator() () {  
            counter++;
            printf("counter is %d\n", counter);
        }     
   };
   Foo_class foo;

파이썬에서는 operator()메소드 이름이 __call__다음과 같은 것을 제외하고 오버로드 할 수도 있습니다 .

클래스 정의는 다음과 같습니다.

class Foo_class:
    def __init__(self): # __init__ is similair to a C++ class constructor
        self.counter = 0
        # self.counter is like a static member
        # variable of a function named "foo"
    def __call__(self): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
foo = Foo_class() # call the constructor

사용되는 클래스의 예는 다음과 같습니다.

from foo import foo

for i in range(0, 5):
    foo() # function call

콘솔에 출력되는 내용은 다음과 같습니다.

counter is 1
counter is 2
counter is 3
counter is 4
counter is 5

함수가 입력 인수를 사용하도록하려면 다음을 추가하십시오 __call__.

# FILE: foo.py - - - - - - - - - - - - - - - - - - - - - - - - -

class Foo_class:
    def __init__(self):
        self.counter = 0
    def __call__(self, x, y, z): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
        print("x, y, z, are %d, %d, %d" % (x, y, z));
foo = Foo_class() # call the constructor

# FILE: main.py - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

from foo import foo

for i in range(0, 5):
    foo(7, 8, 9) # function call

# Console Output - - - - - - - - - - - - - - - - - - - - - - - - - - 

counter is 1
x, y, z, are 7, 8, 9
counter is 2
x, y, z, are 7, 8, 9
counter is 3
x, y, z, are 7, 8, 9
counter is 4
x, y, z, are 7, 8, 9
counter is 5
x, y, z, are 7, 8, 9

3

포화 n + = 1

def foo():
  foo.__dict__.setdefault('count', 0)
  foo.count += 1
  return foo.count

3

글로벌 선언은이 기능을 제공합니다. 아래 예제 ( "f"를 사용하기 위해 Python 3.5 이상)에서 카운터 변수는 함수 외부에서 정의됩니다. 함수에서 전역으로 정의하면 함수 외부의 "글로벌"버전을 함수에서 사용할 수 있어야합니다. 따라서 함수가 실행될 때마다 함수 외부의 값을 유지하면서 함수 외부의 값을 수정합니다.

counter = 0

def foo():
    global counter
    counter += 1
    print("counter is {}".format(counter))

foo() #output: "counter is 1"
foo() #output: "counter is 2"
foo() #output: "counter is 3"

올바르게 사용하면 같은 방식으로 작동합니다. c 코드와의 차이점은 OP의 c 예에서 카운터 변수는 함수에 의해서만 만질 수 있다는 것입니다. 파이썬의 전역 변수는 스크립트의 어느 곳에서나 사용하거나 변경할 수 있습니다
MortenSickel

2

파이썬 메소드 내부의 정적 변수

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

나는 개인적으로 데코레이터에게 다음을 선호합니다. 각자 자신에게.

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)

3
화 내지 마십시오. 그러나이 솔루션은 "대기업 스타일"을 상기시켜줍니다. :-) willa.me/2013/11/the-six-most-common-species-of-code.html
JJC

예, 이식 불가능한 사용 (일반적으로 스택 조작은 CPython 구현 세부 사항이며 PyPy, Jython, IronPython에서 의존 할 수있는 것이 아닙니다), 취약한 스택 조작, 매번 사용할 때마다 십여 개의 함수 호출 인 방법은 더 간단한보다는 장식 ... </ S>
ShadowRanger

1

이 답변은 @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속성 스타일 액세스, la JavaScript를 지원하는 사전 인 이라는 클래스를 사용합니다 ( 2000 년경에 대한 원본 기사 참조 ).

통해 설치할 수 있습니다 pip install bunch

다음과 같이 손으로 쓸 수도 있습니다.

class Bunch(dict):
    def __init__(self, **kw):
        dict.__init__(self,kw)
        self.__dict__ = self

참고 : types.SimpleNamespace(3.3 이후 사용 가능)은 즉시이 동작을 지원합니다 (CPython의 C로 구현되므로 최대한 빠릅니다).
ShadowRanger

0

다니엘의 답을 바탕으로 (추가) :

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 클래스가 싱글 톤 클래스로 제한적으로 정의되면 이상적입니다. 이것은 더 파이썬으로 만들 것입니다.


-1

물론 이것은 오래된 질문이지만 업데이트를 제공 할 수 있다고 생각합니다.

성능 인수가 더 이상 사용되지 않는 것 같습니다. 동일한 테스트 스위트가 siInt_try 및 isInt_re2에 대해 유사한 결과를 제공하는 것으로 보입니다. 물론 결과는 다양하지만 이것은 Xeon W3550을 사용하는 커널 4.3.01에서 Python 3.4.4를 사용하는 컴퓨터의 한 세션입니다. 여러 번 실행했는데 결과가 비슷해 보입니다. 전역 정규 표현식을 정적 함수로 옮겼지만 성능 차이는 무시할 만합니다.

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

성능 문제가 발생하면 try / catch가 가장 미래 지향적이고 모서리가 확실한 코드를 생성하므로 함수로 감싸는 것 같습니다.


1
여기서 무엇을 비교하고 있습니까? 이것은 다른 답변에 대한 의견처럼 보이지만 어떤 답변에 대해서는 명확하지 않으며 질문 자체에는 대답하지 않습니다.
ShadowRanger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.