이 C / C ++ 코드와 같은 관용적 인 파이썬은 무엇입니까?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
특히 클래스 수준이 아닌 함수 수준에서 정적 멤버를 어떻게 구현합니까? 그리고 함수를 클래스에 배치하면 아무것도 변경됩니까?
이 C / C ++ 코드와 같은 관용적 인 파이썬은 무엇입니까?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
특히 클래스 수준이 아닌 함수 수준에서 정적 멤버를 어떻게 구현합니까? 그리고 함수를 클래스에 배치하면 아무것도 변경됩니까?
답변:
약간 역전되었지만 작동해야합니다.
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 )
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
함수에 속성을 추가하고 정적 변수로 사용할 수 있습니다.
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
어쨌든 정적 변수는 다소 드물며 클래스 내 에서이 변수에 대한 더 좋은 곳을 찾아야합니다.
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
대신 예외를 사용하여 동일한 작업을 수행해야합니다.
try
비용이 추가됩니까? 그냥 궁금해서
또한 다음을 고려할 수 있습니다.
def foo():
try:
foo.counter += 1
except AttributeError:
foo.counter = 1
추리:
if
분기 대신 예외를 사용하십시오 ( StopIteration 예외를 생각하십시오 )def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
하는 것은 더 간단하지 않고 덜 효율적입니다.
다른 답변은 당신이 이것을 해야하는 방법을 보여주었습니다. 하지 말아야 할 방법은 다음과 같습니다.
>>> def foo(counter=[0]):
... counter[0] += 1
... print("Counter is %i." % counter[0]);
...
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>
기본값은 함수가 실행될 때마다가 아니라 함수가 처음 평가 될 때만 초기화되므로 목록 또는 기타 변경 가능한 객체를 사용하여 정적 값을 저장할 수 있습니다.
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
읽을 수 있습니다. 또 다른 좋은 점은 쉬운 기능 이름 바꾸기입니다.
types.SimpleNamespace
그것을 만드는, def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
특별한 클래스를 정의 할 필요없이.
많은 사람들이 이미 'hasattr'테스트를 제안했지만 더 간단한 답변이 있습니다.
def func():
func.counter = getattr(func, 'counter', 0) + 1
시도 / 제외, 테스트 hasattr 없음, 기본값으로 getattr.
try
/ except
기반 접근 방식 의 성능 차이 는 의미가 없습니다. 간단한 ipython
%%timeit
마이크로 벤치 마크는 호출 당 255ns try
/ 의 비용을 기반 솔루션의 except
경우 263ns에 비해 getattr
제공했습니다. 그렇다. try
/ except
는 빠르지 만 정확히 "손을 win 다"는 것은 아니다. 작은 미세 최적화입니다. 더 명확한 코드를 작성하십시오. 이와 같은 사소한 성능 차이에 대해 걱정하지 마십시오.
다음은 외부 초기화 호출이 필요하지 않은 완전히 캡슐화 된 버전입니다.
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))
...
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
파이썬에는 정적 변수가 없지만 호출 가능한 클래스 객체를 정의한 다음 함수로 사용하여 위조 할 수 있습니다. 이 답변도 참조하십시오 .
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__()
메소드를 정의하여 임의 클래스의 인스턴스를 호출 할 수 있습니다 .
foo
싱글 톤으로 제공되는 인스턴스 를 사용하지 않는 경우 정확히 무엇을 기대해야합니까 ?
생성기 함수를 사용하여 반복자를 생성하십시오.
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 문 에 대한 문서는 다음과 같습니다 .
다른 솔루션은 카운터 속성을 함수에 연결하며 일반적으로 초기화를 처리하기 위해 복잡한 논리를 사용합니다. 새로운 코드에는 적합하지 않습니다.
Python 3에서 올바른 방법은 nonlocal
명령문 을 사용하는 것입니다.
counter = 0
def foo():
nonlocal counter
counter += 1
print(f'counter is {counter}')
사양에 대해서는 PEP 3104 를 참조하십시오 .nonlocal
.
카운터가 모듈 전용 인 경우 _counter
대신 이름을 지정해야합니다 .
global counter
대신 명령문 으로이 작업을 수행 할 수 있습니다 nonlocal counter
( nonlocal
중첩 함수에서 클로저 상태에 쓸 수 있습니다). 사람들이 함수에 속성을 첨부하는 이유는 함수와 관련된 상태에 대한 전역 네임 스페이스를 오염시키지 않기 때문에 두 함수가 독립적 인을 필요로 할 때 해커 일을 할 필요가 없습니다 counter
. 이 솔루션은 확장되지 않습니다. 함수의 속성이 수행합니다. kdb의 대답 은 어떻게 nonlocal
도움 이 될 수 있지만 복잡성을 더합니다.
nonlocal
이상은 global
당신이 지적 정확히 - 그것은 엄격하게 많은 상황에서 작동합니다.
함수의 속성을 정적 변수로 사용하면 몇 가지 잠재적 인 단점이 있습니다.
두 번째 문제에 대한 관용적 파이썬은 아마도 밑줄로 변수의 이름을 지정하여 변수에 액세스 할 수 없다는 것을 알리고 사실 후에 액세스 할 수 있도록 유지합니다.
대안은 nonlocal
파이썬 3 의 키워드로 지원되는 어휘 클로저를 사용하는 패턴 입니다.
def make_counter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
counter = make_counter()
슬프게도이 솔루션을 데코레이터로 캡슐화 할 방법이 없습니다.
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)
위의 빈센트 코드와 마찬가지로 이것은 함수 데코레이터로 사용되며 함수 이름을 접두어로 사용하여 정적 변수에 액세스해야합니다. 이 코드의 장점은 누구나 알아낼 수있을만큼 똑똑 할 수는 있지만 여러 정적 변수를 사용하여보다 일반적인 방식으로 초기화 할 수 있다는 것입니다.
조금 더 읽기 쉽지만 더 장황합니다 (Zen of Python : 명시 적이 암시 적보다 낫습니다).
>>> def func(_static={'counter': 0}):
... _static['counter'] += 1
... print _static['counter']
...
>>> func()
1
>>> func()
2
>>>
foo()
는 사전을 함수 정의에 지정된 값으로 다시 초기화해야합니다 (따라서 카운터 키의 값은 0 임). 왜 그렇지 않습니까?
관용적 방법은 사용하는 클래스 속성을 가질 수있다. 인스턴스를 분리하지 않으려면 싱글 톤을 사용하십시오.
"정적"변수를 파이썬에 가짜로 만들거나 숨길 수있는 방법에는 여러 가지가 있지만 (지금까지 언급되지 않은 것은 가변적 인 기본 인수를 갖는 것입니다), 이것은 파이썬적인 관용적 방법이 아닙니다. 그냥 수업을 사용하십시오.
또는 사용 패턴에 맞는 발전기 일 수도 있습니다.
default
인수가 가장 우아합니다.
이 질문에 의해 프롬프트 되면 사용하기에 조금 더 좋을 수있는 다른 대안을 제시 할 수 있으며 메소드와 함수 모두 동일하게 보일 것입니다.
@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
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
정적 로컬 변수가있는 함수를 만드는 대신 항상 "함수 개체"라는 것을 만들고 표준 (비 정적) 멤버 변수를 제공 할 수 있습니다.
C ++로 작성된 예제를 제공 했으므로 먼저 C ++에 "함수 객체"가 무엇인지 설명하겠습니다. "함수 객체"는 단순히 오버로드 된 클래스입니다 operator()
. 클래스의 인스턴스는 함수처럼 동작합니다. 예를 들어, 오버로드 된 객체 이고 기술적으로 "함수"가 아닌 int x = square(5);
경우에도 쓸 수 있습니다 . 클래스 객체에 부여 할 수있는 모든 기능을 함수 객체에 제공 할 수 있습니다.square
operator()
# 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
글로벌 선언은이 기능을 제공합니다. 아래 예제 ( "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"
나는 개인적으로 데코레이터에게 다음을 선호합니다. 각자 자신에게.
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)
이 답변은 @claudiu의 답변을 기반으로합니다.
정적 변수에 액세스 할 때마다 항상 함수 이름을 앞에 붙여야 할 때 코드가 명확하지 않다는 것을 알았습니다.
즉, 함수 코드에서 다음과 같이 작성하는 것을 선호합니다.
print(statics.foo)
대신에
print(my_function_name.foo)
그래서 내 해결책은 다음과 같습니다.
statics
함수에 속성을 추가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로 구현되므로 최대한 빠릅니다).
다니엘의 답을 바탕으로 (추가) :
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 클래스가 싱글 톤 클래스로 제한적으로 정의되면 이상적입니다. 이것은 더 파이썬으로 만들 것입니다.
물론 이것은 오래된 질문이지만 업데이트를 제공 할 수 있다고 생각합니다.
성능 인수가 더 이상 사용되지 않는 것 같습니다. 동일한 테스트 스위트가 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가 가장 미래 지향적이고 모서리가 확실한 코드를 생성하므로 함수로 감싸는 것 같습니다.
_
접두사 대신 클래스 또는 모듈 전역 변수를 사용하는 것이 좋습니다 .