関数内の静的変数に相当するPythonは何ですか?


631

このC / C ++コードに相当する慣用的なPythonは何ですか?

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

具体的には、クラスレベルではなく、関数レベルで静的メンバーをどのように実装しますか?また、関数をクラスに配置すると、何かが変わりますか?


22
そこでNOが、私は怖い等価性。関数属性を使ってデコレータハックを実行しても、悲しいことに、外部の変数にアクセスできます。さらに、関数内で関数名をハードコーディングする必要がありますが、これは非常に面倒です。従来の_接頭辞の代わりに、クラスまたはモジュールのグローバル変数を使用することをお勧めします。
lpapp

8
Cプログラマー以外の場合、[ stackoverflow.com/questions/5033627/…関数内の静的変数はその関数のスコープ内でのみ表示されますが、その存続期間はプログラムの存続期間全体であり、初期化は1回だけです)。基本的に、関数呼び出し間で存続する永続的なカウンターまたはストレージ変数。
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のインスタンスは1つしかありません-この1つの関数です。すべての呼び出しは同じ変数にアクセスします。
Claudiu、

121
これを掘り下げて申し訳ありませんが、私はむしろif "counter" not in foo.__dict__: foo.counter = 0の最初の行として置きたいと思いfoo()ます。これは、関数外のコードを回避するのに役立ちます。ただし、これが2008年に可能だったかどうかはわかりません。PS静的関数変数を作成する可能性を検索しているときにこの答えを見つけたので、このスレッドはまだ「生きている」:)
binaryLV

8
@binaryLV:私はおそらく最初のアプローチよりもそれを好みます。最初のアプローチの問題は、すぐには明らかではなくfoofoo.counter = 密接に関連していることです。ただし、デコレータを呼び出さない方法はなく、意味的には何をするのかが意味的に明確であるため、最終的にはデコレータアプローチを優先します(特に、使用する必要がある後者の場合@static_var("counter", 0)よりもif "counter" not in foo.__dict__: foo.counter = 0、私の目にはわかりやすく、理解しやすい)変更される可能性のある関数名(2回))。
Claudiu 2013年

6
@lpapp:静的変数のポイントが何であるかによって異なります。これは、複数の関数呼び出しで同じ値になるといつも思っていましたが、これで十分です。あなたが言ったように、私はそれを変数非表示について考えたことはありませんが、これはそうではありません。
Claudiu 2014

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

222

関数に属性を追加して、静的変数として使用できます。

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 2013

12
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1代わりに例外を使用して、同じことを行う必要があります。
sleblanc 2013

例外は例外的な状況で使用する必要があります。つまり、正常に開いた入力ファイルが突然使用できなくなるなど、プログラマーが予期しない事態が発生します。これは予想される状況です。ifステートメントの方が理にかなっています。
ハックソー

11
@Hack_Saw:まあ、これはPythonicです(許可よりも許しを求める方がいいです)。これは、ifのコストを節約できるため、Python最適化手法で実際に推奨されます(ただし、時期尚早の最適化はお勧めしません)。例外的なケースに関するルール:1.ある意味では、失敗はここでは例外的なケースです。それは一度だけ起こります。2.ルールは例外の使用(つまり、発生)に関するものだと思います。これは、動作すると予想されるものの、バックアップ計画を持っているものの例外をキャッチしています。これは、ほとんどの言語で一般的です。
leewz 2014年

@leewangzhong:例外を発生させないブロックを囲むことはtryコストを追加しますか?ちょっと興味があるんだけど。
trss 2014

202

次のことも検討できます。

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

