Bir fonksiyon içindeki statik değişkenlerin Python eşdeğeri nedir?


630

Bu C / C ++ kodunun deyimsel Python eşdeğeri nedir?

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

spesifik olarak, statik eleman sınıf seviyesinin aksine fonksiyon seviyesinde nasıl uygulanır? Ve işlevi sınıfa yerleştirmek herhangi bir şeyi değiştirir mi?


22
Orada HAYIR korkarım eşitlik gösteren. Dekoratör hack işlev öznitelikleriyle yapsanız bile, dışarıdaki değişkene erişebilirsiniz, bu da noktayı maalesef yener. Dahası, çok sinir bozucu olan işlevdeki işlev adını sabit olarak kodlamanız gerekir. Klasik _önek yerine bir sınıf veya modül genel değişkenleri kullanmanızı öneririm .
lpapp

8
C-programcı olmayanlar için, bir işlev içindeki [ stackoverflow.com/questions/5033627/… statik değişkeni yalnızca bu işlevin kapsamı içinde görülebilir, ancak ömrü programın tüm ömrüdür ve yalnızca bir kez başlatılır). Temel olarak, işlev çağrıları arasında kalan kalıcı bir sayaç veya depolama değişkeni.
smci

2
@lpapp: bir tür sınıf üyesi . Diğer kodun görüntülemesini veya değiştirilmesini engelleyemeyeceğimiz konusunda haklısınız.
smci

Yanıtlar:


681

Biraz ters döndü, ancak bu işe yarayacak:

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

Sayaç başlatma kodunu en altta değil, üstte bir dekoratör oluşturabilirsiniz:

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

Sonra kodu şöyle kullanın:

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

foo.Maalesef hala öneki kullanmanızı gerektirecektir .

(Kredi: @ony )


23
sadece bir foo örneği vardır - bu tek işlev. tüm çağrılar aynı değişkene erişir.
Claudiu

121
Bunu kazdýđým için özür dilerim, ama if "counter" not in foo.__dict__: foo.counter = 0ilk hatlarýný tercih ederim foo(). Bu, fonksiyonun dışındaki kodlardan kaçınmaya yardımcı olur. Bu 2008 yılında geri mümkün olsa emin değilim. PS Statik fonksiyon değişkenleri yaratma olasılığını ararken bu cevabı
buldum

