Jaki jest idiomatyczny odpowiednik Pythona w tym kodzie C / C ++?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
w szczególności, w jaki sposób implementuje się element statyczny na poziomie funkcji, a nie na poziomie klasy? I czy umieszczenie funkcji w klasie coś zmienia?
_
prefiksu.Odpowiedzi:
Trochę odwrócone, ale to powinno działać:
Jeśli chcesz, aby kod inicjalizacji licznika był na górze zamiast na dole, możesz utworzyć dekorator:
Następnie użyj kodu w następujący sposób:
foo.
Niestety nadal będzie wymagać użycia prefiksu.( Źródło : @ony )
źródło
if "counter" not in foo.__dict__: foo.counter = 0
jako pierwsze wierszefoo()
. Pomogłoby to uniknąć kodu poza funkcją. Nie jestem jednak pewien, czy było to możliwe w 2008 roku. PS Znalazłem tę odpowiedź, szukając możliwości utworzenia statycznych zmiennych funkcyjnych, więc ten wątek jest nadal „żywy” :)foo
ifoo.counter =
są one ściśle powiązane. jednak ostatecznie wolę podejście do dekoratora, ponieważ nie ma możliwości, aby dekorator nie został powołany i jest semantycznie bardziej oczywiste, co robi (@static_var("counter", 0)
jest łatwiejsze i ma więcej sensu dla moich oczu niżif "counter" not in foo.__dict__: foo.counter = 0
, szczególnie w tym ostatnim, którego musisz użyć nazwa funkcji (dwukrotnie), która może ulec zmianie).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Możesz dodać atrybuty do funkcji i użyć jej jako zmiennej statycznej.
Alternatywnie, jeśli nie chcesz ustawiać zmiennej poza funkcją, możesz użyć,
hasattr()
aby uniknąćAttributeError
wyjątku:W każdym razie zmienne statyczne są raczej rzadkie i powinieneś znaleźć lepsze miejsce dla tej zmiennej, najprawdopodobniej wewnątrz klasy.
źródło
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
powinien zrobić to samo, używając zamiast tego wyjątków.try
dodatkowe koszty? Po prostu ciekawy.Można również rozważyć:
Rozumowanie:
if
rozgałęzienia (pomyśl wyjątek StopIteration )źródło
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
do tego nie jest prostsze, a także mniej wydajne.Inne odpowiedzi pokazały, jak należy to zrobić. Oto sposób, w jaki nie powinieneś:
Wartości domyślne są inicjowane tylko podczas pierwszej oceny funkcji, a nie za każdym razem, gdy jest wykonywana, dzięki czemu do przechowywania wartości statycznych można użyć listy lub dowolnego obiektu zmiennego.
źródło
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
ja, uważam to za dobrze czytelne. Kolejną zaletą jest łatwa zmiana nazwy funkcji.types.SimpleNamespace
, dzięki czemudef foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
nie trzeba definiować specjalnej klasy.Wiele osób już sugerowało przetestowanie „hasattr”, ale istnieje prostsza odpowiedź:
Bez prób / z wyjątkiem, bez testowania hasattr, po prostu getattr z domyślnym.
źródło
try
/except
opartym na ravwojdyla podejściem jest dość bez znaczenia. Prostyipython
%%timeit
mikrobenchmark dał koszttry
/except
na 255 ns za połączenie, w porównaniu do 263 ns dlagetattr
rozwiązania bazowego. Tak,try
/except
jest szybszy, ale nie do końca „wygrywa rozdanie”; to drobna mikrooptymalizacja. Napisz dowolny kod, który wydaje się wyraźniejszy, nie martw się o takie trywialne różnice wydajności.Oto w pełni zamknięta wersja, która nie wymaga zewnętrznego wywołania inicjalizacji:
W Pythonie funkcje są obiektami i możemy po prostu dodawać do nich zmienne składowe lub małpować je za pomocą specjalnego atrybutu
__dict__
. Wbudowanyvars()
zwraca specjalny atrybut__dict__
.EDYCJA: Uwaga, w przeciwieństwie do alternatywy
try:except AttributeError
odpowiedzi, przy takim podejściu zmienna zawsze będzie gotowa na logikę kodu po inicjalizacji. Myślę, żetry:except AttributeError
alternatywą dla poniższych będzie mniej SUSZENIA i / lub niezręczny przepływ:EDYCJA 2: Polecam powyższe podejście tylko wtedy, gdy funkcja będzie wywoływana z wielu lokalizacji. Jeśli zamiast tego funkcja jest wywoływana tylko w jednym miejscu, lepiej użyć
nonlocal
:źródło
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
zamiastnot X in Y
(lub doradzać używanie, jeśli po prostu go używałeś w celu bardziej podobnego porównania między tym ahasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
Python nie ma zmiennych statycznych, ale można go sfałszować, definiując obiekt klasy na żądanie, a następnie używając go jako funkcji. Zobacz także tę odpowiedź .
Zauważ, że
__call__
umożliwia wywołanie klasy (obiektu) według własnej nazwy. Dlatego wywołaniefoo()
powyższego wywołuje__call__
metodę klasy . Z dokumentacji :źródło
foo
udostępnianej jako singleton.Użyj funkcji generatora, aby wygenerować iterator.
Następnie użyj go jak
Jeśli chcesz górny limit:
Jeśli iterator zakończy się (jak w powyższym przykładzie), możesz również zapętlić go bezpośrednio, np
Oczywiście w tych prostych przypadkach lepiej użyć xrange :)
Oto dokumentacja dotycząca deklaracji dochodu .
źródło
Inne rozwiązania dołączają do funkcji atrybut licznika, zwykle ze skomplikowaną logiką do obsługi inicjalizacji. Jest to nieodpowiednie dla nowego kodu.
W Pythonie 3 właściwym sposobem jest użycie
nonlocal
instrukcji:Zobacz PEP 3104 do specyfikacji
nonlocal
oświadczeniu.Jeśli licznik ma być prywatny dla modułu, należy go
_counter
zamiast tego nazwać .źródło
global counter
instrukcji zamiastnonlocal counter
(nonlocal
pozwala tylko pisać do stanu zamknięcia w funkcji zagnieżdżonej). Powodem, dla którego ludzie dołączają atrybut do funkcji jest unikanie zanieczyszczania globalnej przestrzeni nazw dla stanu, który jest specyficzny dla funkcji, więc nie musisz robić nawet trudniejszych rzeczy, gdy dwie funkcje potrzebują niezależnychcounter
s. To rozwiązanie nie jest skalowane; atrybuty na funkcji do. Odpowiedź kdb brzmi, jaknonlocal
może pomóc, ale dodaje złożoności.nonlocal
ciąguglobal
jest dokładnie tak, jak podkreślić - to działa w ściśle więcej okolicznościach.Używanie atrybutu funkcji jako zmiennej statycznej ma pewne potencjalne wady:
Idiomatyczny python dla drugiego problemu prawdopodobnie nazwałby zmienną wiodącym znakiem podkreślenia, aby zasygnalizować, że nie ma do niej dostępu, jednocześnie utrzymując ją dostępną po fakcie.
Alternatywą może być wzorzec wykorzystujący zamknięcia leksykalne, które są obsługiwane przez
nonlocal
słowo kluczowe w python 3.Niestety nie wiem, jak zawrzeć to rozwiązanie w dekoratorze.
źródło
Podobnie jak powyższy kod Vincenta, byłby on używany jako dekorator funkcji, a do zmiennych statycznych należy uzyskać dostęp z nazwą funkcji jako prefiksem. Zaletą tego kodu (choć trzeba przyznać, że każdy może być na tyle inteligentny, by to rozgryźć) jest to, że można mieć wiele zmiennych statycznych i inicjować je w bardziej konwencjonalny sposób.
źródło
Trochę bardziej czytelny, ale bardziej szczegółowy (Zen of Python: wyraźne jest lepsze niż niejawne):
Zobacz tutaj wyjaśnienie, jak to działa.
źródło
foo()
powinien ponownie zainicjować słownik do wartości określonej w definicji funkcji (tak więc klawisz licznika ma wartość 0). Dlaczego tak nie jest?Python zwyczajowo używa podkreślników do wskazania zmiennych prywatnych. Jedynym powodem w C, aby zadeklarować zmienną statyczną wewnątrz funkcji, jest ukrycie jej poza funkcją, co nie jest tak naprawdę idiomatycznym Pythonem.
źródło
Po wypróbowaniu kilku podejść wykorzystuję ulepszoną wersję odpowiedzi @ warvariuc:
źródło
Idiomatyczne sposobem jest użycie klasy , które mogą mieć atrybuty. Jeśli potrzebujesz, aby instancje nie były oddzielne, użyj singletonu.
Istnieje wiele sposobów na fałszowanie lub modyfikowanie „statycznych” zmiennych w Pythonie (jednym z dotychczas nie wspomnianych jest modyfikowalny argument domyślny), ale nie jest to Pythoniczny, idiomatyczny sposób. Po prostu użyj klasy.
Lub ewentualnie generator, jeśli twój wzorzec użytkowania pasuje.
źródło
default
argument jest najbardziej elegancki.Poddane temu pytaniu , czy mogę przedstawić inną alternatywę, która może być nieco przyjemniejsza w użyciu i będzie wyglądać tak samo dla metod i funkcji:
Jeśli podoba Ci się użycie, oto implementacja:
źródło
Kolejnym (niezalecanym!) Zwrotem na wywoływalnym obiekcie, takim jak https://stackoverflow.com/a/279598/916373 , jeśli nie masz nic przeciwko użyciu funky podpisu połączenia, byłoby zrobić
źródło
Zamiast tworzyć funkcję posiadającą statyczną zmienną lokalną, zawsze możesz utworzyć tak zwany „obiekt funkcji” i nadać jej standardową (niestatyczną) zmienną składową.
Ponieważ podałeś przykład napisany w C ++, najpierw wyjaśnię, czym jest „obiekt funkcji” w C ++. „Obiekt funkcji” to po prostu dowolna klasa z przeciążeniem
operator()
. Instancje klasy będą zachowywać się jak funkcje. Na przykład możesz pisać,int x = square(5);
nawet jeślisquare
jest obiektem (z przeciążeniemoperator()
) i technicznie nie jest „funkcją”. Możesz nadać obiektowi funkcyjnemu dowolną funkcję, którą możesz nadać obiektowi klasy.W Pythonie możemy również przeciążać,
operator()
ale zamiast tego nazwa metody__call__
:Oto definicja klasy:
Oto przykład zastosowanej klasy:
Dane wyjściowe drukowane na konsoli to:
Jeśli chcesz, aby twoja funkcja pobierała argumenty wejściowe, możesz również dodać je do
__call__
:źródło
Soulution n + = 1
źródło
Globalna deklaracja zapewnia tę funkcjonalność. W poniższym przykładzie (python 3.5 lub nowszy, aby użyć „f”) zmienna licznika jest zdefiniowana poza funkcją. Zdefiniowanie go jako globalnego w funkcji oznacza, że wersja „globalna” poza funkcją powinna zostać udostępniona tej funkcji. Dlatego przy każdym uruchomieniu funkcja modyfikuje wartość poza funkcją, zachowując ją poza funkcją.
źródło
Zmienna statyczna w metodzie Python
źródło
Osobiście wolę te niż dekoratorów. Do każdego własnego.
źródło
Ta odpowiedź opiera się na odpowiedzi @claudiu.
Odkryłem, że mój kod jest coraz mniej wyraźny, gdy zawsze muszę wstawić nazwę funkcji, ilekroć zamierzam uzyskać dostęp do zmiennej statycznej.
Mianowicie w moim kodzie funkcji wolałbym pisać:
zamiast
Więc moim rozwiązaniem jest:
statics
atrybut do funkcjistatics
jako alias domy_function.statics
Uwaga
Moja metoda używa klasy o nazwie
Bunch
, która jest słownikiem obsługującym dostęp w stylu atrybutów, a la JavaScript (zobacz oryginalny artykuł na ten temat, około 2000 r.)Można go zainstalować za pośrednictwem
pip install bunch
Można go również napisać ręcznie:
źródło
types.SimpleNamespace
(dostępne od 3.3) obsługuje to zachowanie od razu (i jest zaimplementowane w C na CPython, więc jest tak szybkie, jak to tylko możliwe).Opierając się na odpowiedzi Daniela (uzupełnienia):
Powodem, dla którego chciałem dodać tę część, jest to, że zmienne statyczne są używane nie tylko do zwiększania o jakąś wartość, ale także sprawdzają, czy var statyczny jest równy jakiejś wartości, jako przykład z życia.
Zmienna statyczna jest nadal chroniona i używana tylko w zakresie funkcji use_foo ()
W tym przykładzie wywołanie funkcji foo () działa dokładnie tak, jak (w odniesieniu do odpowiedniego odpowiednika c ++):
jeśli klasa Foo jest zdefiniowana jako klasa singleton, byłoby to idealne. To by uczyniło go bardziej pytonicznym.
źródło
Jasne, że to stare pytanie, ale myślę, że mogę trochę zaktualizować.
Wygląda na to, że argument dotyczący wydajności jest przestarzały. Ten sam zestaw testów wydaje się dawać podobne wyniki dla siInt_try i isInt_re2. Oczywiście wyniki są różne, ale jest to jedna sesja na moim komputerze z Pythonem 3.4.4 na jądrze 4.3.01 z Xeon W3550. Uruchomiłem go kilka razy, a wyniki wydają się podobne. Przeniosłem globalny regex na funkcję static, ale różnica w wydajności jest znikoma.
Z problemem wydajnościowym wydaje się, że try / catch stworzy kod najbardziej odporny na przyszłe i narożniki, więc może po prostu zawiń go w funkcję
źródło