Tujuannya adalah untuk membuat kelas tiruan yang berperilaku seperti resultset db.
Jadi misalnya, jika kueri basis data kembali, menggunakan ekspresi dict {'ab':100, 'cd':200}
,, maka saya ingin melihat:
>>> dummy.ab
100
Pada awalnya saya pikir mungkin saya bisa melakukannya dengan cara ini:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
tetapi c.ab
mengembalikan objek properti sebagai gantinya.
Mengganti setattr
saluran dengan k = property(lambda x: vs[i])
tidak ada gunanya sama sekali.
Jadi apa cara yang tepat untuk membuat properti instance saat runtime?
PS Saya mengetahui adanya alternatif yang disajikan dalam Bagaimana __getattribute__
metode yang digunakan?
python
properties
runtime
monkeypatching
Anthony Kong
sumber
sumber
:
dan__init__
referensiself.fn_readyonly
.Jawaban:
Saya kira saya harus memperluas jawaban ini, sekarang saya lebih tua dan lebih bijaksana dan tahu apa yang terjadi. Lebih baik terlambat daripada tidak sama sekali.
Anda bisa menambahkan properti ke kelas secara dinamis. Tapi itulah intinya: Anda harus menambahkannya ke kelas .
A
property
sebenarnya adalah implementasi sederhana dari hal yang disebut deskriptor . Ini adalah objek yang menyediakan penanganan kustom untuk atribut yang diberikan, pada kelas tertentu . Agak seperti cara untuk faktorif
pohon besar keluar__getattribute__
.Ketika saya meminta
foo.b
dalam contoh di atas, Python melihat bahwab
didefinisikan pada alat kelas protokol deskriptor -yang hanya berarti itu sebuah objek dengan__get__
,__set__
atau__delete__
metode. Deskriptor mengklaim bertanggung jawab untuk menangani atribut itu, sehingga Python memanggilFoo.b.__get__(foo, Foo)
, dan nilai pengembalian dikirimkan kembali kepada Anda sebagai nilai atribut. Dalam kasusproperty
, masing-masing metode ini hanya menyebutfget
,fset
ataufdel
Anda lulus keproperty
konstruktor.Deskriptor adalah cara Python untuk mengekspos plumbing dari seluruh implementasi OO-nya. Bahkan, ada jenis deskriptor lain yang bahkan lebih umum daripada
property
.Metode sederhana hanyalah jenis deskriptor lainnya. Itu
__get__
paku pada instance panggilan sebagai argumen pertama; pada dasarnya, ia melakukan ini:Ngomong-ngomong, saya menduga inilah sebabnya deskriptor hanya bekerja di kelas: mereka adalah formalisasi dari hal-hal yang memberdayakan kelas di tempat pertama. Mereka bahkan merupakan perkecualian dari aturan: Anda jelas dapat menetapkan deskriptor untuk suatu kelas, dan kelas itu sendiri adalah contoh dari
type
! Bahkan, mencoba membacaFoo.b
panggilan diamproperty.__get__
; itu hanya idiomatik untuk deskriptor untuk mengembalikan diri ketika diakses sebagai atribut kelas.Saya pikir itu sangat keren bahwa hampir semua sistem OO Python dapat diekspresikan dalam Python. :)
Oh, dan saya menulis posting blog bertele - tele tentang deskriptor beberapa waktu lalu jika Anda tertarik.
sumber
setattr (Foo, 'name', property (func))
Jadi yang Anda inginkan adalah kamus tempat Anda dapat mengeja ['b'] sebagai ab?
Itu mudah:
sumber
d.items = 1
,d.items
kembali<built-in method items of atdict object at ...>
. Anda masih bisa melakukand["items"]
atau menggunakan__getattribute__
bukan__getattr__
, tetapi ini mencegah menggunakan sebagian besar metode dikt.Tampaknya Anda bisa menyelesaikan masalah ini lebih mudah dengan
namedtuple
, karena Anda tahu seluruh daftar bidang sebelumnya.Jika Anda benar-benar perlu menulis setter Anda sendiri, Anda harus melakukan metaprogramming di tingkat kelas;
property()
tidak berfungsi pada instance.sumber
namedtuple
pantas mendapatkan hadiah karena membuatnya halus dan elegan untuk menjadi prinsip berorientasi objek yang setia.property()
? tidak ada jawaban apa pun yang khusus untuk properti hanya baca.Anda tidak perlu menggunakan properti untuk itu. Timpa saja
__setattr__
untuk membuatnya hanya baca.Tada.
sumber
Katakanlah Anda memiliki objek yang ingin Anda tambahkan properti. Biasanya, saya ingin menggunakan properti ketika saya harus mulai mengelola akses ke atribut dalam kode yang memiliki penggunaan hilir, sehingga saya dapat mempertahankan API yang konsisten. Sekarang saya biasanya akan menambahkannya ke kode sumber di mana objek didefinisikan, tetapi mari kita asumsikan Anda tidak memiliki akses itu, atau Anda harus benar-benar secara dinamis memilih fungsi Anda secara terprogram.
Buat kelas
Menggunakan contoh yang didasarkan pada dokumentasi untuk
property
, mari kita buat kelas objek dengan atribut "tersembunyi" dan buat instance darinya:Dengan Python, kami berharap ada satu cara yang jelas dalam melakukan sesuatu. Namun, dalam hal ini, saya akan menunjukkan dua cara: dengan notasi dekorator, dan tanpa. Pertama, tanpa notasi dekorator. Ini mungkin lebih berguna untuk penugasan dinamis dari getter, setter, atau deleter.
Dynamic (alias Monkey Patching)
Mari kita buat beberapa untuk kelas kita:
Dan sekarang kami menetapkan ini ke properti. Perhatikan bahwa kita dapat memilih fungsi kita secara terprogram di sini, menjawab pertanyaan dinamis:
Dan penggunaan:
Dekorator
Kita dapat melakukan hal yang sama seperti yang kita lakukan di atas dengan notasi dekorator, tetapi dalam kasus ini, kita harus memberi nama metode semua nama yang sama (dan saya akan merekomendasikan menjaganya tetap sama dengan atribut), sehingga tugas terprogram tidak sepele seperti itu menggunakan metode di atas:
Dan tetapkan objek properti dengan setter dan delta yang disediakan untuk kelas:
Dan penggunaan:
sumber
Saya mengajukan pertanyaan serupa pada posting Stack Overflow ini untuk membuat pabrik kelas yang membuat tipe sederhana. Hasilnya adalah jawaban ini yang memiliki versi pabrik kelas yang berfungsi. Berikut cuplikan jawabannya:
Anda dapat menggunakan beberapa variasi ini untuk membuat nilai default yang merupakan tujuan Anda (ada juga jawaban dalam pertanyaan yang berhubungan dengan ini).
sumber
Tidak yakin apakah saya benar-benar memahami pertanyaan, tetapi Anda dapat mengubah properti instance saat runtime dengan bawaan
__dict__
kelas Anda:sumber
self.__dict__[key] = value
Bagi mereka yang datang dari mesin pencari, berikut adalah dua hal yang saya cari ketika berbicara tentang properti dinamis :
__dict__
bagus jika Anda ingin menempatkan properti yang dibuat secara dinamis.__getattr__
Adalah baik untuk hanya melakukan sesuatu ketika nilai dibutuhkan, seperti permintaan basis data. Combo set / get bagus untuk menyederhanakan akses ke data yang disimpan di kelas (seperti pada contoh di atas).Jika Anda hanya menginginkan satu properti dinamis, lihat fungsi properti () bawaan.
sumber
Anda tidak dapat menambahkan yang baru
property()
ke instance saat runtime, karena properti adalah deskriptor data. Sebaliknya, Anda harus secara dinamis membuat kelas baru, atau kelebihan__getattribute__
untuk memproses deskriptor data pada instance.sumber
Cara terbaik untuk mencapai adalah dengan mendefinisikan
__slots__
. Dengan begitu instance Anda tidak dapat memiliki atribut baru.Cetakan itu
12
Itu memberi:
AttributeError: 'C' object has no attribute 'ab'
sumber
Contoh lain cara mencapai efek yang diinginkan
Jadi sekarang kita bisa melakukan hal-hal seperti:
sumber
Berikut ini solusinya:
Setelah kelas telah ditentukan, Anda cukup melakukan ini untuk menambahkan properti ke dalamnya secara dinamis:
Berikut ini adalah contoh lengkap, diuji dengan Python 3:
sumber
Anda dapat menggunakan kode berikut untuk memperbarui atribut kelas menggunakan objek kamus:
sumber
Ini sedikit berbeda dari yang diinginkan OP, tetapi saya mengguncang otak saya sampai saya mendapatkan solusi yang berfungsi, jadi saya menempatkan di sini untuk orang berikutnya
Saya membutuhkan cara untuk menentukan setter dinamis dan getter.
Saya tahu bidang saya sebelumnya jadi saya akan membuat properti saya. CATATAN: Anda tidak dapat melakukan instance PER ini, properti ini akan ada di kelas !!!
Mari kita uji semuanya sekarang ..
Apakah ini membingungkan? Ya, maaf saya tidak bisa memberikan contoh dunia nyata yang berarti. Juga, ini bukan untuk yang berhati ringan.
sumber
Ini tampaknya berhasil (tetapi lihat di bawah):
Jika Anda membutuhkan perilaku yang lebih kompleks, silakan edit jawaban Anda.
sunting
Berikut ini mungkin akan lebih hemat memori untuk kumpulan data besar:
sumber
Untuk menjawab dorongan utama pertanyaan Anda, Anda ingin atribut read-only dari dict sebagai sumber data yang tidak dapat diubah:
Saya akan menunjukkan cara menggunakan
namedtuple
daricollections
modul untuk mencapai ini:kembali
100
sumber
Dan hasilnya adalah:
sumber
Memperluas ide dari kjfletch
Keluaran:
sumber
Meskipun banyak jawaban diberikan, saya tidak dapat menemukan jawaban yang saya sukai. Saya menemukan solusi saya sendiri yang membuat
property
bekerja untuk kasus yang dinamis. Sumber untuk menjawab pertanyaan awal:sumber
Sesuatu yang bekerja untuk saya adalah ini:
Keluaran
sumber
Saya baru-baru ini mengalami masalah yang sama, solusi yang saya gunakan
__getattr__
dan__setattr__
untuk properti yang ingin saya tangani, semua yang lain diteruskan ke aslinya.sumber
Berikut adalah contoh sederhana untuk membuat objek properti secara terprogram.
sumber
Satu-satunya cara untuk melampirkan properti secara dinamis adalah dengan membuat kelas baru dan instansnya dengan properti baru Anda.
sumber
Banyak jawaban yang disediakan membutuhkan begitu banyak baris per properti, yaitu / dan / atau - apa yang saya anggap implementasi yang jelek atau membosankan karena diperlukan pengulangan untuk banyak properti, dll. Saya lebih suka terus merebus / menyederhanakannya sampai mereka tidak dapat disederhanakan lagi atau sampai tidak ada gunanya untuk melakukannya.
Singkatnya: dalam pekerjaan yang diselesaikan, jika saya mengulangi 2 baris kode, saya biasanya mengubahnya menjadi fungsi pembantu baris tunggal, dan seterusnya ... Saya menyederhanakan matematika atau argumen aneh seperti (start_x, start_y, end_x, end_y) ke (x, y, w, h) yaitu x, y, x + w, y + h (kadang-kadang membutuhkan min / max atau jika w / h negatif dan implementasinya tidak menyukainya, saya akan kurangi dari x / y dan abs w / h dll.).
Mengesampingkan pengambil / penetapan internal adalah cara yang baik untuk pergi, tetapi masalahnya adalah Anda harus melakukan itu untuk setiap kelas, atau induk kelas ke basis itu ... Ini tidak bekerja untuk saya karena saya lebih suka menjadi bebas memilih anak / orang tua untuk warisan, simpul anak, dll.
Saya telah menciptakan solusi yang menjawab pertanyaan tanpa menggunakan tipe data Dict untuk memasok data karena saya menemukan bahwa menjadi membosankan untuk memasukkan data, dll ...
Solusi saya mengharuskan Anda untuk menambahkan 2 baris tambahan di atas kelas Anda untuk membuat kelas dasar untuk kelas yang ingin Anda tambahkan properti, kemudian 1 baris per dan Anda memiliki opsi untuk menambahkan panggilan balik untuk mengontrol data, memberi tahu Anda ketika data berubah , batasi data yang dapat diatur berdasarkan nilai dan / atau tipe data, dan banyak lagi.
Anda juga memiliki opsi untuk menggunakan _object.x, _object.x = value, _object.GetX (), _object.SetX (value) dan mereka ditangani secara setara.
Selain itu, nilai adalah satu-satunya data non-statis yang ditugaskan ke instance kelas, tetapi properti aktual ditugaskan ke kelas yang berarti hal-hal yang tidak ingin Anda ulangi, tidak perlu diulang ... Anda dapat menetapkan nilai default sehingga pengambil tidak perlu setiap kali, meskipun ada opsi untuk mengganti nilai default default, dan ada opsi lain sehingga pengambil mengembalikan nilai yang disimpan mentah dengan menimpa pengembalian default (catatan: metode ini berarti nilai mentah hanya diberikan ketika nilai diberikan, jika tidak, tidak ada - ketika nilai diatur ulang, maka itu menetapkan Tidak ada, dll.)
Ada banyak fungsi pembantu juga - properti pertama yang ditambahkan menambahkan 2 atau lebih pembantu ke kelas untuk mereferensikan nilai instance ... Mereka adalah ResetAccessors (_key, ..) vararg diulangi (semua dapat diulang menggunakan arg yang disebut pertama) ) dan SetAccessors (_key, _value) dengan opsi lebih banyak ditambahkan ke kelas utama untuk membantu dalam efisiensi - yang direncanakan adalah: cara untuk mengelompokkan pengakses bersama, jadi jika Anda cenderung mengatur ulang beberapa waktu, setiap kali , Anda dapat menetapkannya ke grup dan mengatur ulang grup alih-alih mengulangi tombol yang disebutkan setiap kali, dan lainnya.
Instance / nilai tersimpan mentah disimpan di kelas., kelas. referensi Kelas Accessor yang memegang vars / nilai / fungsi statis untuk properti. _kelas. adalah properti itu sendiri yang dipanggil saat diakses melalui kelas instan selama pengaturan / mendapatkan, dll.
Accessor _class .__ menunjuk ke kelas, tetapi karena itu internal maka perlu ditugaskan di kelas yang mengapa saya memilih untuk menggunakan __Name = AccessorFunc (...) untuk menetapkannya, satu baris per properti dengan banyak opsional argumen untuk digunakan (menggunakan vararg yang dikunci karena lebih mudah dan lebih efisien untuk mengidentifikasi dan memelihara) ...
Saya juga membuat banyak fungsi, seperti yang disebutkan, beberapa di antaranya menggunakan informasi fungsi accessor sehingga tidak perlu dipanggil (karena sedikit merepotkan saat ini - saat ini Anda perlu menggunakan _class. .FunctionName (_class_instance , args) - Saya sempat menggunakan stack / trace untuk mengambil referensi instance untuk mengambil nilai dengan menambahkan fungsi-fungsi yang menjalankan bit maraton ini, atau dengan menambahkan accessors ke objek dan menggunakan self (dinamai ini untuk menunjukkan mereka Sedang untuk instance dan untuk mempertahankan akses ke diri, referensi kelas AccessorFunc, dan informasi lainnya dari dalam definisi fungsi).
Ini tidak cukup dilakukan, tetapi ini adalah pegangan kaki yang fantastis. Catatan: Jika Anda tidak menggunakan __Name = AccessorFunc (...) untuk membuat properti, Anda tidak akan memiliki akses ke kunci __ meskipun saya mendefinisikannya dalam fungsi init. Jika Anda melakukannya, maka tidak ada masalah.
Juga: Perhatikan bahwa Nama dan Kunci berbeda ... Nama itu 'formal', digunakan dalam Pembuatan Nama Fungsi, dan kuncinya adalah untuk penyimpanan data dan akses. yaitu _class.x di mana huruf kecil x adalah kunci, namanya akan menjadi huruf besar X sehingga GetX () adalah fungsi alih-alih Getx () yang terlihat sedikit aneh. ini memungkinkan self.x bekerja dan terlihat sesuai, tetapi juga memungkinkan GetX () dan terlihat sesuai.
Saya memiliki contoh kelas yang diatur dengan kunci / nama yang identik, dan berbeda untuk ditampilkan. banyak fungsi pembantu dibuat untuk menampilkan data (Catatan: Tidak semua ini selesai) sehingga Anda dapat melihat apa yang sedang terjadi.
Daftar fungsi saat ini menggunakan kunci: x, nama: X output sebagai:
Ini bukan daftar lengkap - ada beberapa yang belum membuatnya pada saat posting ...
Beberapa data yang dihasilkan adalah:
Ini untuk kelas baru yang dibuat menggunakan kelas Demo tanpa data yang diberikan selain nama (sehingga bisa menjadi output) yaitu _foo, nama variabel yang saya gunakan ...
Dan ini setelah menetapkan semua properti _foo (kecuali nama) nilai-nilai berikut dalam urutan yang sama: 'string', 1.0, True, 9, 10, False
Perhatikan bahwa karena tipe data atau batasan nilai terbatas, beberapa data tidak ditugaskan - ini adalah desain. Penyetel melarang tipe data atau nilai buruk ditugaskan, bahkan dari ditugaskan sebagai nilai default (kecuali Anda mengabaikan perilaku perlindungan nilai default)
Kode belum diposting di sini karena saya tidak punya ruang setelah contoh dan penjelasan ... Juga karena itu akan berubah.
Harap Catatan: pada saat posting ini, file berantakan - ini akan berubah. Tetapi, jika Anda menjalankannya dalam Sublime Text dan mengkompilasinya, atau menjalankannya dari Python, itu akan mengkompilasi dan mengeluarkan banyak informasi - bagian AccessorDB tidak dilakukan (yang akan digunakan untuk memperbarui Print Getters dan GetKeyOutput helper fungsi bersamaan dengan diubah menjadi fungsi Instance, mungkin dimasukkan ke dalam fungsi tunggal dan diganti namanya - lihat ..)
Berikutnya: Tidak semua yang diperlukan untuk menjalankannya - banyak hal yang dikomentari di bagian bawah adalah untuk informasi lebih lanjut yang digunakan untuk debugging - mungkin tidak ada di sana ketika Anda mengunduhnya. Jika ya, Anda harus dapat menghapus komentar dan mengkompilasi ulang untuk mendapatkan informasi lebih lanjut.
Saya mencari solusi untuk memerlukan MyClassBase: pass, MyClass (MyClassBase): ... - jika Anda tahu solusinya - posting.
Satu-satunya hal yang diperlukan di kelas adalah baris __ - str adalah untuk debugging seperti halnya init - mereka dapat dihapus dari Kelas Demo tetapi Anda perlu mengomentari atau menghapus beberapa baris di bawah ini (_foo / 2/3 ) ..
Kelas String, Dict, dan Util di bagian atas adalah bagian dari perpustakaan Python saya - mereka tidak lengkap. Saya menyalin beberapa hal yang saya butuhkan dari perpustakaan, dan saya membuat beberapa hal baru. Kode lengkap akan ditautkan ke perpustakaan lengkap dan akan memasukkannya bersama dengan memberikan panggilan yang diperbarui dan menghapus kode (sebenarnya, satu-satunya kode yang tersisa adalah Kelas Demo dan pernyataan cetak - sistem AccessorFunc akan dipindahkan ke perpustakaan). ..
Bagian dari file:
Keindahan ini membuatnya sangat mudah untuk membuat kelas baru dengan properti yang ditambahkan secara dinamis dengan AccessorFuncs / callbacks / tipe data / penegakan nilai, dll.
Untuk saat ini, tautannya ada di (Tautan ini harus mencerminkan perubahan pada dokumen.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
Juga: Jika Anda tidak menggunakan Teks Sublime, saya merekomendasikannya pada Notepad ++, Atom, Visual Code, dan lainnya karena implementasi threading yang tepat membuatnya jauh, lebih cepat untuk digunakan ... Saya juga bekerja pada kode seperti IDE sistem pemetaan untuk itu - lihat di: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Tambahkan Repo di Package Manager terlebih dahulu, lalu Instal Plugin - ketika versi 1.0.0 siap, saya akan menambahkan ke daftar plugin utama ...)
Saya harap solusi ini membantu ... dan, seperti biasa:
Hanya karena berfungsi, tidak membuatnya benar - Josh 'Acecool' Moser
sumber