8
@binaryLV: Bunu muhtemelen ilk yaklaşıma tercih ederim. İlk yaklaşımdaki sorun, bunun hemen açık olmadığı foove foo.counter = yakından ilişkili olduğudur. dekoratör denilen olmayacak yolu yoktur olarak ancak, ben sonuçta, dekoratör yaklaşımı tercih ve anlamsal olarak o (ne daha açıktır @static_var("counter", 0)üzerinde daha kolay ve daha gözlerimi daha mantıklı if "counter" not in foo.__dict__: foo.counter = 0özellikle kullanımına sahip ikincisi olduğu gibi, değişebilecek işlev adı (iki kez).
Claudiu

6
@lpapp: Statik değişkenlerin noktasının ne olduğuna bağlıdır. Ben her zaman bu tatmin edici birden fazla işlev çağrıları arasında aynı değer olacağını düşündüm. Daha önce de söylediğin gibi, değişken saklamayla ilgili olmadım.
Claudiu

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

221

Bir işleve öznitelikler ekleyebilir ve bunu statik değişken olarak kullanabilirsiniz.

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

# attribute must be initialized
myfunc.counter = 0

Alternatif olarak, değişkeni işlevin dışında ayarlamak istemiyorsanız, hasattr()bir AttributeErroristisnayı önlemek için kullanabilirsiniz :

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

Her neyse, statik değişkenler oldukça nadirdir ve büyük olasılıkla bir sınıf içinde bu değişken için daha iyi bir yer bulmalısınız.


6
Neden if ifadesi yerine denemiyorsunuz?
ravwojdyla

12
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1bunun yerine istisnaları kullanarak aynı şeyi yapmalıdır.
sleblanc

İstisnalar istisnai durumlar için kullanılmalıdır, örn. Aniden başarılı bir şekilde açmış olduğu bir girdi dosyası gibi programlayıcının beklediği durumlar gerçekleşmez. Bu beklenen bir durum, if ifadesi daha anlamlı.
Hack Saw

11
@Hack_Saw: Bu Pythonic (aftan izin istemekten daha iyidir). Bu aslında bir Python optimizasyon tekniklerinde tavsiye edilir, çünkü bir if maliyeti kaydeder (gerçi erken optimizasyon tavsiye etmiyorum). İstisnai durumlar hakkındaki kuralınız: 1. Başarısızlık bir anlamda burada istisnai bir durumdur. Sadece bir kez olur. 2. Bence bu kural istisnaları kullanmak (yani yükseltmek) ile ilgilidir. Bu, çalışmayı beklediğiniz bir şey için bir istisna yakalar, ancak çoğu dilde yaygın olan bir yedekleme planına sahiptir.
leewz

@ leewangzhong: İçinde istisna oluşturmayan bir bloğun tryeklenmesi herhangi bir maliyet getirir mi? Sadece merak.
trss

201

Bir de düşünülebilir:

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

Akıl Yürütme:

  • çok pitonik ("af değil izin iste")
  • ifşube yerine istisna kullanın (yalnızca bir kez atıldı) ( StopIteration istisnasını düşünün )

11
Python'u uzun süredir yapmıyorum, ancak bu dilin örtük anlamlarından birini tatmin ediyor: (oldukça) kolay değilse, yanlış yapıyorsunuz .
ZX9

Sınıf yöntemleriyle hemen çalışmadı, "self.foo.counter = 1" AttributeError öğesini yeniden yükseltiyor.
villasv

16
Bu doğru çözümdür ve kabul edilen cevap olmalıdır, çünkü modül çalıştırıldığında veya ondan bir şey alındığında değil, işlev çağrıldığında başlatma kodu çalıştırılacaktır, bu durumda dekoratör yaklaşımını kullanırsanız şu anda kabul edilen cevap. Bkz. Python dekoratör işlevinin yürütülmesi . Büyük bir kütüphane modülünüz varsa, içe aktarmadığınız işlevler de dahil olmak üzere her dekoratör çalıştırılır.
Nils Lindemann

3
Daha basit bir yaklaşım: def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
TheCuriousOne

5
@MANU Kullanımı hasattr() için kullanımı daha basit ve daha az verimli.
moooeeeep

48

Diğer cevaplar bunu nasıl yapmanız gerektiğini göstermiştir. İşte yapmamanız gereken bir yol:

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

Varsayılan değerler, her işlev yürütüldüğünde değil, yalnızca işlev ilk değerlendirildiğinde başlatılır, böylece statik değerleri depolamak için bir liste veya başka bir değiştirilebilir nesne kullanabilirsiniz.


Bunu denedim, ama bazı nedenlerden dolayı, fonksiyon parametresi 0'a değil 140'a başlıyor. Neden bu?
andrewdotnich

1
@bouvard Statik değişken gerektiren özyinelemeli işlevler için, gerçekten iyi okuyan tek işlev budur.
cankurtaran

1
Ben birkaç yaklaşım denedim ve keşke bu pitonik olarak kabul edilir. def foo(arg1, arg2, _localstorage=DataClass(counter=0))Benim gibi bazı anlamlı isimlerle iyi okunabilir buluyorum. Başka bir iyi nokta, kolay işlev yeniden adlandırmadır.
VPfB

2
Neden böyle yapmaman gerektiğini söylüyorsun? Benim için mükemmel görünüyor!
Konstantin

1
@VPfB: Genel depolama için şunu kullanabilirsiniz types.SimpleNamespaceyapma, def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):özel bir sınıf tanımlamak gerek kalmadan.
19:25

43

Birçok kişi zaten 'hasattr' testini önerdi, ancak daha basit bir cevap var:

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

Hiçbir deneyin / hariç, hiçbir test hasattr, sadece bir varsayılan ile getattr.


