Saya mencoba memahami kapan harus menggunakan __getattr__
atau __getattribute__
. The dokumentasi menyebutkan __getattribute__
berlaku untuk kelas gaya baru. Apa itu kelas gaya baru?
Saya mencoba memahami kapan harus menggunakan __getattr__
atau __getattribute__
. The dokumentasi menyebutkan __getattribute__
berlaku untuk kelas gaya baru. Apa itu kelas gaya baru?
Jawaban:
Perbedaan utama antara __getattr__
dan __getattribute__
itu __getattr__
hanya dipanggil jika atribut tidak ditemukan dengan cara biasa. Ini bagus untuk menerapkan fallback untuk atribut yang hilang, dan mungkin salah satu dari dua yang Anda inginkan.
__getattribute__
dipanggil sebelum melihat atribut sebenarnya pada objek, dan bisa jadi sulit untuk diterapkan dengan benar. Anda dapat berakhir dalam rekursi tak terbatas dengan sangat mudah.
Kelas gaya baru berasal dari object
, kelas gaya lama adalah mereka yang di Python 2.x tanpa kelas dasar eksplisit. Tetapi perbedaan antara kelas gaya lama dan gaya baru bukanlah yang penting ketika memilih antara __getattr__
dan __getattribute__
.
Anda hampir pasti menginginkannya __getattr__
.
__getattribute__
akan dipanggil untuk setiap akses, dan __getattr__
akan dipanggil untuk waktu yang __getattribute__
dimunculkan AttributeError
. Mengapa tidak menyimpan semuanya dalam satu?
__getattribute__
.
objec.__getattribute__
meminta myclass.__getattr__
dalam kondisi yang tepat.
Mari kita lihat beberapa contoh sederhana dari keduanya __getattr__
dan __getattribute__
metode sulap.
__getattr__
Python akan memanggil __getattr__
metode setiap kali Anda meminta atribut yang belum ditentukan. Dalam contoh berikut, Hitungan kelas saya tidak__getattr__
metode. Sekarang pada pokoknya ketika saya mencoba mengakses keduanya obj1.mymin
dan obj1.mymax
atribut semuanya berfungsi dengan baik. Tetapi ketika saya mencoba mengakses obj1.mycurrent
atribut - Python memberi sayaAttributeError: 'Count' object has no attribute 'mycurrent'
class Count():
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent) --> AttributeError: 'Count' object has no attribute 'mycurrent'
Sekarang Count kelas saya memiliki __getattr__
metode. Sekarang ketika saya mencoba mengakses obj1.mycurrent
atribut - python mengembalikan saya apa pun yang telah saya terapkan di__getattr__
metode . Dalam contoh saya setiap kali saya mencoba memanggil atribut yang tidak ada, python membuat atribut itu dan mengaturnya ke nilai integer 0.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
def __getattr__(self, item):
self.__dict__[item]=0
return 0
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)
__getattribute__
Sekarang mari kita lihat __getattribute__
metodenya. Jika Anda memiliki __getattribute__
metode di kelas Anda, python memanggil metode ini untuk setiap atribut terlepas dari apakah itu ada atau tidak. Jadi mengapa kita membutuhkan __getattribute__
metode? Salah satu alasan bagus adalah Anda dapat mencegah akses ke atribut dan membuatnya lebih aman seperti yang ditunjukkan pada contoh berikut.
Setiap kali seseorang mencoba mengakses atribut saya yang dimulai dengan substring 'cur' python memunculkanAttributeError
pengecualian. Kalau tidak, ia mengembalikan atribut itu.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Penting: Untuk menghindari rekursi tak terbatas di __getattribute__
metode, implementasinya harus selalu memanggil metode kelas dasar dengan nama yang sama untuk mengakses atribut apa pun yang diperlukan. Misalnya: object.__getattribute__(self, name)
atau super().__getattribute__(item)
tidakself.__dict__[item]
Jika kelas Anda mengandung kedua getattr dan getAttribute sihir metode kemudian __getattribute__
disebut pertama. Tetapi jika __getattribute__
memunculkan
AttributeError
eksepsi maka eksepsi akan diabaikan dan __getattr__
metode akan dipanggil. Lihat contoh berikut:
class Count(object):
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattr__(self, item):
self.__dict__[item]=0
return 0
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
# note this class subclass object
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
__getattribute__
tapi pasti ini bukan. Karena per contoh Anda, semua yang Anda lakukan __getattribute__
adalah meningkatkan AttributeError
pengecualian jika atribut tidak ada di __dict__
objek; tetapi Anda tidak benar-benar membutuhkan ini karena ini adalah implementasi standar __getattribute__
dan infact __getattr__
adalah persis apa yang Anda butuhkan sebagai mekanisme mundur.
current
didefinisikan pada instance Count
(lihat __init__
), jadi cukup naikkan AttributeError
jika atributnya tidak ada apa yang terjadi - itu mengacu pada__getattr__
untuk semua nama yang dimulai dengan 'cur', termasuk current
, tetapi juga curious
, curly
...
Ini hanyalah contoh berdasarkan penjelasan Ned Batchelder .
__getattr__
contoh:
class Foo(object):
def __getattr__(self, attr):
print "looking up", attr
value = 42
self.__dict__[attr] = value
return value
f = Foo()
print f.x
#output >>> looking up x 42
f.x = 3
print f.x
#output >>> 3
print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')
Dan jika contoh yang sama digunakan dengan __getattribute__
Anda akan mendapatkan >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
__getattr__()
Implementasi dunia nyata hanya menerima set terbatas nama atribut yang valid dengan meningkatkan AttributeError
nama atribut yang tidak valid, sehingga menghindari masalah yang halus dan sulit di-debug . Contoh ini tanpa syarat menerima semua nama atribut sebagai valid - penyalahgunaan aneh dan rawan kesalahan __getattr__()
. Jika Anda ingin "kontrol total" atas pembuatan atribut seperti dalam contoh ini, Anda ingin __getattribute__()
.
defaultdict
.
__getattr__
akan dipanggil sebelum pencarian superclass. Ini tidak masalah untuk subkelas langsung object
, karena satu-satunya metode yang benar-benar Anda pedulikan ada metode ajaib yang mengabaikan instance, tetapi untuk struktur pewarisan yang lebih kompleks, Anda benar-benar menghapus kemampuan untuk mewarisi apa pun dari induk.
Kelas gaya baru diwarisi dari object
, atau dari kelas gaya baru lain:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
Kelas gaya lama tidak:
class SomeObject:
pass
Ini hanya berlaku untuk Python 2 - dalam Python 3 semua hal di atas akan membuat kelas gaya baru.
Lihat 9. Kelas (tutorial Python), NewClassVsClassicClass dan Apa perbedaan antara gaya lama dan kelas gaya baru di Python? untuk detail.
Kelas-kelas gaya baru adalah kelas-kelas yang mengelompokkan "objek" (langsung atau tidak langsung). Mereka memiliki __new__
metode kelas sebagai tambahan__init__
dan memiliki perilaku tingkat rendah yang agak lebih rasional.
Biasanya, Anda ingin menimpanya __getattr__
(jika Anda keduanya), jika tidak, Anda akan kesulitan mendukung sintaks "self.foo" dalam metode Anda.
Info tambahan: http://www.devx.com/opensource/Article/31482/0/page/4
Dalam membaca Beazley & Jones PCB, saya menemukan case-use yang eksplisit dan praktis __getattr__
membantu menjawab bagian "kapan" dari pertanyaan OP. Dari buku:
" __getattr__()
Metodenya seperti catch-all untuk pencarian atribut. Ini adalah metode yang dipanggil jika kode mencoba mengakses atribut yang tidak ada." Kami tahu ini dari jawaban di atas, tetapi dalam resep PCB 8.15, fungsi ini digunakan untuk menerapkan pola desain delegasi . Jika Objek A memiliki atribut Objek B yang mengimplementasikan banyak metode yang ingin didelegasikan Objek A, daripada mendefinisikan ulang semua metode Obyek B di Objek A hanya untuk memanggil metode Objek B, tentukan __getattr__()
metode sebagai berikut:
def __getattr__(self, name):
return getattr(self._b, name)
di mana _b adalah nama atribut Object A yang merupakan Object B. Ketika sebuah metode yang didefinisikan pada Object B disebut pada Object A, __getattr__
metode tersebut akan dipanggil pada akhir rantai pencarian. Ini akan membuat kode lebih bersih juga, karena Anda tidak memiliki daftar metode yang ditentukan hanya untuk mendelegasikan ke objek lain.