Bagaimana cara mengganti __getattr__ dengan Python tanpa melanggar perilaku default?


185

Saya ingin mengganti __getattr__metode pada kelas untuk melakukan sesuatu yang mewah tetapi saya tidak ingin merusak perilaku default.

Apa cara yang benar untuk melakukan ini?


20
"Hancurkan," katanya, bukan "berubah." Itu cukup jelas: atribut "mewah" tidak boleh mengganggu atribut bawaan dan harus berperilaku sebanyak mungkin seperti mereka. Jawaban Michael benar dan membantu.
olooney

Jawaban:


268

Mengesampingkan __getattr__harus baik - __getattr__hanya disebut sebagai upaya terakhir yaitu jika tidak ada atribut dalam contoh yang cocok dengan namanya. Misalnya, jika Anda mengakses foo.bar, maka __getattr__hanya akan dipanggil jika footidak memiliki atribut yang dipanggil bar. Jika atributnya adalah atribut yang tidak ingin Anda tangani, naikkan AttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

Namun, tidak seperti __getattr__, __getattribute__akan dipanggil dulu (hanya berfungsi untuk kelas gaya baru yaitu yang diwariskan dari objek). Dalam hal ini, Anda dapat mempertahankan perilaku default seperti:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

Lihat dokumen Python untuk informasi lebih lanjut .


Bah, hasil edit Anda memiliki hal yang sama dengan yang saya tunjukkan dalam jawaban saya, +1.

12
Keren, Python sepertinya tidak suka memanggil super dalam __getattr__- ada ide apa yang harus dilakukan? ( AttributeError: 'super' object has no attribute '__getattr__')
gatoatigrado

1
Tanpa melihat kode Anda, sulit untuk mengatakannya, tetapi sepertinya tidak ada superclasses Anda yang mendefinisikan getattr .
Colin vH

Ini bekerja dengan hasattr juga karena: "Ini diterapkan dengan memanggil getattr (objek, nama) dan melihat apakah ia memunculkan pengecualian atau tidak." docs.python.org/2/library/functions.html#hasattr
ShaBANG

1
-1 Ini memang memodifikasi perilaku default. Sekarang Anda memiliki AttributeErrortanpa konteks atribut dalam args pengecualian.
wim

34
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

Terima kasih untuk ini. Hanya ingin memastikan saya memiliki pesan default yang benar tanpa menggali sumbernya.
ShawnFumo

4
Saya pikir itu self.__class__.__name__harus digunakan sebagai pengganti self.__class__kalau-kalau kelas menimpa__repr__
Michael Scott Cuthbert

3
Ini lebih baik daripada jawaban yang diterima, tapi alangkah baiknya tidak harus menulis ulang kode itu dan berpotensi kehilangan perubahan hulu jika kata-katanya diubah atau konteks tambahan ditambahkan ke objek pengecualian di masa depan.
wim

13

Untuk memperluas jawaban Michael, jika Anda ingin mempertahankan perilaku default menggunakan __getattr__, Anda dapat melakukannya seperti:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

Sekarang pesan pengecualian lebih deskriptif:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

2
@ fed.pavlo Anda yakin? Mungkin Anda campur __getattr__dan __getattribute__?
José Luis

salahku. Saya melewatkan panggilan ke metode yang berbeda dari diri saya sendiri. ; (
fed.pavlo

2
Memang jawaban
@Michael
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.