2
oraya bir fonk yerleştirdiğinizde getattr'un üçüncü parm'ına dikkat edin, örneğin: def func (): def foo (): geri dönüş 1112 func.counter = getattr (func, 'counter', foo ()) + 1 aradığınızda func, foo her zaman çağrılır!
Kod

1
Her işlev çağrıldığında sadece getattr çağrısı. Performans bir sorun değilse, gayet iyi eğer deneyin / hariç eller aşağı kazanacaktır.
Mark Lawrence

2
@MarkLawrence: Aslında, en azından Windows x64 3.8.0 kurulumumda, bu cevap ve ravwojdyla'nın eşdeğer try/ excepttabanlı yaklaşımı arasındaki performans farkı oldukça anlamsız. Basit bir ipython %%timeitmikrobenchmark , temel çözüm için 263 ns'ye karşılık çağrı başına 255 ns'de try/ maliyetini verdi . Evet, / daha hızlıdır, ancak tam olarak "eller aşağı kazanmak" değildir; küçük bir mikro optimizasyon. Kodun daha net göründüğü her şeyi yazın, bunun gibi önemsiz performans farkları hakkında endişelenmeyin. exceptgetattrtryexcept
19:47

@ShadowRanger bunu kıyasladığınız için teşekkürler. MarkLawrence'ın 2 yıldır yaptığı açıklamaları merak ediyorum ve araştırmayı yaptığınız için çok mutluyum. Son cümlenize kesinlikle katılıyorum - "kodu daha net görünen her şeyi yazın" - bu yüzden bu yanıtı yazdım.
Jonathan

28

Harici başlatma çağrısı gerektirmeyen tamamen kapsüllenmiş bir sürüm:

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

Python'da işlevler nesnedir ve özel özellik aracılığıyla bunlara üye değişkenleri ekleyebilir veya ekleyebiliriz __dict__. Yerleşik vars()getiri özel nitelik__dict__ .

EDIT: Not, alternatif try:except AttributeErrorcevap aksine, bu yaklaşım ile değişken başlatmadan sonra her zaman kod mantığı için hazır olacak. Bence try:except AttributeErroraşağıdakilere alternatif daha az KURU olacak ve / veya garip akışına sahip:

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: Yukarıdaki yaklaşımı yalnızca işlev birden çok yerden çağrıldığında öneririm. Bunun yerine işlev yalnızca bir yerde çağrılırsa, kullanmak daha iyidir 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
bununla ilgili tek sorun, gerçekten düzgün olmaması ve bu kalıbı kullanmak istediğinizde kodu kesip yapıştırmanız gerekiyor ... bu yüzden bir dekoratör kullanmam
Claudiu

2
muhtemelen şöyle bir şey kullanmalıdırtry: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
endolith

2
Lütfen X not in Yyerine kullanın not X in Y(veya sadece daha benzer görünümlü bir karşılaştırma yapmak için kullanıyorsanız, kullanmanızı öneririz hasattr)
Nick T

Buna ne dersiniz: def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
TheCuriousOne

ideal değildir, çünkü eğer fıkra gereksiz yere yuvalama ekler, bu durumda setdefault tercih ederim
Riaz Rizvi

27

Python'un statik değişkenleri yoktur, ancak çağrılabilir bir sınıf nesnesini tanımlayıp sonra bir işlev olarak kullanarak onu taklit edebilirsiniz. Ayrıca bu cevaba bakınız .

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__Bir sınıfın (nesnenin) bir örneğini kendi adına göre çağrılabilir hale getiren not . Bu yüzden foo()yukarıda çağırmak sınıfın __call__yöntemini çağırır . Belgelerden :

Rasgele sınıf örnekleri, sınıflarında bir __call__()yöntem tanımlanarak çağrılabilir hale getirilebilir .


15
İşlevler zaten nesnelerdir, bu yüzden gereksiz bir katman ekler.
Dasich

Bunun gerçekten iyi bir fikir olduğuna dair uzun bir görüş için bu SO cevabına bakın. stackoverflow.com/questions/460586 . Bu tür bir sınıfı tek başına yapmanın, belki de bu stackoverflow.com/questions/6760685 gibi , iyi bir fikir olacağını kabul ediyorum. @ S.Lott'un "... sayacı sınıf tanımına taşı ..." ile ne anlama geldiğini bilmiyorum çünkü zaten sınıf-değişken konumunda görünüyor bana.
Reb.Cabin