推論:

  • 多くのpythonic(「許可ではなく許しを求める」)
  • if分岐の代わりに例外を使用(1回だけスロー)(StopIteration例外と考えてください

11
私は長い間Pythonを使用していませんでしたが、これは言語の暗黙の要素の1つを満たしていますそれが(かなり)簡単でない場合は、間違っています
ZX9

「self.foo.counter = 1」は、AttributeErrorを再度発生させます。
villasv

16
これは正しい解決策であり、モジュールが実行されたときやモジュールから何かがインポートされたときではなく、関数が呼び出されたときに初期化コードが実行されるため、これが受け入れられる答えになるはずです。現在受け入れられている答え。Pythonデコレータ関数の実行を参照してください。巨大なライブラリモジュールがある場合は、インポートしない関数を含め、すべてのデコレータが実行されます。
ニルスリンデマン2017年

3
より単純なアプローチ:def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
TheCuriousOne '12

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 2008年

1
@bouvard静的変数を必要とする再帰関数の場合、これは本当にうまくいく唯一のものです。
ライフバランス2017年

1
私はいくつかのアプローチを試しましたが、これがpythonicとして受け入れられるようになりたいと思います。def foo(arg1, arg2, _localstorage=DataClass(counter=0))私のようないくつかの意味のある名前で、それは読みやすいと思います。もう1つの優れた点は、関数名を簡単に変更できることです。
VPfB 2018年

2
どうしてそんなことをしてはいけないと言うの?私には完全に合理的に見えます!
コンスタンティン

1
@VPfB:一般的なストレージの場合、を使用してtypes.SimpleNamespacedef foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):特別なクラスを定義する必要なく作成できます。
ShadowRanger

43

多くの人々はすでに「hasattr」のテストを提案していますが、もっと簡単な答えがあります:

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

try / exceptなし、hasattrのテストなし、デフォルトのgetattrのみ。


2
たとえば、funcをそこに置くときは、getattrの3番目のパラメーターに注意してください。def func():def foo():return 1112 func.counter = getattr(func、 'counter'、foo())+ 1呼び出したときfunc、fooは常に呼び出されます!
Codefor

1
funcが呼び出されるたびにgetattrを呼び出すだけです。パフォーマンスに問題がない場合は問題ありませんが、それがtry / exceptである場合は勝者になります。
マークローレンス

2
@MarkLawrence:実際には、少なくとも私のWindows x64 3.8.0インストールでは、この回答とravwojdylaの同等のtry/ exceptベースのアプローチのパフォーマンスの違いはかなり無意味です。単純なipython %%timeitマイクロベンチマークでは、try/ のコストがexceptコールあたり255 nsでしたが、getattrベースのソリューションでは263 nsでした。はい、try/の方exceptが高速ですが、正確に「勝つ」というわけではありません。それは小さなマイクロ最適化です。より明確に見えるコードを記述します。このような些細なパフォーマンスの違いを心配する必要はありません。
ShadowRanger

@ShadowRangerのベンチマークに感謝します。MarkLawrenceの発言について2年間疑問に思っていたのですが、あなたが調査を行ってくれてとても嬉しいです。私は間違いなくあなたの最後の文に同意します-「コードがより明確に見えるように書いてください」-それがまさに私がこの答えを書いた理由です。
ジョナサン

28

以下は、外部初期化呼び出しを必要としない完全にカプセル化されたバージョンです。

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))
    ...

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
ですか

if句が不要なネストを追加するため、理想的ではありません。この状況では、setdefaultを使用します
Riaz Rizvi

27

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__()、そのクラスでメソッドを定義することにより、呼び出し可能にすることができます。


15
関数はすでにオブジェクトなので、これは不要なレイヤーを追加するだけです。
DasIch、2011年

これが実際に良いアイデアであるという長い意見については、このSOの回答を参照してください。 stackoverflow.com/questions/460586。そのようなクラスをシングルトンにすることも、おそらくこのstackoverflow.com/questions/6760685のようにするのも良い考えだと私は同意します。@ S.Lottが「...カウンターをクラス定義に移動...」とはどういう意味かわかりません。なぜなら、それはすでにクラス変数の位置にあるように見えるからです。
Reb.Cabin、2015年

1
私の調査によると、このクラステクニックは、このページに提示されているアプローチの中で最も「Pythonic」であるように見え、トリックを最小限にしています。したがって、私はそれを、新しいPython開発者として、関数内のCの静的な変数の置き換えに最適なものとして採用することを計画しています。
ガブリエルステープルズ

1
foo1 = Foo()およびfoo2 = Foo()が必要な場合はどうなりますか?
マークローレンス

@MarkLawrence次に、呼び出し可能なクラスの2つの異なるインスタンスがあり、それぞれに独自のカウンターがあります。fooシングルトンとして提供されるインスタンスを使用していない場合に期待するべきことはどれですか。
アーロンマクミリン

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 counternonlocalちょうどあなたがネストされた関数に閉鎖状態に書き込むことができます)。関数に属性をアタッチする理由は、関数に固有の状態のグローバル名前空間を汚染しないようにするためです。そのため、2つの関数が独立したを必要とする場合でも、ハッカーの作業を行う必要はありませんcounter。このソリューションは拡張できません。関数の属性は行います。kdbの答えはどのようにnonlocal役立つかですが、複雑さが増します。
ShadowRanger

