Adakah yang bisa mengubah namedtuple atau menyediakan kelas alternatif agar berfungsi untuk objek yang bisa berubah?
Terutama untuk keterbacaan, saya ingin sesuatu yang mirip dengan nametuple yang melakukan ini:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
Itu harus memungkinkan untuk membuat acar objek yang dihasilkan. Dan sesuai dengan karakteristik bernama tuple, urutan output saat diwakili harus sesuai dengan urutan daftar parameter saat membuat objek.
python
mutable
namedtuple
Alexander
sumber
sumber
namedtuple
s, tampaknya Anda tidak perlu mereferensikan atribut dengan indeks, yaitu jadip[0]
danp[1]
akan menjadi cara alternatif untuk mereferensikanx
dany
masing - masing, benar?Jawaban:
Ada alternatif yang bisa berubah untuk
collections.namedtuple
- recordclass .Ini memiliki API dan footprint memori yang sama
namedtuple
dan mendukung penugasan (Seharusnya lebih cepat juga). Sebagai contoh:Untuk python 3.6 dan lebih tinggi
recordclass
(sejak 0.5) mendukung jenis petunjuk:Ada contoh yang lebih lengkap (ini juga termasuk perbandingan kinerja).
Sejak 0.9
recordclass
perpustakaan menyediakan varian lain -recordclass.structclass
fungsi pabrik. Ini dapat menghasilkan kelas, yang instansinya menempati lebih sedikit memori daripada__slots__
instans berbasis. Ini dapat menjadi penting untuk instance dengan nilai atribut, yang tidak dimaksudkan untuk memiliki siklus referensi. Ini dapat membantu mengurangi penggunaan memori jika Anda perlu membuat jutaan instance. Berikut adalah contoh ilustrasi .sumber
recordclass
lebih lambat, membutuhkan lebih banyak memori, dan membutuhkan ekstensi-C dibandingkan dengan resep Antti Haapala dannamedlist
.recordclass
adalah versi yang bisa berubahcollection.namedtuple
yang mewarisi api, jejak memori, tetapi tugas dukungan.namedlist
sebenarnya adalah turunan dari kelas python dengan slot. Ini lebih berguna jika Anda tidak membutuhkan akses cepat ke bidangnya dengan indeks.recordclass
misalnya (python 3.5.2) sekitar 2-3% lebih lambat daripada untuknamedlist
namedtuple
dan pembuatan kelas sederhanaPoint = namedtuple('Point', 'x y')
, Jedi dapat melengkapi atribut secara otomatis, sementara ini bukan kasusnyarecordclass
. Jika saya menggunakan kode pembuatan yang lebih panjang (berdasarkanRecordClass
), maka Jedi memahamiPoint
kelasnya, tetapi bukan konstruktor atau atributnya ... Apakah ada cararecordclass
untuk bekerja dengan baik dengan Jedi?types.SimpleNamespace diperkenalkan dengan Python 3.3 dan mendukung persyaratan yang diminta.
sumber
SimpleNamespace
gagal tes 6-10 (akses oleh indeks, pembongkaran berulang, iterasi, dikt yang dipesan, penggantian di tempat) dan 12, 13 (bidang, slot). Perhatikan bahwa dokumentasi (yang Anda tautkan dalam jawaban) secara khusus mengatakan "SimpleNamespace
mungkin berguna sebagai pengganticlass NS: pass
. Namun, untuk penggunaan jenis rekaman terstrukturnamedtuple()
sebagai gantinya."SimpleNamespace
membuat objek, bukan konstruktor kelas, dan tidak bisa menjadi pengganti nametuple. Perbandingan jenis tidak akan berfungsi, dan jejak memori akan jauh lebih tinggi.Sebagai alternatif yang sangat Pythonic untuk tugas ini, sejak Python-3.7, Anda dapat menggunakan
dataclasses
modul yang tidak hanya berperilaku seperti mutableNamedTuple
karena mereka menggunakan definisi kelas normal, tetapi juga mendukung fitur kelas lainnya.Dari PEP-0557:
Fitur ini diperkenalkan di PEP-0557 yang dapat Anda baca lebih lanjut di tautan dokumentasi yang disediakan.
Contoh:
Demo:
sumber
dataclass
gagal tes 6-10 (akses oleh indeks, pembongkaran berulang, iterasi, dikt yang dipesan, penggantian di tempat) dan 12, 13 (bidang, slot) dengan Python 3.7 .1.Terbaru namedlist 1,7 melewati semua tes Anda dengan baik Python 2.7 dan Python 3.5 pada 11 Jan 2016. Ini adalah implementasi python murni sedangkan
recordclass
adalah ekstensi C. Tentu saja, itu tergantung pada kebutuhan Anda apakah ekstensi C lebih disukai atau tidak.Tes Anda (tetapi juga lihat catatan di bawah):
Output pada Python 2.7
Satu-satunya perbedaan dengan Python 3.5 adalah
namedlist
ukurannya menjadi lebih kecil, ukurannya 56 (Python 2.7 melaporkan 64).Perhatikan bahwa saya telah mengubah tes 10 Anda untuk penggantian di tempat. The
namedlist
memiliki_replace()
metode yang melakukan salinan dangkal, dan itu sangat masuk akal bagi saya karenanamedtuple
di perpustakaan standar berperilaku dengan cara yang sama. Mengubah semantik_replace()
metode akan membingungkan. Menurut pendapat saya,_update()
metode ini harus digunakan untuk pembaruan di tempat. Atau mungkin saya gagal memahami maksud dari tes 10 Anda?sumber
namedlist
toko dalam contoh daftar. Masalahnya adalah bahwacpython
'slist
sebenarnya adalah array dinamis. Secara desain, itu mengalokasikan lebih banyak memori daripada yang diperlukan untuk membuat mutasi daftar lebih murah.list
dan secara default menggunakan__slots__
pengoptimalan. Ketika saya mengukur, penggunaan memori kurang darirecordclass
: 96 byte vs 104 byte untuk enam bidang pada Python 2.7recorclass
menggunakan lebih banyak memori karena ini adalahtuple
objek mirip dengan ukuran memori variabel.types.SimpleNamespace
. Sayangnya, pylint tidak menyukainya :-(Sepertinya jawaban untuk pertanyaan ini adalah tidak.
Di bawah ini cukup dekat, tetapi secara teknis tidak bisa berubah. Ini membuat
namedtuple()
instance baru dengan nilai x yang diperbarui:Di sisi lain, Anda dapat membuat kelas sederhana
__slots__
yang akan bekerja dengan baik untuk sering memperbarui atribut instance kelas:Untuk menambah jawaban ini, menurut saya
__slots__
bagus digunakan di sini karena hemat memori ketika Anda membuat banyak instance kelas. Satu-satunya kekurangan adalah Anda tidak dapat membuat atribut kelas baru.Berikut satu utas relevan yang menggambarkan efisiensi memori - Kamus vs Objek - mana yang lebih efisien dan mengapa?
Konten yang dikutip dalam jawaban utas ini adalah penjelasan yang sangat ringkas mengapa
__slots__
lebih hemat memori - slot Pythonsumber
Berikut ini adalah solusi yang baik untuk Python 3: Kelas minimal yang menggunakan
__slots__
danSequence
kelas dasar abstrak; tidak melakukan deteksi kesalahan mewah atau semacamnya, tetapi berfungsi, dan berperilaku sebagian besar seperti tupel yang bisa berubah (kecuali untuk pemeriksaan ketik).Contoh:
Jika mau, Anda dapat memiliki metode untuk membuat kelas juga (meskipun menggunakan kelas eksplisit lebih transparan):
Contoh:
Dalam Python 2 Anda perlu menyesuaikannya sedikit - jika Anda mewarisi dari
Sequence
, kelas akan memiliki a__dict__
dan__slots__
kemauan akan berhenti berfungsi.Solusi dalam Python 2 adalah tidak mewarisi
Sequence
, tetapiobject
. Jikaisinstance(Point, Sequence) == True
diinginkan, Anda perlu mendaftarkanNamedMutableSequence
sebagai kelas dasar untukSequence
:sumber
Mari terapkan ini dengan pembuatan tipe dinamis:
Ini memeriksa atribut untuk melihat apakah mereka valid sebelum mengizinkan operasi dilanjutkan.
Jadi, apakah ini bisa dijadikan acar? Ya jika (dan hanya jika) Anda melakukan hal berikut:
Definisi tersebut harus ada dalam namespace Anda, dan harus ada cukup lama agar acar dapat menemukannya. Jadi jika Anda mendefinisikan ini sebagai paket Anda, itu harus berhasil.
Pickle akan gagal jika Anda melakukan hal berikut, atau membuat definisi sementara (keluar dari ruang lingkup saat fungsi berakhir, katakanlah):
Dan ya, itu mempertahankan urutan bidang yang terdaftar dalam pembuatan tipe.
sumber
__iter__
metode denganfor k in self._attrs_: yield getattr(self, k)
, itu akan mendukung pembongkaran seperti tupel.__len__
,__getitem__
, dan__setiem__
metode untuk mendukung mendapatkan valus dengan indeks, sepertip[0]
. Dengan potongan terakhir ini, ini sepertinya jawaban yang paling lengkap dan benar (bagi saya).__len__
dan__iter__
bagus.__getitem__
dan__setitem__
benar-benar dapat dipetakan keself.__dict__.__setitem__
danself.__dict__.__getitem__
Tupel menurut definisi tidak dapat diubah.
Namun Anda dapat membuat subkelas kamus di mana Anda dapat mengakses atribut dengan notasi titik;
sumber
Jika Anda ingin perilaku serupa seperti namaiuple tetapi bisa berubah coba daftar nama
Perhatikan bahwa agar bisa berubah itu tidak bisa menjadi tupel.
sumber
Memberikan kinerja tidak terlalu penting, seseorang dapat menggunakan peretasan konyol seperti:
sumber
z
, Anda harus meneleponmutable_z.z.pop(0)
kemudianmutable_z.z.append(new_value)
. Jika Anda salah, Anda akan mendapatkan lebih dari 1 elemen dan program Anda akan berperilaku tidak terduga.mutable_z.z[0] = newValue
. Ini memang peretasan, seperti yang dinyatakan.