1
Araştırmamdan yola çıkarak, bu sınıf tekniği bu sayfada sunulan yaklaşımların en "Pythonic "'i gibi görünüyor ve en az hileyi kullanıyor. Bu nedenle, yeni bir Python geliştiricisi olarak, işlevlerdeki C-statik benzeri değişkenlerin yerine geçmem yerine geçmeyi planlıyorum.
Gabriel Staples

1
Foo1 = Foo () ve foo2 = Foo () istersem ne olur?
Mark Lawrence

@MarkLawrence Ardından, her biri kendi sayaçlarına sahip iki farklı çağırılabilir sınıf örneğiniz olur. Hangi foosingleton olarak sağlanan örneği kullanmıyorsanız tam olarak ne beklenir .
Aaron McMillin

14

Yineleyici oluşturmak için bir jeneratör işlevi kullanın.

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

Sonra şöyle kullanın

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

Bir üst sınır istiyorsanız:

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

Yineleyici sonlanırsa (yukarıdaki örnekte olduğu gibi), doğrudan

for i in foo_gen(20):
    print i

Tabii ki, bu basit durumlarda xrange kullanmak daha iyidir :)

İşte getiri beyanı ile ilgili belgeler .


11

Diğer çözümler, genellikle başlatmayı işlemek için kıvrık mantıkla işleve bir sayaç niteliği ekler. Bu yeni kod için uygun değil.

Python 3'te doğru yol nonlocalifadeyi kullanmaktır :

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

Teknik özellikler için PEP 3104'e bakınız .nonlocal .

Sayacın modüle özel olması isteniyorsa, _counterbunun yerine adlandırılmalıdır .


Python 3'ten önce bile, bunu her zaman bir global counter yerine ifadenonlocal counter ( nonlocalsadece iç içe bir işlevde kapanma durumuna yazmanıza izin verir). İnsanların işleve bir öznitelik eklemesinin nedeni, işleve özgü durum için genel ad alanını kirletmekten kaçınmaktır, bu nedenle iki işlevin bağımsız olması gerektiğinde hackier şeyler bile yapmanız gerekmez counter. Bu çözüm ölçeklenmemektedir; işlev üzerindeki öznitelikleri yapar. kdb'nin cevabı nasıl nonlocalyardımcı olabilir, ancak karmaşıklık ekler.
ShadowRanger

Eh, bir fabrika fonksiyonunun veya dekoratörün karmaşıklığı, bunu çok fazla yapmadığınız sürece aşırı derecede bence ve bu durumda tasarım zaten biraz kokulu. Bir kereye mahsus olmak için, yerel olmayan sayacı ekleyin ve onunla yapın. Adlandırma kuralları ile ilgili cevaba biraz ekledim. Ayrıca, tavsiye nonlocaletmemin nedeni globaltam olarak işaret ettiğiniz gibi - kesinlikle daha fazla koşullarda çalışıyor.
cbarrick

8

Bir işlevin özniteliğini statik değişken olarak kullanmanın bazı dezavantajları vardır:

  • Değişkene her erişmek istediğinizde, işlevin tam adını yazmanız gerekir.
  • Dış kod, değişkene kolayca erişebilir ve değerle karışabilir.

İkinci sayı için deyimsel python, değişkene, gerçekten sonra erişilebilir kalırken erişilmesi gerektiğini belirtmek için önde gelen bir alt çizgiyle adlandırmak olacaktır.

Alternatif olarak nonlocal, python 3'teki anahtar kelimeyle desteklenen sözcüksel kapanışları kullanan bir model olabilir .

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

Ne yazık ki bu çözümü bir dekoratöre kapsüllemek için hiçbir yol bilmiyorum.


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)

