このC / C ++コードに相当する慣用的なPythonは何ですか?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
具体的には、クラスレベルではなく、関数レベルで静的メンバーをどのように実装しますか?また、関数をクラスに配置すると、何かが変わりますか?
このC / C ++コードに相当する慣用的なPythonは何ですか?
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、私の目にはわかりやすく、理解しやすい)変更される可能性のある関数名(2回))。
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分岐の代わりに例外を使用(1回だけスロー)(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))私のようないくつかの意味のある名前で、それは読みやすいと思います。もう1つの優れた点は、関数名を簡単に変更できることです。
types.SimpleNamespace、def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):特別なクラスを定義する必要なく作成できます。
多くの人々はすでに「hasattr」のテストを提案していますが、もっと簡単な答えがあります:
def func():
func.counter = getattr(func, 'counter', 0) + 1
try / exceptなし、hasattrのテストなし、デフォルトのgetattrのみ。
try/ exceptベースのアプローチのパフォーマンスの違いはかなり無意味です。単純なipython %%timeitマイクロベンチマークでは、try/ のコストがexceptコールあたり255 nsでしたが、getattrベースのソリューションでは263 nsでした。はい、try/の方exceptが高速ですが、正確に「勝つ」というわけではありません。それは小さなマイクロ最適化です。より明確に見えるコードを記述します。このような些細なパフォーマンスの違いを心配する必要はありません。
以下は、外部初期化呼び出しを必要としない完全にカプセル化されたバージョンです。
def fn():
fn.counter=vars(fn).setdefault('counter',-1)
fn.counter+=1
print (fn.counter)
Pythonでは、関数はオブジェクトであり、特別な属性を使用して、メンバー変数を単純に追加したり、モンキーパッチを適用したりできます__dict__。ビルトインvars()は特別な属性を返します__dict__。
編集:別のtry:except AttributeError答えとは異なり、このアプローチでは、変数は初期化後のコードロジックに対して常に準備ができていることに注意してください。try:except AttributeError次の代替案は、DRYが少なくなり、フローがぎこちなくなると思います。
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:関数が複数の場所から呼び出される場合のみ、上記のアプローチをお勧めします。代わりに関数が1つの場所でのみ呼び出される場合は、次のように使用することをお勧めします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
Pythonには静的変数はありませんが、呼び出し可能なクラスオブジェクトを定義し、それを関数として使用することで、偽の変数にすることができます。この回答もご覧ください。
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ちょうどあなたがネストされた関数に閉鎖状態に書き込むことができます)。関数に属性をアタッチする理由は、関数に固有の状態のグローバル名前空間を汚染しないようにするためです。そのため、2つの関数が独立したを必要とする場合でも、ハッカーの作業を行う必要はありませんcounter。このソリューションは拡張できません。関数の属性は行います。kdbの答えはどのようにnonlocal役立つかですが、複雑さが増します。
nonlocalオーバーは、globalご指摘のように正確である-それは厳密により状況で動作します。
関数の属性を静的変数として使用すると、いくつかの潜在的な欠点があります。
2番目の問題の慣用的なpythonは、おそらく、変数にアクセスすることを意図していないことを示すために、先頭に下線を付けて変数に名前を付ける一方で、事後にアクセスできるようにします。
代替案はnonlocal、Python 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)
上記のvincentのコードと同様に、これは関数デコレータとして使用され、静的変数には関数名を接頭辞としてアクセスする必要があります。このコードの利点(確かに誰でもそれを理解できるほど賢いかもしれませんが)は、複数の静的変数を持ち、より従来的な方法でそれらを初期化できることです。
少し読みやすくなりますが、より冗長になります(Zen of Python:明示的の方が暗黙的よりも優れています)。
>>> def func(_static={'counter': 0}):
... _static['counter'] += 1
... print _static['counter']
...
>>> func()
1
>>> func()
2
>>>
これがどのように機能するかの説明については、ここを参照してください。
foo()は、関数定義で指定された値にディクショナリーを再初期化する必要があります(したがって、カウンターキーの値は0です)。なぜそれはしないのですか?
慣用的な方法は、使用することですクラスをの属性を持つことができ、。インスタンスを分離しないようにする必要がある場合は、シングルトンを使用します。
「静的」変数をPythonに偽装または変更する方法はいくつかあります(これまで述べていないのは、可変のデフォルト引数を持つことです)。これは、Pythonの慣用法ではありませんな方法ではありません。クラスを使用するだけです。
または、使用パターンが適切な場合はジェネレーター。
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);場合でも記述できます。関数オブジェクトには、クラスオブジェクトに与えることができる任意の機能を与えることができます。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;
Pythonでは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
グローバル宣言はこの機能を提供します。以下の例(Python 3.5以降で「f」を使用)では、counter変数は関数の外部で定義されています。関数内でグローバルとして定義することは、関数の外部の「グローバル」バージョンを関数で使用できるようにする必要があることを意味します。そのため、関数が実行されるたびに、関数の外側の値が変更され、関数を超えて保持されます。
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.staticsfrom 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))
リマーク
私のメソッドは、BunchJavaScriptの属性スタイルのアクセスをサポートする辞書であるというクラスを使用します(元の記事を参照) 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)
私がこの部分を追加したかったのは、静的変数は実際の例として、ある値による増分だけでなく、静的変数がある値と等しいかどうかもチェックするためです。
静的変数は引き続き保護され、関数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がシングルトンクラスとして限定的に定義されている場合、それは理想的です。これにより、よりPython的になります。
確かにこれは古い質問ですが、私はいくつかの更新を提供する可能性があると思います。
パフォーマンスの議論は時代遅れのようです。同じテストスイートは、siInt_tryとisInt_re2に対して同様の結果を与えるようです。もちろん結果は異なりますが、これはXeon W3550を搭載したカーネル4.3.01上のpython 3.4.4を搭載した私のコンピューター上の1セッションです。何度か実行しましたが、結果は似ているようです。グローバル正規表現をstatic関数に移動しましたが、パフォーマンスの違いはごくわずかです。
isInt_try: 0.3690
isInt_str: 0.3981
isInt_re: 0.5870
isInt_re2: 0.3632
パフォーマンスの問題は解決されているため、try / catchは最も将来性があり、コーナーケースに耐えるコードを生成するように思われるため、関数でラップする
_接頭辞の代わりに、クラスまたはモジュールのグローバル変数を使用することをお勧めします。