Haruskah saya memberikan nilai default kepada anggota kelas saya seperti ini:
class Foo:
num = 1
atau seperti ini?
class Foo:
def __init__(self):
self.num = 1
Dalam pertanyaan ini saya menemukan bahwa dalam kedua kasus,
bar = Foo()
bar.num += 1
adalah operasi yang terdefinisi dengan baik.
Saya mengerti bahwa metode pertama akan memberi saya variabel kelas sedangkan yang kedua tidak. Namun, jika saya tidak memerlukan variabel kelas, tetapi hanya perlu menetapkan nilai default untuk variabel instan saya, apakah kedua metode sama baiknya? Atau salah satunya lebih 'pythonic' dari yang lain?
Satu hal yang saya perhatikan adalah bahwa dalam tutorial Django, mereka menggunakan metode kedua untuk mendeklarasikan Model. Secara pribadi saya pikir metode kedua lebih elegan, tetapi saya ingin tahu apa cara 'standar' itu.
self.attr = attr or []
di dalam__init__
metode. Itu mencapai hasil yang sama (menurut saya) dan masih jelas serta dapat dibaca.Kedua cuplikan tersebut melakukan hal yang berbeda, jadi ini bukan masalah selera tetapi masalah perilaku apa yang benar dalam konteks Anda. Dokumentasi Python menjelaskan perbedaannya, tetapi berikut beberapa contohnya:
Bukti A
class Foo: def __init__(self): self.num = 1
Ini mengikat
num
ke instance Foo . Perubahan ke bidang ini tidak diterapkan ke contoh lain.Jadi:
>>> foo1 = Foo() >>> foo2 = Foo() >>> foo1.num = 2 >>> foo2.num 1
Bukti B
class Bar: num = 1
Ini mengikat kelas
num
Bar . Perubahan disebarkan!>>> bar1 = Bar() >>> bar2 = Bar() >>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation >>> bar2.num 1 >>> Bar.num = 3 >>> bar2.num 3 >>> bar1.num 2 >>> bar1.__class__.num 3
Jawaban sebenarnya
Kode dalam pameran B salah untuk ini: mengapa Anda ingin mengikat atribut kelas (nilai default pada pembuatan instance) ke satu instance?
Kode di pameran A tidak apa-apa.
Jika Anda ingin memberikan default untuk variabel contoh di konstruktor Anda, saya akan melakukan ini:
class Foo: def __init__(self, num = None): self.num = num if num is not None else 1
...atau bahkan:
class Foo: DEFAULT_NUM = 1 def __init__(self, num = None): self.num = num if num is not None else DEFAULT_NUM
... atau bahkan: (lebih disukai, tetapi jika dan hanya jika Anda berurusan dengan tipe yang tidak dapat diubah!)
class Foo: def __init__(self, num = 1): self.num = num
Dengan cara ini Anda dapat melakukan:
foo1 = Foo(4) foo2 = Foo() #use default
sumber
def __init__(self, num = None)
Menggunakan anggota kelas untuk memberikan nilai default bekerja dengan sangat baik asalkan Anda berhati-hati untuk melakukannya dengan nilai yang tidak dapat diubah. Jika Anda mencoba melakukannya dengan daftar atau dikt yang akan sangat mematikan. Ini juga berfungsi di mana atribut instance adalah referensi ke kelas asalkan nilai defaultnya adalah None.
Saya telah melihat teknik ini digunakan dengan sangat sukses dalam repoze yang merupakan kerangka kerja yang berjalan di atas Zope. Keuntungannya di sini tidak hanya ketika kelas Anda disimpan ke database hanya atribut non-default yang perlu disimpan, tetapi juga ketika Anda perlu menambahkan bidang baru ke dalam skema, semua objek yang ada melihat bidang baru dengan defaultnya nilai tanpa perlu benar-benar mengubah data yang disimpan.
Saya merasa ini juga berfungsi dengan baik dalam pengkodean yang lebih umum, tetapi ini adalah gaya. Gunakan apa pun yang membuat Anda paling bahagia.
sumber
Menggunakan anggota kelas untuk nilai default dari variabel instan bukanlah ide yang baik, dan ini pertama kalinya saya melihat ide ini disebutkan sama sekali. Ini berfungsi dalam contoh Anda, tetapi mungkin gagal dalam banyak kasus. Misalnya, jika nilainya bisa berubah, mutasi pada instance yang tidak dimodifikasi akan mengubah default:
>>> class c: ... l = [] ... >>> x = c() >>> y = c() >>> x.l [] >>> y.l [] >>> x.l.append(10) >>> y.l [10] >>> c.l [10]
sumber
[]
diklon di sekitar sana;x.l
dany.l
keduanya adalah nama jahat dan nilai langsung berbeda yang terhubung ke rujukan yang sama. (Istilah pengacara bahasa dibuat)Anda juga dapat mendeklarasikan variabel kelas sebagai Tidak Ada yang akan mencegah propagasi. Ini berguna ketika Anda membutuhkan kelas yang didefinisikan dengan baik dan ingin mencegah AttributeErrors. Sebagai contoh:
>>> class TestClass(object): ... t = None ... >>> test = TestClass() >>> test.t >>> test2 = TestClass() >>> test.t = 'test' >>> test.t 'test' >>> test2.t >>>
Juga jika Anda membutuhkan default:
>>> class TestClassDefaults(object): ... t = None ... def __init__(self, t=None): ... self.t = t ... >>> test = TestClassDefaults() >>> test.t >>> test2 = TestClassDefaults([]) >>> test2.t [] >>> test.t >>>
Tentu tetap ikuti info di jawaban lain tentang menggunakan tipe mutable vs immutable sebagai default di
__init__
.sumber
Dengan dataclass , fitur yang ditambahkan di Python 3.7, sekarang ada cara lain (cukup nyaman) untuk mencapai pengaturan nilai default pada instance kelas. Dekorator
dataclass
akan secara otomatis menghasilkan beberapa metode di kelas Anda, seperti konstruktor. Seperti catatan dokumentasi yang ditautkan di atas, "[t] variabel anggota yang digunakan dalam metode yang dihasilkan ini ditentukan menggunakan anotasi jenis PEP 526 ".Mempertimbangkan contoh OP, kita bisa menerapkannya seperti ini:
from dataclasses import dataclass @dataclass class Foo: num: int = 0
Saat membangun sebuah objek dari tipe kelas ini, kita bisa secara opsional menimpa nilainya.
print('Default val: {}'.format(Foo())) # Default val: Foo(num=0) print('Custom val: {}'.format(Foo(num=5))) # Custom val: Foo(num=5)
sumber