Yukarıdaki vincent koduna benzer şekilde, bu bir işlev dekoratörü olarak kullanılır ve statik değişkenlere işlev adı ile önek olarak erişilmelidir. Bu kodun avantajı (herkesin anlayabileceği kadar akıllı olmasına rağmen) avantajı, birden fazla statik değişkene sahip olmanız ve bunları daha geleneksel bir şekilde başlatmanızdır.


7

Biraz daha okunabilir, ancak daha ayrıntılı (Python Zen: açık, örtük olmaktan iyidir):

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

Bunun nasıl çalıştığına dair bir açıklama için buraya bakın .


bu kodun neden çalıştığını açıklayabilir misiniz? İkincisi foo()sözlüğü işlev tanımında belirtilen değere yeniden başlatmalıdır (böylece sayaç anahtarı 0 değerine sahip olmalıdır). Neden yok?
raffaem

3
@raffamaiden: Varsayılan bağımsız değişkenler, işlev her çağrıldığında değil, işlev tanımlandığında yalnızca bir kez değerlendirilir.
Daniel K.

6
_counter = 0
def foo ():
   global _counter
   _counter + = 1
   'counter is' yazdır, _counter

Python özel değişkenleri belirtmek için alt çizgi kullanır. C'nin fonksiyon içindeki statik değişkeni bildirmesinin tek nedeni, onu gerçekten deyimsel Python olmayan fonksiyonun dışında gizlemektir.


4

Birkaç yaklaşımı denedikten sonra @ warvariuc'un cevabının geliştirilmiş bir versiyonunu kullanıyorum:

import types

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

3

Deyimsel yolu kullanmaktır sınıfı özelliklerine sahip olabilir. Ayrı olmamak için örneklere ihtiyacınız varsa, bir singleton kullanın.

"Statik" değişkenleri Python'a taklit etmenin veya karıştırmanın birkaç yolu vardır (şimdiye kadar bahsedilmeyen biri değiştirilebilir bir varsayılan argümana sahip olmaktır), ancak bunu yapmak için Pythonic, deyimsel bir yol değildir. Sadece bir sınıf kullan.

Ya da kullanım modeliniz uygunsa bir jeneratör.


Bağımsız özyinelemeli işlevler için defaultargüman en zarif olanıdır.
cankurtaran

3

Bu sorudan istendiğinde , biraz daha hoş olabilecek ve hem yöntemler hem de işlevler için aynı görünecek başka bir alternatif sunabilir miyim:

@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    

Kullanımı beğendiyseniz, uygulama:

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

Eğer korkak bir çağrı imzası kullanmak sakıncası yoksa, https://stackoverflow.com/a/279598/916373 gibi çağrılabilir nesne üzerinde başka (tavsiye edilmez!) Bir bükülme,

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

Statik bir yerel değişkene sahip bir işlev oluşturmak yerine, her zaman "işlev nesnesi" olarak adlandırılan şeyi oluşturabilir ve ona standart (statik olmayan) bir üye değişken verebilirsiniz.

C ++ ile yazılmış bir örnek verdiğiniz için, önce C ++ 'da bir "fonksiyon nesnesi" nin ne olduğunu açıklayacağım. Bir "fonksiyon nesnesi", aşırı yüklenmiş herhangi bir sınıftır operator(). Sınıfın örnekleri işlevler gibi davranacaktır. Örneğin, teknik olarak bir "işlev" değil , bir nesne (aşırı yüklenmiş ) int x = square(5);olsa bile yazabilirsiniz . Bir sınıf nesnesi verebileceğiniz özelliklerden herhangi birine bir işlev nesnesi verebilirsiniz.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'da, operator()yöntemin adlandırılması dışında aşırı yüklenebiliriz __call__:

İşte bir sınıf tanımı:

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

Kullanılan sınıfın bir örneği:

from foo import foo

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

Konsola yazdırılan çıktı:

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

İşlevinizin girdi bağımsız değişkenleri almasını istiyorsanız, bunları da ekleyebilirsiniz __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

Ruh hali n + = 1

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

3