えーと、ファクトリ関数やデコレータの複雑さは、これを頻繁に行わない限りやり過ぎだと思います。その場合、デザインは既に少し臭いです。1回限りの場合は、非ローカルカウンターを追加するだけです。命名規則についての回答に少し追加しました。また、私はお勧めの理由nonlocalオーバーは、globalご指摘のように正確である-それは厳密により状況で動作します。
cbarrick

8

関数の属性を静的変数として使用すると、いくつかの潜在的な欠点があります。

  • 変数にアクセスするたびに、関数の完全な名前を書き出す必要があります。
  • 外部のコードは変数に簡単にアクセスでき、値を変更できます。

2番目の問題の慣用的なpythonは、おそらく、変数にアクセスすることを意図していないことを示すために、先頭に下線を付けて変数に名前を付ける一方で、事後にアクセスできるようにします。

代替案はnonlocal、Python 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)

上記のvincentのコードと同様に、これは関数デコレータとして使用され、静的変数には関数名を接頭辞としてアクセスする必要があります。このコードの利点(確かに誰でもそれを理解できるほど賢いかもしれませんが)は、複数の静的変数を持ち、より従来的な方法でそれらを初期化できることです。


7

少し読みやすくなりますが、より冗長になります(Zen of Python:明示的の方が暗黙的よりも優れています)。

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

これがどのように機能するかの説明については、ここを参照してください。


このコードが機能する理由を詳しく説明できますか?2番目foo()は、関数定義で指定された値にディクショナリーを再初期化する必要があります(したがって、カウンターキーの値は0です)。なぜそれはしないのですか?
ラファエム2017

3
@raffamaiden:デフォルト引数は、関数が定義されるときに一度だけ評価され、関数が呼び出されるたびに評価されません。
ダニエルK.

6
_counter = 0
def foo():
   グローバル_counter
   _counter + = 1
   'counter is'、_counter

Pythonでは通常、アンダースコアを使用してプライベート変数を示します。Cで関数内で静的変数を宣言する唯一の理由は、関数の外で静的変数を非表示にすることです。これは、実際には慣用的なPythonではありません。


4

いくつかのアプローチを試した後、@ warvariucの回答の改良版を使用します。

import types

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

3

慣用的な方法は、使用することですクラスをの属性を持つことができ、。インスタンスを分離しないようにする必要がある場合は、シングルトンを使用します。

「静的」変数をPythonに偽装または変更する方法はいくつかあります(これまで述べていないのは、可変のデフォルト引数を持つことです)。これは、Pythonの慣用法ではありませんな方法ではありません。クラスを使用するだけです。

または、使用パターンが適切な場合はジェネレーター。


スタンドアロンの再帰関数の場合、default引数は最もエレガントなものです。
ライフバランス2017年

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;

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

3

ソリューションn + = 1

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

3

グローバル宣言はこの機能を提供します。以下の例(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"

これは、正しく使用すれば同じように機能します。cコードとの違いは、OPのcの例では、カウンター変数は関数によってのみ操作できるということです。Pythonのグローバル変数は、スクリプトの任意の場所で使用または変更できます
MortenSickel

2

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

私は個人的には以下のものをデコレータよりも好みます。それぞれ独自に。

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))

リマーク

私のメソッドは、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に実装されているため、可能な限り高速です)。
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)

私がこの部分を追加したかったのは、静的変数は実際の例として、ある値による増分だけでなく、静的変数がある値と等しいかどうかもチェックするためです。

静的変数は引き続き保護され、関数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的になります。


-1

確かにこれは古い質問ですが、私はいくつかの更新を提供する可能性があると思います。

パフォーマンスの議論は時代遅れのようです。同じテストスイートは、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は最も将来性があり、コーナーケースに耐えるコードを生成するように思われるため、関数でラップする


1
ここでも何を比較していますか?これは他の回答に対するコメントのように見えますが、どの回答かは明確ではなく、質問自体には回答しません。
ShadowRanger
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.