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.mymindan obj1.mymaxatribut semuanya berfungsi dengan baik. Tetapi ketika saya mencoba mengakses obj1.mycurrentatribut - 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.mycurrentatribut - 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
AttributeErroreksepsi 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 AttributeErrorpengecualian 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.
currentdidefinisikan pada instance Count(lihat __init__), jadi cukup naikkan AttributeErrorjika 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 AttributeErrornama 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.