Genel bir bildirim bu işlevselliği sağlar. Aşağıdaki örnekte ("f" kullanmak için python 3.5 veya üstü ), sayaç değişkeni işlevin dışında tanımlanır. İşlevde global olarak tanımlanması, işlev dışındaki "global" sürümün işlev için kullanılabilir hale getirilmesi gerektiğini belirtir. Böylece işlev her çalıştığında, işlevin dışındaki değeri koruyarak işlevin dışındaki değeri değiştirir.

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"

Bu, doğru kullanıldığında aynı şekilde çalışır. C kodunun farkı OP'nin c örneğinde sayaç değişkenine sadece fonksiyon tarafından dokunulmasıdır.
Python'daki

2

Python yönteminin içindeki statik değişken

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

Şahsen aşağıdakileri dekoratörlere tercih ederim. Her birine kendi.

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
Lütfen rahatsız olmayın, ancak bu çözüm bana biraz "büyük şirket tarzı"
JJC

Evet, taşınabilir olmayan (genel olarak yığın manipülasyonu kullanmak, bir CPython uygulama detayıdır, PyPy, Jython, IronPython, ne var), kırılgan yığın manipülasyonu, her kullanımda yarım düzine fonksiyon çağrısı ile güvenebileceğiniz bir şey değildir olan yolu daha iyi bir basit daha dekoratör ... </ s>
ShadowRanger

1

Bu cevap @claudiu'nun cevabı üzerine kuruludur.

Statik bir değişkene erişmek istediğimde her zaman işlev adını eklemek zorunda kaldığımda kodumun daha az netleştiğini gördüm.

Yani, fonksiyon kodumda yazmayı tercih ederim:

print(statics.foo)

onun yerine

print(my_function_name.foo)

Benim çözümüm:

  1. staticsişleve bir öznitelik ekle
  2. işlev kapsamında, yerel bir değişkeni staticsdiğer ad olarakmy_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))

düşünce

Metodum Bunch, öznitelik stili erişimi ve bir la JavaScript'i destekleyen bir sözlük olan adlı bir sınıf kullanıyor ( bununla ilgili orijinal makaleye bakın , yaklaşık 2000)

Üzerinden kurulabilir pip install bunch

Ayrıca şöyle yazılabilir:

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

Not: types.SimpleNamespace(3.3'ten beri kullanılabilir) bu davranışı kutudan çıkarır (ve CPython'da C'de uygulanır, bu yüzden olabildiğince hızlıdır).
ShadowRanger

0

Daniel'in cevabı üzerine inşa (ekleme):

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)

Bu parçayı eklemek istememin nedeni, statik değişkenlerin sadece bir değere göre artış için değil, aynı zamanda gerçek bir yaşam örneği olarak statik varlığın bir değere eşit olup olmadığını kontrol etmesidir.

Statik değişken hala korunur ve yalnızca use_foo () işlevi kapsamında kullanılır

Bu örnekte, call to foo () işlevi tam olarak (karşılık gelen c ++ eşdeğerine göre) olarak çalışır:

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 sınıfı kısıtlayıcı olarak tektonlu bir sınıf olarak tanımlanırsa, bu ideal olacaktır. Bu onu daha pitonik yapar.


-1

Tabii bu eski bir soru ama sanırım bazı güncelleme sağlayabilirim.

Performans argümanı geçersiz görünüyor. Aynı test paketi siInt_try ve isInt_re2 için benzer sonuçlar veriyor gibi görünüyor. Tabii ki sonuçlar değişiyor, ancak bu benim bilgisayarımda Xeon W3550 ile 4.3.01 çekirdeğinde python 3.4.4 ile bir oturum. Birkaç kez çalıştırdım ve sonuçlar benzer görünüyor. Global normal ifadeyi statik fonksiyona taşıdım, ancak performans farkı göz ardı edilebilir.

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

Performans sorunu ortadan kalktığında, dene / yakala en gelecekteki ve köşe çantasına dayanıklı kodu üretecek gibi görünüyor.


1
Burada ne karşılaştırıyorsun? Bu, diğer cevaplar hakkında bir yorum gibi görünüyor, ancak hangilerinin olduğu açık değil ve sorunun kendisinin yanıtı yok.
ShadowRanger
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.