Jadi, saya bermain-main dengan Python saat menjawab pertanyaan ini , dan saya menemukan bahwa ini tidak valid:
o = object()
o.attr = 'hello'
karena sebuah AttributeError: 'object' object has no attribute 'attr'
. Namun, dengan kelas apa pun yang diwarisi dari objek, itu valid:
class Sub(object):
pass
s = Sub()
s.attr = 'hello'
Pencetakan s.attr
menampilkan 'halo' seperti yang diharapkan. Mengapa demikian? Apa dalam spesifikasi bahasa Python yang menentukan bahwa Anda tidak dapat menetapkan atribut ke objek vanilla?
python
attributes
language-design
Smashery
sumber
sumber
object
tidak dapat diubah dan atribut baru tidak dapat ditambahkan? Sepertinya ini yang paling masuk akal.object
instance kelas, bukan padaobject
kelas.Jawaban:
Untuk mendukung penetapan atribut arbitrer, sebuah objek membutuhkan a
__dict__
: a dict terkait dengan objek tersebut, di mana atribut arbitrer dapat disimpan. Jika tidak, tidak ada tempat untuk meletakkan atribut baru.Sebuah contoh dari
object
tidak tidak membawa sekitar__dict__
- jika itu terjadi, sebelum mengerikan masalah ketergantungan melingkar (karenadict
, seperti kebanyakan segalanya, mewarisi dariobject
;-), ini akan pelana setiap objek di Python dengan dict, yang berarti overhead dari banyak byte per objek yang saat ini tidak memiliki atau memerlukan dict (pada dasarnya, semua objek yang tidak memiliki atribut yang dapat ditetapkan secara sewenang-wenang tidak memiliki atau memerlukan dict).Misalnya, menggunakan excellent
pympler
proyek yang (Anda bisa mendapatkannya melalui svn dari sini ), kita dapat melakukan beberapa pengukuran ...:>>> from pympler import asizeof >>> asizeof.asizeof({}) 144 >>> asizeof.asizeof(23) 16
Anda tidak ingin setiap orang
int
mengambil 144 byte, bukan hanya 16, kan? -)Sekarang, ketika Anda membuat kelas (mewarisi dari apa pun), banyak hal berubah ...:
>>> class dint(int): pass ... >>> asizeof.asizeof(dint(23)) 184
...itu
__dict__
ini sekarang ditambahkan (ditambah, sedikit lebih overhead) - sehinggadint
misalnya dapat memiliki atribut sewenang-wenang, tetapi Anda membayar cukup biaya ruang untuk fleksibilitas itu.Jadi bagaimana jika Anda menginginkan
int
hanya dengan satu atribut tambahanfoobar
...? Ini adalah kebutuhan yang langka, tetapi Python memang menawarkan mekanisme khusus untuk tujuan tersebut ...>>> class fint(int): ... __slots__ = 'foobar', ... def __init__(self, x): self.foobar=x+100 ... >>> asizeof.asizeof(fint(23)) 80
... tidak cukup sebagai kecil sebagai
int
, pikiran Anda! (atau bahkan keduanyaint
, satuself
dan satuself.foobar
- yang kedua dapat dipindahkan), tapi pasti jauh lebih baik daripada adint
.Ketika kelas memiliki
__slots__
atribut khusus (urutan string), makaclass
pernyataan (lebih tepatnya, metaclass default,type
) tidak melengkapi setiap instance kelas itu dengan__dict__
(dan karena itu kemampuan untuk memiliki atribut arbitrer), hanya terbatas , kumpulan "slot" yang kaku (pada dasarnya tempat yang masing-masing dapat menampung satu referensi ke beberapa objek) dengan nama yang diberikan.Dalam pertukaran untuk fleksibilitas yang hilang, Anda mendapatkan banyak byte per contoh (mungkin bermakna hanya jika Anda memiliki zillions kasus berkeliaran di sekitar, tapi, ada yang menggunakan kasus untuk itu).
sumber
__slots__
tidak bekerja dengan jenis variabel-panjang sepertistr
,tuple
, dan Python 3 jugaint
.__dict__
, apakah kelasnya harus memiliki__slot__
atribut?Sub
memiliki__dict__
atribut dan objek tidak, karena ituSub
mewarisiobject
, bagaimana atribut itu (dan sejenisnya__module__
) ditambahkan ke dalam warisan? Mungkin ini bisa menjadi pertanyaan baru__dict__
hanya dibuat saat pertama kali dibutuhkan, jadi situasi biaya memori tidak sesederhanaasizeof
keluaran membuatnya terlihat. (asizeof
tidak tahu bagaimana menghindari__dict__
materialisasi.) Anda dapat melihat dict tidak terwujud hingga dibutuhkan dalam contoh ini , dan Anda dapat melihat salah satu jalur kode yang bertanggung jawab untuk__dict__
materialisasi di sini .Seperti yang dikatakan oleh penjawab lain, an
object
tidak memiliki a__dict__
.object
adalah kelas dasar dari semua jenis, termasukint
ataustr
. Dengan demikian apapun yang diberikanobject
akan menjadi beban bagi mereka juga. Bahkan sesuatu yang sederhana seperti opsional__dict__
akan membutuhkan penunjuk tambahan untuk setiap nilai; ini akan membuang 4-8 byte memori tambahan untuk setiap objek dalam sistem, untuk utilitas yang sangat terbatas.Alih-alih melakukan instance kelas dummy, dengan Python 3.3+, Anda dapat (dan harus) menggunakannya
types.SimpleNamespace
.sumber
Ini hanya karena pengoptimalan.
Dicts relatif besar.
>>> import sys >>> sys.getsizeof((lambda:1).__dict__) 140
Kebanyakan (mungkin semua) kelas yang didefinisikan dalam C tidak memiliki perintah untuk pengoptimalan.
Jika Anda melihat kode sumber, Anda akan melihat bahwa ada banyak pemeriksaan untuk melihat apakah objek memiliki dict atau tidak.
sumber
Jadi, menyelidiki pertanyaan saya sendiri, saya menemukan ini tentang bahasa Python: Anda dapat mewarisi dari hal-hal seperti int, dan Anda melihat perilaku yang sama:
>>> class MyInt(int): pass >>> x = MyInt() >>> print x 0 >>> x.hello = 4 >>> print x.hello 4 >>> x = x + 1 >>> print x 1 >>> print x.hello Traceback (most recent call last): File "<interactive input>", line 1, in <module> AttributeError: 'int' object has no attribute 'hello'
Saya berasumsi kesalahan pada akhirnya adalah karena fungsi add mengembalikan int, jadi saya harus mengganti fungsi seperti
__add__
dan semacamnya untuk mempertahankan atribut khusus saya. Tapi ini semua sekarang masuk akal bagi saya (menurut saya), ketika saya memikirkan "objek" seperti "int".sumber
Itu karena objek adalah "tipe", bukan kelas. Secara umum, semua kelas yang didefinisikan dalam ekstensi C (seperti semua tipe data bawaan, dan hal-hal seperti array numpy) tidak mengizinkan penambahan atribut arbitrer.
sumber
__dict__
, karena alasan yang diberikan oleh Alex Martelli.https://docs.python.org/3/library/functions.html#object :
sumber
Ini adalah (IMO) salah satu batasan mendasar dengan Python - Anda tidak dapat membuka kembali kelas. Saya percaya masalah sebenarnya, disebabkan oleh fakta bahwa kelas yang diimplementasikan dalam C tidak dapat diubah pada saat runtime ... subclass bisa, tetapi tidak kelas dasar.
sumber