Apa persamaan Python idiomatik dari kode C / C ++ ini?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
khususnya, bagaimana seseorang mengimplementasikan anggota statis pada tingkat fungsi, yang bertentangan dengan tingkat kelas? Dan apakah menempatkan fungsi ke dalam kelas mengubah sesuatu?
_
awalan konvensional .Jawaban:
Agak terbalik, tetapi ini seharusnya berhasil:
Jika Anda menginginkan kode inisialisasi penghitung di bagian atas dan bukan di bawah, Anda dapat membuat dekorator:
Kemudian gunakan kode seperti ini:
Sayangnya, Anda masih harus menggunakan
foo.
awalan.(Kredit: @ony )
sumber
if "counter" not in foo.__dict__: foo.counter = 0
sebagai baris pertamafoo()
. Ini akan membantu menghindari kode di luar fungsi. Tidak yakin apakah ini mungkin kembali pada 2008. PS Menemukan jawaban ini saat mencari kemungkinan untuk membuat variabel fungsi statis, jadi utas ini masih "hidup" :)foo
danfoo.counter =
terkait erat. Namun, saya akhirnya lebih suka pendekatan dekorator, karena tidak ada cara dekorator tidak akan dipanggil dan secara semantik lebih jelas apa yang dilakukannya (@static_var("counter", 0)
lebih mudah & lebih masuk akal bagi mata saya daripadaif "counter" not in foo.__dict__: foo.counter = 0
, terutama karena pada yang terakhir Anda harus menggunakan nama fungsi (dua kali) yang mungkin berubah).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Anda dapat menambahkan atribut ke suatu fungsi, dan menggunakannya sebagai variabel statis.
Atau, jika Anda tidak ingin mengatur variabel di luar fungsi, Anda dapat menggunakan
hasattr()
untuk menghindariAttributeError
pengecualian:Bagaimanapun variabel statis agak jarang, dan Anda harus menemukan tempat yang lebih baik untuk variabel ini, kemungkinan besar di dalam kelas.
sumber
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
harus melakukan hal yang sama, menggunakan pengecualian.try
menambah biaya? Hanya penasaran.Seseorang juga dapat mempertimbangkan:
Pemikiran:
if
cabang (pikirkan pengecualian StopIteration )sumber
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
untuk ini tidak sederhana dan juga kurang efisien.Jawaban lain telah menunjukkan cara Anda melakukan ini. Inilah cara yang seharusnya tidak Anda lakukan:
Nilai default diinisialisasi hanya ketika fungsi pertama kali dievaluasi, tidak setiap kali dieksekusi, sehingga Anda dapat menggunakan daftar atau objek yang dapat diubah lainnya untuk menyimpan nilai statis.
sumber
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
saya menemukannya dapat dibaca dengan baik. Poin bagus lainnya adalah penggantian nama fungsi mudah.types.SimpleNamespace
, membuatnyadef foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
tanpa perlu mendefinisikan kelas khusus.Banyak orang telah menyarankan pengujian 'hasattr', tetapi ada jawaban yang lebih sederhana:
Tidak mencoba / kecuali, tanpa pengujian hasattr, cukup getattr dengan default.
sumber
try
/except
berbasis ravwojdyla cukup berarti.ipython
%%timeit
Microbenchmark sederhana memberi biayatry
/except
pada 255 ns per panggilan, vs 263 ns untukgetattr
solusi berbasis. Ya,try
/except
lebih cepat, tapi itu tidak sepenuhnya "menang telak"; ini adalah mikro-optimasi kecil. Tulis kode apa pun yang tampak lebih jelas, jangan khawatir tentang perbedaan kinerja sepele seperti ini.Ini adalah versi yang dienkapsulasi penuh yang tidak memerlukan panggilan inisialisasi eksternal:
Dalam Python, fungsi adalah objek dan kita bisa menambahkan, atau tambalan monyet, variabel anggota melalui atribut khusus
__dict__
. Built-invars()
mengembalikan atribut khusus__dict__
.EDIT: Catatan, tidak seperti
try:except AttributeError
jawaban alternatif , dengan pendekatan ini variabel akan selalu siap untuk logika kode setelah inisialisasi. Saya pikirtry:except AttributeError
alternatif untuk yang berikut ini akan kurang KERING dan / atau memiliki aliran yang canggung:EDIT2: Saya hanya merekomendasikan pendekatan di atas ketika fungsi akan dipanggil dari beberapa lokasi. Jika alih-alih fungsi hanya dipanggil di satu tempat, lebih baik menggunakan
nonlocal
:sumber
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
daripadanot X in Y
(atau menyarankan menggunakan jika Anda hanya menggunakannya demi perbandingan yang lebih mirip antara itu danhasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
Python tidak memiliki variabel statis tetapi Anda bisa memalsukannya dengan mendefinisikan objek kelas yang dapat dipanggil dan kemudian menggunakannya sebagai fungsi. Lihat juga jawaban ini .
Catatan yang
__call__
membuat instance kelas (objek) dapat dipanggil dengan namanya sendiri. Itu sebabnya memanggil difoo()
atas memanggil metode kelas__call__
. Dari dokumentasi :sumber
foo
yang disediakan sebagai singleton.Gunakan fungsi generator untuk menghasilkan iterator.
Kemudian gunakan seperti
Jika Anda ingin batas atas:
Jika iterator berakhir (seperti contoh di atas), Anda juga dapat mengulanginya secara langsung, seperti
Tentu saja, dalam kasus-kasus sederhana ini lebih baik menggunakan xrange :)
Berikut adalah dokumentasi pada pernyataan hasil .
sumber
Solusi lain melampirkan atribut penghitung ke fungsi, biasanya dengan logika berbelit-belit untuk menangani inisialisasi. Ini tidak pantas untuk kode baru.
Dalam Python 3, cara yang benar adalah dengan menggunakan
nonlocal
pernyataan:Lihat PEP 3104 untuk spesifikasi
nonlocal
pernyataan tersebut.Jika penghitung dimaksudkan untuk pribadi ke modul, itu harus dinamai
_counter
sebaliknya.sumber
global counter
pernyataan alih-alihnonlocal counter
(nonlocal
hanya memungkinkan Anda menulis ke status penutupan dalam fungsi bersarang). Alasan orang melampirkan atribut ke fungsi adalah untuk menghindari mencemari namespace global untuk negara yang khusus untuk fungsi, sehingga Anda tidak perlu melakukan hal-hal yang bahkan peretasan ketika dua fungsi perlu independencounter
. Solusi ini tidak berskala; atribut pada fungsi lakukan. Jawaban kdb adalah bagaimananonlocal
bisa membantu, tetapi menambah kompleksitas.nonlocal
lebihglobal
adalah persis seperti yang Anda tunjukkan - itu bekerja dalam keadaan yang lebih ketat.Menggunakan atribut fungsi sebagai variabel statis memiliki beberapa kelemahan potensial:
Python idiomatik untuk masalah kedua mungkin akan memberi nama variabel dengan garis bawah utama untuk memberi sinyal bahwa itu tidak dimaksudkan untuk diakses, sambil tetap diakses setelah fakta.
Alternatif akan menjadi pola menggunakan penutupan leksikal, yang didukung dengan
nonlocal
kata kunci di python 3.Sayangnya saya tidak tahu cara untuk merangkum solusi ini menjadi dekorator.
sumber
Sama seperti kode vincent di atas, ini akan digunakan sebagai dekorator fungsi dan variabel statis harus diakses dengan nama fungsi sebagai awalan. Keuntungan dari kode ini (walaupun harus diakui siapa pun mungkin cukup pintar untuk mengetahuinya) adalah bahwa Anda dapat memiliki beberapa variabel statis dan menginisialisasinya dengan cara yang lebih konvensional.
sumber
Sedikit lebih mudah dibaca, tetapi lebih verbose (Zen dari Python: eksplisit lebih baik daripada implisit):
Lihat di sini untuk penjelasan tentang cara kerjanya.
sumber
foo()
harus menginisialisasi ulang kamus ke nilai yang ditentukan dalam definisi fungsi (jadi dengan tombol penghitung memiliki nilai 0). Kenapa tidak?Python biasanya menggunakan garis bawah untuk menunjukkan variabel pribadi. Satu-satunya alasan di C untuk mendeklarasikan variabel statis di dalam fungsi adalah untuk menyembunyikannya di luar fungsi, yang sebenarnya bukan Python idiomatik.
sumber
Setelah mencoba beberapa pendekatan, saya akhirnya menggunakan versi jawaban @ warvariuc yang lebih baik:
sumber
Cara idiomatis adalah menggunakan kelas , yang dapat memiliki atribut. Jika Anda perlu contoh untuk tidak terpisah, gunakan singleton.
Ada beberapa cara Anda bisa memalsukan atau mengubah variabel "statis" menjadi Python (yang tidak disebutkan sejauh ini adalah memiliki argumen default yang bisa berubah-ubah), tetapi ini bukan cara Pythonic, idiomatis untuk melakukannya. Cukup gunakan kelas.
Atau mungkin generator, jika pola penggunaan Anda cocok.
sumber
default
argumennya adalah yang paling elegan.Diminta oleh pertanyaan ini , bolehkah saya menyajikan alternatif lain yang mungkin sedikit lebih baik untuk digunakan dan akan terlihat sama untuk kedua metode dan fungsi:
Jika Anda suka penggunaannya, inilah implementasinya:
sumber
Sentuhan lain (tidak disarankan!) Pada objek yang dapat dipanggil seperti https://stackoverflow.com/a/279598/916373 , jika Anda tidak keberatan menggunakan tanda tangan panggilan yang funky, adalah melakukan
sumber
Alih-alih membuat fungsi yang memiliki variabel lokal statis, Anda selalu dapat membuat apa yang disebut "objek fungsi" dan memberinya variabel anggota standar (non-statis).
Karena Anda memberi contoh C ++ yang ditulis, saya pertama-tama akan menjelaskan apa "objek fungsi" di C ++. "Objek fungsi" adalah kelas apa saja dengan kelebihan beban
operator()
. Contoh kelas akan berperilaku seperti fungsi. Sebagai contoh, Anda dapat menulisint x = square(5);
meskipunsquare
objek (dengan kelebihan bebanoperator()
) dan secara teknis bukan "fungsi." Anda bisa memberikan objek-fungsi salah satu fitur yang bisa Anda berikan pada objek kelas.Dalam Python, kita juga bisa membebani
operator()
kecuali bahwa metode ini dinamai__call__
:Berikut adalah definisi kelas:
Berikut adalah contoh kelas yang digunakan:
Output yang dicetak ke konsol adalah:
Jika Anda ingin fungsi Anda mengambil argumen input, Anda dapat menambahkannya
__call__
juga:sumber
Soulution n + = 1
sumber
Deklarasi global menyediakan fungsionalitas ini. Pada contoh di bawah ini (python 3.5 atau lebih besar untuk menggunakan "f"), variabel counter didefinisikan di luar fungsi. Mendefinisikannya sebagai global dalam fungsi menandakan bahwa versi "global" di luar fungsi harus tersedia untuk fungsi tersebut. Jadi setiap kali fungsi berjalan, ia memodifikasi nilai di luar fungsi, melestarikannya di luar fungsi.
sumber
Variabel statis di dalam metode Python
sumber
Saya pribadi lebih suka yang berikut ini daripada dekorator. Untuk masing-masing.
sumber
Jawaban ini dibangun berdasarkan jawaban @claudiu.
Saya menemukan bahwa kode saya menjadi kurang jelas ketika saya harus selalu menambahkan nama fungsi, setiap kali saya ingin mengakses variabel statis.
Yaitu, dalam kode fungsi saya, saya lebih suka menulis:
dari pada
Jadi, solusi saya adalah:
statics
atribut ke fungsistatics
sebagai alias kemy_function.statics
Ucapan
Metode saya menggunakan kelas bernama
Bunch
, yang merupakan kamus yang mendukung akses gaya-atribut, ala JavaScript (lihat artikel asli tentang itu, sekitar 2000)Ini dapat diinstal melalui
pip install bunch
Dapat juga ditulis tangan seperti ini:
sumber
types.SimpleNamespace
(tersedia sejak 3.3) mendukung perilaku ini di luar kotak (dan diimplementasikan dalam C pada CPython, jadi ini secepat mungkin).Membangun jawaban Daniel (tambahan):
Alasan mengapa saya ingin menambahkan bagian ini adalah, variabel statis digunakan tidak hanya untuk menambah nilai, tetapi juga memeriksa apakah var statis sama dengan nilai tertentu, sebagai contoh kehidupan nyata.
Variabel statis masih dilindungi dan digunakan hanya dalam lingkup fungsi use_foo ()
Dalam contoh ini, panggilan ke foo () berfungsi persis seperti (sehubungan dengan setara c ++):
jika kelas Foo didefinisikan secara terbatas sebagai kelas tunggal, itu akan ideal. Ini akan membuatnya lebih pythonic.
sumber
Tentu ini adalah pertanyaan lama tapi saya pikir saya mungkin memberikan beberapa pembaruan.
Tampaknya argumen kinerja sudah usang. Rangkaian tes yang sama muncul untuk memberikan hasil yang serupa untuk siInt_try dan isInt_re2. Tentu saja hasilnya bervariasi, tetapi ini adalah satu sesi di komputer saya dengan python 3.4.4 pada kernel 4.3.01 dengan Xeon W3550. Saya telah menjalankannya beberapa kali dan hasilnya tampak serupa. Saya memindahkan regex global ke fungsi statis, tetapi perbedaan kinerja dapat diabaikan.
Dengan keluarnya masalah kinerja, tampaknya try / catch akan menghasilkan kode bukti masa depan dan cornercase-paling jadi mungkin hanya membungkusnya dalam fungsi
sumber