__Getattribute__ öğesini sonsuz özyineleme hatası olmadan nasıl uygularım?


102

Bir sınıftaki bir değişkene erişimi geçersiz kılmak, ancak diğerlerini normal şekilde döndürmek istiyorum. Bunu ile nasıl başarabilirim __getattribute__?

Aşağıdakini denedim (bu da ne yapmaya çalıştığımı göstermeli) ancak bir özyineleme hatası alıyorum:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

Yanıtlar:


128

Bir özyineleme hatası alıyorsunuz çünkü self.__dict__içindeki özniteliğe erişme girişiminiz __getattribute__sizi __getattribute__yeniden çağırıyor . Eğer kullanırsanız objects' __getattribute__yerine, çalışır:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return object.__getattribute__(self, name)

Bu işe yarar çünkü object(bu örnekte) temel sınıftır. Temel sürümünü arayarak, __getattribute__daha önce içinde bulunduğunuz yinelemeli cehennemden kaçının.

Foo.py kodlu Ipython çıktısı:

In [1]: from foo import *

In [2]: d = D()

In [3]: d.test
Out[3]: 0.0

In [4]: d.test2
Out[4]: 21

Güncelleme:

Mevcut belgelerdeki yeni stil sınıfları için daha fazla öznitelik erişimi başlıklı bölümde , sonsuz özyinelemeden kaçınmak için tam olarak bunu yapmayı önerdikleri bir şey var.


1
İlginç. Peki orada sen ne yapıyorsun? Nesne neden değişkenlerime sahip olsun?
Greg

1
..ve her zaman nesneyi kullanmak istiyor muyum? Ya diğer sınıflardan miras alırsam?
Greg

1
Evet, her sınıf oluşturduğunuzda ve kendi sınıfınızı yazmadığınızda , nesne tarafından sağlanan get özniteliğini kullanırsınız .
Egil

20
super () kullanmak ve bu nedenle python'un temel sınıflarınızda bulduğu ilk getattribute yöntemini kullanmak daha iyi değil mi? -super(D, self).__getattribute__(name)
gepatino

10
Ya da sadece super().__getattribute__(name)Python 3'te kullanabilirsiniz .
jeromej

25

Aslında __getattr__bunun yerine özel yöntemi kullanmak istediğine inanıyorum .

Python belgelerinden alıntı:

__getattr__( self, name)

Bir öznitelik araması, özniteliği olağan yerlerde bulamadığında çağrılır (yani, bu bir örnek özniteliği değildir veya kendi için sınıf ağacında bulunmaz). name, öznitelik adıdır. Bu yöntem, (hesaplanan) öznitelik değerini döndürmeli veya bir AttributeError istisnası oluşturmalıdır.
Öznitelik normal mekanizma aracılığıyla bulunursa __getattr__()çağrılmadığını unutmayın. (Bu, __getattr__()ve arasındaki kasıtlı bir asimetridir __setattr__().) Bu, hem verimlilik nedenleriyle hem __setattr__()de örneğin diğer özelliklerine erişmenin bir yolu olmadığı için yapılır . En azından örnek değişkenler için, örnek öznitelik sözlüğüne herhangi bir değer girmeyerek (bunun yerine bunları başka bir nesneye ekleyerek) toplam kontrolü taklit edebileceğinizi unutmayın. Bakın__getattribute__() Yeni stil sınıflarında tam kontrol elde etmenin bir yolu için aşağıdaki yöntem.

Not: işe Bunun için, örnek olmalıdır değil bir var testçizgi böylece, niteliği self.test=20çıkarılmalıdır.


2
Aslında, OP'nin kodunun doğasına göre , onu her zaman "olağan yerlerde" bulacağı __getattr__için geçersiz kılmak testişe yaramaz.
Casey Kuball

1
ilgili Python belgelerine giden mevcut bağlantı (yanıtta belirtilenden farklı görünüyor): docs.python.org/3/reference/datamodel.html#object.__getattr__
CrepeGoat

17

Python dili referansı:

Bu yöntemde sonsuz özyinelemeden kaçınmak için, uygulaması, ihtiyaç duyduğu özniteliklere erişmek için her zaman aynı ada sahip temel sınıf yöntemini çağırmalıdır, örneğin, object.__getattribute__(self, name) .

Anlamı:

def __getattribute__(self,name):
    ...
        return self.__dict__[name]

Adlı bir özelliği arıyorsunuz __dict__. Çünkü bir nitelik olduğu __getattribute__için, __dict__hangi çağrıların çağrıldığı aramada çağrılır __getattribute__... yada yada yada

