Apa idiomatik Python yang setara dengan ini, C/C++ code?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
secara khusus, bagaimana seseorang melaksanakan anggota statis pada fungsi tingkat, sebagai lawan ke tingkat kelas? Dan tidak menempatkan fungsi menjadi hotel perubahan apa-apa?
Sedikit terbalik, tapi ini harus bekerja:
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
foo.counter = 0
Jika anda ingin meja inisialisasi kode di atas, bukan dari bawah, anda dapat membuat dekorator:
def static_var(varname, value):
def decorate(func):
setattr(func, varname, value)
return func
return decorate
Kemudian gunakan kode seperti ini:
@static_var("counter", 0)
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
It'll masih memerlukan anda untuk menggunakan foo.
awalan, sayangnya.
EDIT (terima kasih untuk ony)hal Ini terlihat bahkan lebih baik:
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
Anda dapat menambahkan atribut ke fungsi, dan menggunakannya sebagai variabel statis.
def myfunc():
myfunc.counter += 1
print myfunc.counter
# attribute must be initialized
myfunc.counter = 0
Atau, jika anda don't ingin setup variabel di luar fungsi, anda dapat menggunakan hasattr()
untuk menghindari AttributeError
pengecualian:
def myfunc():
if not hasattr(myfunc, "counter"):
myfunc.counter = 0 # it doesn't exist yet, so initialize it
myfunc.counter += 1
Pokoknya variabel statis yang agak jarang, dan anda harus menemukan tempat yang lebih baik untuk variabel ini, kemungkinan besar di dalam kelas.
Satu bisa juga mempertimbangkan:
def foo():
try:
foo.counter += 1
except AttributeError:
foo.counter = 1
Penalaran:
meminta maaf belum dapat izin
)jika
cabang (berpikir StopIteration pengecualian)Jawaban yang lain telah menunjukkan cara anda harus melakukan hal ini. Berikut ini's cara yang anda tidak't:
>>> def foo(counter=[0]):
... counter[0] += 1
... print("Counter is %i." % counter[0]);
...
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>
Nilai Default yang dijalankan hanya jika fungsi pertama dievaluasi, tidak setiap kali dijalankan, sehingga anda dapat menggunakan daftar atau yang lain bisa berubah objek untuk menyimpan nilai-nilai statis.
Python doesn't memiliki variabel statis tetapi anda dapat palsu itu dengan mendefinisikan callable kelas objek dan kemudian menggunakannya sebagai fungsi. Lihat juga jawaban ini.
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
Perhatikan bahwa __panggilan__
membuat sebuah instance dari sebuah kelas (objek) callable dengan nama sendiri. Yang's mengapa memanggil foo()
atas panggilan kelas' __panggilan__
metode. Dari dokumentasi:
sewenang-wenang kelas dapat dibuat callable dengan mendefinisikan
__panggilan__()
metode dalam kelas mereka.
Berikut ini adalah sepenuhnya dikemas versi yang doesn't membutuhkan eksternal inisialisasi call:
def fn():
fn.counter=vars(fn).setdefault('counter',-1)
fn.counter+=1
print (fn.counter)
Di Python, fungsi objek dan kita hanya dapat menambah, atau monyet patch, variabel anggota mereka melalui atribut khusus __dict__
. Built-in var()
mengembalikan atribut khusus __dict__
.
EDIT: Catatan, tidak seperti alternatif mencoba:kecuali AttributeError
menjawab, dengan pendekatan ini variabel yang akan selalu siap untuk kode logika berikut inisialisasi. Saya pikir mencoba:kecuali AttributeError
alternatif berikut akan kurang KERING dan/atau memiliki canggung aliran:
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: saya hanya merekomendasikan pendekatan di atas ketika fungsi ini akan dipanggil dari beberapa lokasi. Jika alih fungsi ini hanya disebut dalam satu tempat, it's lebih baik menggunakan nonlokal
:
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))
...
Menggunakan function generator untuk menghasilkan sebuah iterator.
def foo_gen():
n = 0
while True:
n+=1
yield n
Kemudian gunakan seperti
foo = foo_gen().next
for i in range(0,10):
print foo()
Jika anda ingin batas atas:
def foo_gen(limit=100000):
n = 0
while n < limit:
n+=1
yield n
Jika iterator berakhir (seperti contoh diatas), anda juga dapat loop di atasnya secara langsung, seperti
for i in foo_gen(20):
print i
Tentu saja, dalam kasus-kasus yang sederhana itu's baik untuk menggunakan xrange :)
Berikut ini adalah dokumentasi di pernyataan hasil.
Solusi lain melampirkan counter atribut fungsi, biasanya dengan logika yang berbelit-belit untuk menangani inisialisasi. Ini adalah tidak pantas untuk kode baru.
Di Python 3, cara yang tepat adalah dengan menggunakan nonlokal
pernyataan:
counter = 0
def foo():
nonlocal counter
counter += 1
print(f'counter is {counter}')
Lihat PEP 3104 untuk spesifikasi dari nonlokal
pernyataan.
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)
Seperti vincent's kode di atas, ini akan digunakan sebagai fungsi dekorator dan variabel statis harus diakses dengan nama fungsi sebagai awalan. Keuntungan dari kode ini (meskipun memang ada yang mungkin cukup pintar untuk mencari tahu) adalah bahwa anda dapat memiliki beberapa variabel statis dan menginisialisasi mereka dalam banyak cara konvensional.
Menggunakan atribut yang berfungsi sebagai variabel statis memiliki beberapa kelemahan potensial:
Idiomatik python untuk masalah kedua mungkin akan penamaan variabel dengan awalan garis bawah untuk sinyal bahwa hal ini tidak dimaksudkan untuk diakses, namun tetap dapat diakses setelah fakta.
Alternatif akan menggunakan pola leksikal penutupan, yang didukung dengan nonlokal
kata kunci di python 3.
def make_counter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
counter = make_counter()
Sayangnya aku tahu tidak ada cara untuk merangkum solusi ini menjadi dekorator.
Sedikit lebih mudah dibaca, tapi lebih verbose (Zen dari Python: explicit lebih baik dari implisit):
>>> def func(_static={'counter': 0}):
... _static['counter'] += 1
... print _static['counter']
...
>>> func()
1
>>> func()
2
>>>
Lihat di sini untuk penjelasan tentang bagaimana ini bekerja.
_counter = 0 def foo(): global _counter _counter += 1 cetak 'counter', _counterPython lazim menggunakan garis bawah untuk menunjukkan pribadi variabel. Satu-satunya alasan dalam C untuk mendeklarasikan variabel statis dalam fungsi ini adalah untuk menyembunyikan hal itu di luar fungsi, yang tidak benar-benar idiomatik Python.
Alih-alih menciptakan sebuah fungsi yang memiliki statis variabel lokal, anda selalu dapat membuat apa yang disebut "fungsi objek" dan memberikan standar (non-statis) variabel anggota.
Karena anda memberi contoh yang ditulis C++, pertama saya akan menjelaskan apa "fungsi objek" dalam C++. "fungsi objek" hanya setiap kelas dengan kelebihan beban operator()
. Contoh kelas akan berperilaku seperti fungsi. Misalnya, anda dapat menulis int x = square(5);
bahkan jika square
adalah sebuah objek (dengan kelebihan beban operator()
) dan tidak secara teknis bukan "fungsi." Anda dapat memberikan fungsi-objek setiap fitur yang anda bisa memberikan sebuah kelas objek.
# 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;
Di Python, kita juga bisa overload operator()
kecuali bahwa metode ini bukan bernama __panggilan__
:
Berikut ini adalah definisi kelas:
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
Berikut adalah contoh dari kelas yang sedang digunakan:
from foo import foo
for i in range(0, 5):
foo() # function call
Output dicetak ke konsol adalah:
counter is 1
counter is 2
counter is 3
counter is 4
counter is 5
Jika anda ingin fungsi untuk mengambil input argumen, anda dapat menambahkan mereka ke __panggilan__
juga:
# 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
Yang ungkapan cara menggunakan kelas, yang dapat memiliki atribut. Jika anda membutuhkan contoh untuk tidak menjadi terpisah, menggunakan singleton.
Ada sejumlah cara yang bisa anda palsu atau muka "statis" variabel dalam Python (salah satu yang tidak disebutkan sejauh ini untuk bisa berubah default argumen), tapi ini bukan Pythonic, idiomatik cara untuk melakukannya. Hanya menggunakan sebuah kelas.
Atau mungkin generator, jika anda penggunaan pola yang cocok.
Diminta oleh ini question, mungkin saya hadir alternatif lain yang mungkin sedikit lebih baik digunakan dan akan terlihat sama untuk kedua metode dan fungsi:
@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
Jika anda suka penggunaan, di sini's pelaksanaan:
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
Sebuah deklarasi global menyediakan fungsi ini. Dalam contoh di bawah ini (python 3.5 atau yang lebih baru untuk menggunakan "f"), yang counter variabel yang didefinisikan di luar fungsi. Mendefinisikan sebagai global dalam fungsi menandakan bahwa "global" versi luar fungsi yang harus dibuat tersedia untuk fungsi. Jadi setiap kali menjalankan fungsi, memodifikasi nilai di luar fungsi, melestarikan itu melampaui batas fungsi.
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"