1 Pertanyaan: objek 'super' tidak memanggil __getattr__

pertanyaan dibuat di Tue, Aug 21, 2012 12:00 AM

Saya punya satu objek yang dibungkus di dalam yang lain. "Wrapper" mengakses atribut dari objek "Dibungkus" dengan menimpa __getattr__. Ini berfungsi dengan baik sampai saya perlu menimpa atribut pada sub kelas, dan kemudian mengakses atribut dari kelas dasar menggunakan super().

Saya masih bisa mengakses atribut langsung dari __getattr__ tetapi mengapa super() tidak berfungsi?

 
class Wrapped(object):
    def __init__(self, value):
        self.value = value

    def hello_world(self):
        print 'hello world', self.value

class Wrapper(object):
    def __init__(self, obj):
        self.wrapped_obj = obj

    def __getattr__(self, name):
        if name in self.__dict__:
            return getattr(self, name)
        else:
            return getattr(self.wrapped_obj, name)

class Subclass(Wrapper):
    def __init__(self, obj):
        super(Subclass, self).__init__(obj)

    def hello_world(self):
        # this works
        func = super(Subclass, self).__getattr__('hello_world')()
        # this doesn't
        super(Subclass, self).hello_world()

a = Wrapped(2)
b = Subclass(a)
b.hello_world()
    
17
  1. Saya tidak yakin apakah saya membaca dokumentasi dengan benar, tetapi ini tampaknya merupakan fitur: Perhatikan bahwa super() diimplementasikan sebagai bagian dari proses pengikatan untuk pencarian atribut bertitik eksplisit seperti super().__getitem__(name). Itu melakukannya dengan menerapkan metode __getattribute__() sendiri untuk mencari kelas dalam urutan yang dapat diprediksi yang mendukung multiple inheritance. Karenanya, super() tidak ditentukan untuk pencarian implisit menggunakan pernyataan atau operator seperti super()[name].
    2012-08-21 02: 55: 58Z
  2. Sebagai catatan, kode __getattr__ Anda di dalam if name in self.__dict__: tidak akan pernah tercapai: __getattr__ tidak dipanggil jika nama didefinisikan dalam __dict__ instance. Ini merupakan sumber yang berpotensi sulit ditemukan bug.
    2012-08-21 16: 26: 07Z
1 Jawaban                              1                         

Menurut ini, super tidak mengizinkan panggilan implisit dari "hook" "fungsi seperti __getattr__. Saya tidak yakin mengapa ini diterapkan dengan cara ini (mungkin ada alasan yang bagus dan hal-hal sudah cukup membingungkan karena objek super memiliki __getattribute__ dan __get__ metode kustom seperti itu), tetapi sepertinya itu memang seperti itu.

Edit: Posting ini tampaknya sedikit memperjelas. Sepertinya masalahnya adalah lapisan tambahan tipuan yang disebabkan oleh __getattribute__ diabaikan ketika memanggil fungsi secara implisit. Melakukan foo.x setara dengan

 
foo.__getattr__(x)

(Dengan asumsi tidak ada metode __getattribute__ didefinisikan dan x tidak dalam foo.__dict__) Namun, ini TIDAK setara dengan

 
foo.__getattribute__('__getattr__')(x)

Karena super mengembalikan objek proxy, ia memiliki lapisan tipuan ekstra yang menyebabkan banyak hal gagal.

P.S. Pemeriksaan self.__dict__ di fungsi __getattr__ Anda sama sekali tidak perlu. __getattr__ hanya dipanggil jika atributnya belum ada dict Anda. (Gunakan __getattribute__ jika Anda ingin selalu dipanggil, tetapi kemudian Anda harus sangat berhati-hati, karena bahkan sesuatu yang sederhana seperti if name in self.__dict__ akan menyebabkan rekursi tak terbatas.

    
12
2012-08-21 03: 41: 21Z
  1. Ini memang terlihat seperti bug - setidaknya tidak mematuhi Prinsip Terkejut;)
    2016-03-08 11: 12: 06Z
  2. @ Tony di sisi lain, ada alasan kuat untuk membatasi tipuan metode sulap. Tidak hanya untuk kinerja, tetapi karena jika tidak Anda akan masuk ke loop tak terbatas dalam beberapa kasus (seperti harus menelepon __getattribute__ untuk menemukan metode __getattribute__!)
    2016-03-08 20: 46: 08Z
sumber ditempatkan sini