return  object.__getattribute__(self, name)

Temel sınıfları kullanmak __getattribute__gerçek özniteliği bulmaya yardımcı olur.


13

Kullanmak istediğinizden emin misiniz __getattribute__? Gerçekte ne başarmaya çalışıyorsun?

İstediğinizi yapmanın en kolay yolu şudur:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    test = 0

veya:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    @property
    def test(self):
        return 0

Düzenleme: Bir örneğinin her durumda Dfarklı değerlere sahip olacağını unutmayın test. İlk durumda d.test20, ikincisinde 0 olacaktır. Nedenini bulmak için bunu size bırakacağım.

Düzenleme2: Greg, örnek 2'nin, özellik salt okunur olduğu ve __init__yöntem onu ​​20'ye ayarlamaya çalıştığı için başarısız olacağını belirtti . Bunun daha eksiksiz bir örneği şöyle olabilir:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    _test = 0

    def get_test(self):
        return self._test

    def set_test(self, value):
        self._test = value

    test = property(get_test, set_test)

Açıkçası, bir sınıf olarak bu neredeyse tamamen yararsızdır, ancak size devam etmeniz için bir fikir verir.


Ah, dersi yönettiğinizde sessiz olmuyor, değil mi? "Script1.py" dosyası, satır 5, init self.test = 20 AttributeError: öznitelik ayarlanamıyor
Greg

Doğru. Bunu üçüncü bir örnek olarak düzelteceğim. İyi tespit edildi.
Singletoned

5

İşte daha güvenilir bir versiyon:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21
    def __getattribute__(self, name):
        if name == 'test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

Ana sınıftan __ getattribute __ yöntemini çağırır ve sonunda nesneye geri döner. Diğer atalar onu geçersiz kılmazsa __ getattribute __ yöntemi.


5

__getattribute__Yöntem nasıl kullanılır?

Normal noktalı aramadan önce çağrılır. Eğer yükselirse AttributeErrorararız __getattr__.

Bu yöntemin kullanımı oldukça nadirdir. Standart kitaplıkta yalnızca iki tanım vardır:

$ grep -Erl  "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py

En iyi pratik

Tek bir özniteliğe erişimi programlı olarak kontrol etmenin uygun yolu şudur property. Sınıf Daşağıdaki gibi yazılmalıdır (açık istenen davranışı kopyalamak için isteğe bağlı olarak ayarlayıcı ve silici ile):

class D(object):
    def __init__(self):
        self.test2=21

    @property
    def test(self):
        return 0.

    @test.setter
    def test(self, value):
        '''dummy function to avoid AttributeError on setting property'''

    @test.deleter
    def test(self):
        '''dummy function to avoid AttributeError on deleting property'''

Ve kullanım:

>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0

Bir özellik bir veri tanımlayıcısıdır, bu nedenle normal noktalı arama algoritmasında aranan ilk şeydir.

İçin seçenekler __getattribute__

Her öznitelik için arama yapmanız gerekiyorsa, birkaç seçeneğiniz vardır __getattribute__.

  • yükseltmek AttributeError, __getattr__çağrılmasına neden olmak (uygulanmışsa)
  • ondan bir şey geri vermek
    • superüst (muhtemelen object) uygulamasını çağırmak için kullanarak
    • arama __getattr__
    • kendi noktalı arama algoritmanızı bir şekilde uygulamak

Örneğin:

class NoisyAttributes(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self, name):
        print('getting: ' + name)
        try:
            return super(NoisyAttributes, self).__getattribute__(name)
        except AttributeError:
            print('oh no, AttributeError caught and reraising')
            raise
    def __getattr__(self, name):
        """Called if __getattribute__ raises AttributeError"""
        return 'close but no ' + name    


>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20

Başlangıçta istediğin şeyi.

Ve bu örnek, başlangıçta istediğiniz şeyi nasıl yapabileceğinizi göstermektedir:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

Ve şöyle davranacak:

>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test

Traceback (most recent call last):
  File "<pyshell#216>", line 1, in <module>
    del o.test
AttributeError: test

Kod incelemesi

Yorum içeren kodunuz. Kendi kendine girişte noktalı bir aramanız var __getattribute__. Bu nedenle bir özyineleme hatası alıyorsunuz. İsmin olup olmadığını kontrol edebilir "__dict__"ve supergeçici çözüm için kullanabilirsiniz , ancak bu kapsamaz __slots__. Bunu bir alıştırma olarak okuyucuya bırakacağım.

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:      #   v--- Dotted lookup on self in __getattribute__
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.