__Getattr__ vs __getattribute__ arasındaki fark


Yanıtlar:


497

Arasındaki temel farklılık __getattr__ve __getattribute__olmasıdır __getattr__nitelik zamanki yollar bulunamadığı takdirde sadece çağrılır. Eksik nitelikler için bir geri dönüş uygulamak için iyidir ve muhtemelen istediğiniz iki taneden biridir.

__getattribute__nesnenin gerçek özelliklerine bakmadan önce çağrılır ve bu nedenle doğru bir şekilde uygulanması zor olabilir. Sonsuz özyinelemelere çok kolayca son verebilirsiniz.

Yeni stil sınıflarından türetilir object, eski stil sınıfları Python 2.x'te açık taban sınıfı olmayan sınıflardır. Ancak, eski stil ile yeni stil sınıfları arasındaki fark __getattr__ve arasında seçim yaparken önemli değildir __getattribute__.

Neredeyse kesinlikle istiyorsun __getattr__.


6
İkisini de aynı sınıfta uygulayabilir miyim? Eğer yapabilirsem, ikisini de uygulamak için ne var?
Alcott

13
@Alcott: ikisini de uygulayabilirsiniz, ama neden yapacağınızdan emin değilim. __getattribute__her erişim __getattr__için çağrılacak ve __getattribute__yükseltilen zamanlar için çağrılacaktır AttributeError. Neden hepsini bir arada tutmuyorsun?
Ned Batchelder

10
@NedBatchelder, mevcut yöntemlere yapılan çağrıları (koşullu olarak) geçersiz kılmak istiyorsanız, kullanmak istersiniz __getattribute__.
Jace Browning

28
"Bu yöntemde sonsuz özyinelemeden kaçınmak için, uygulamasının her zaman aynı ada sahip temel sınıf yöntemini çağırması gerekir, örneğin nesne .__ getattribute __ (self, name)."
kmonsoor

1
@kmonsoor. Her ikisini de uygulamak için iyi bir neden. doğru koşullar altında objec.__getattribute__çağırır myclass.__getattr__.
Mad Physicist

138

Her ikisinin __getattr__ve __getattribute__sihirli yöntemlerin bazı basit örneklerini görelim .

__getattr__

Python, __getattr__daha önce tanımlanmamış bir özellik talep ettiğinizde yöntemi çağırır . Aşağıdaki örnekte sınıfımın Count__getattr__ yöntemi yoktur . Şimdi her ikisine de erişmeye çalıştığımda obj1.myminve obj1.mymaxniteliklerde her şey iyi çalışıyor. Ama obj1.mycurrentözniteliğe erişmeye çalıştığımda - Python banaAttributeError: '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'

Şimdi benim sınıf Kont sahiptir __getattr__yöntemi. Şimdi obj1.mycurrentözniteliğe erişmeye çalıştığımda - python __getattr__yöntemime uyguladığım her şeyi döndürüyor . Örneğimde, var olmayan bir özniteliği çağırmaya çalıştığımda, python bu özniteliği oluşturur ve 0 tamsayı değerine ayarlar.

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__

Şimdi __getattribute__yöntemi görelim . __getattribute__Sınıfınızda bir yöntem varsa , python, var olup olmadığına bakılmaksızın her öznitelik için bu yöntemi çağırır. Peki neden __getattribute__yönteme ihtiyacımız var ? Bunun iyi bir nedeni, özniteliklere erişimi engelleyebilir ve aşağıdaki örnekte gösterildiği gibi daha güvenli hale getirebilirsiniz.

Birisi alt dize 'cur' ile başlayan özniteliklerime erişmeye çalıştığında python AttributeErroristisna oluşturur. Aksi takdirde bu niteliği döndürür.

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)

Önemli: __getattribute__Yöntemde sonsuz özyinelemeden kaçınmak için , uygulaması, gereken özelliklere 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)ya da super().__getattribute__(item)değilself.__dict__[item]

ÖNEMLİ

Sınıf hem içeriyorsa getattr ve getAttribute sonra sihirli yöntemleri __getattribute__ilk olarak adlandırılır. Ancak istisna __getattribute__yükselirse AttributeErroristisna dikkate alınmaz ve __getattr__yöntem çağrılır. Aşağıdaki örneğe bakın:

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)

1
Tam olarak geçersiz kılmak için ne kullanacağımdan emin değilim __getattribute__ama kesinlikle bu değil. Senin başına Örneğin, sen yapıyorsun tüm Çünkü __getattribute__yükselterek olduğu AttributeErrornitelik de yoksa istisna __dict__nesnesinin; ancak buna gerçekten ihtiyacınız yok, çünkü bu varsayılan uygulama __getattribute__ve infact __getattr__tam olarak bir geri dönüş mekanizması olarak ihtiyacınız olan şey.
Rohit

@Rohit currentolaylarına tanımlanır Count(bkz __init__), bu yüzden sadece yükselterek AttributeErrorbu uygulamalara atıfta - nitelik oldukça ne oluyor orada değil değilse __getattr__için tüm dahil isimlerin 'cur' başlayan current, ama aynı zamanda curious, curly...
joelb

16

Bu sadece Ned Batchelder'in açıklamasına dayanan bir örnektir .

__getattr__ misal:

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')

Aynı örnek ile kullanılırsa, __getattribute__>>>RuntimeError: maximum recursion depth exceeded while calling a Python object


9
Aslında, bu korkunç. Gerçek dünyadaki __getattr__()uygulamalar, yalnızca AttributeErrorgeçersiz özellik adları için yükselterek sınırlı bir geçerli özellik adı kümesini kabul eder , böylece ince ve hata ayıklaması zor sorunları önler . Bu örnek koşulsuz olarak tüm özellik adlarını geçerli olarak kabul eder - tuhaf (ve açıkçası hataya eğilimli) kötüye kullanım __getattr__(). Bu örnekte olduğu gibi öznitelik oluşturma üzerinde "tam kontrol" istiyorsanız, __getattribute__()bunun yerine istediğinizi belirtin.
Cecil Curry

3
@CecilCurry: Bağlandığınız tüm sorunlar, bu cevabın yapmadığı bir değer yerine örtük olarak Hiçbiri döndürmesini içeriyor. Tüm özellik adlarını kabul etmede sorun nedir? İle aynıdır defaultdict.
Nick Matteo

2
Sorun, üst sınıf aramasından önce__getattr__ çağrılacak olmasıdır . Bu doğrudan bir alt sınıf için uygundur , çünkü gerçekten umursadığınız tek yöntem yine de örneği görmezden gelen sihirli yöntemler vardır, ancak daha karmaşık miras yapısı için, herhangi bir şeyi üst öğeden devralma yeteneğini tamamen kaldırırsınız. object
Mad Physicist

1
@Simon K Bhatta4ya, son baskı bildiriminiz bir yorum. Sağ? Bu uzun bir satır ve okumak için sıkıcı (Biri sağ tarafta çok kaydırmalı). Kod bölümünden sonra satırı koymaya ne dersiniz? Ya da kod bölümüne koymak istiyorsanız, iki farklı satıra bölmek daha iyi olacağını düşünüyorum.
Md. Abu Nafee Ibna Zahid

14

Yeni stil sınıfları objectbaşka bir yeni stil sınıfından miras alır veya başka bir yeni stil sınıfından miras alır :

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

Eski tarz sınıflar şunları yapmaz:

class SomeObject:
    pass

Bu sadece Python 2 için geçerlidir - Python 3'te yukarıdakilerin hepsi yeni stil sınıfları yaratacaktır.

Bkz. 9. Sınıflar (Python eğitimi), NewClassVsClassicClass ve Python'daki eski stil ile yeni stil sınıfları arasındaki fark nedir? detaylar için.


6

Yeni stil sınıfları, "doğrudan" veya dolaylı olarak "nesne" sınıfına giren sınıflardır. Bunlara __new__ek olarak bir sınıf yöntemi vardır __init__ve biraz daha rasyonel düşük seviyeli davranışa sahiptirler.

Genellikle, geçersiz kılmak istersiniz __getattr__(ikisini de geçersiz kılarsanız ), aksi takdirde yöntemlerinizde "self.foo" sözdizimini desteklemekte zorlanırsınız.

Ek bilgi: http://www.devx.com/opensource/Article/31482/0/page/4


2

Beazley & Jones PCB'yi okurken __getattr__, OP sorununun "ne zaman" kısmının yanıtlanmasına yardımcı olan açık ve pratik bir kullanım senaryosuna rastladım . Kitaptan:

" __getattr__()Yöntem, öznitelik araması için bir tümünü yakala gibi. Kod, var olmayan bir özniteliğe erişmeye çalışırsa çağrılan bir yöntemdir." Bunu yukarıdaki cevaplardan biliyoruz, ancak PCB tarifi 8.15'te, bu işlevsellik, delegasyon tasarım modelini uygulamak için kullanılır . A Nesnesi, A Nesnesinin temsil etmek istediği birçok yöntemi uygulayan bir Nesne B özniteliğine sahipse, yalnızca Nesne B'nin yalnızca Nesne B'nin yöntemlerini çağırmak için Nesne B'deki tüm yöntemlerini yeniden tanımlamak yerine __getattr__(), aşağıdaki gibi bir yöntem tanımlayın :

def __getattr__(self, name):
    return getattr(self._b, name)

burada _b, Object B olan Object A özniteliğinin adıdır. Object B'de tanımlanan bir yöntem Object A'da __getattr__çağrıldığında , yöntem arama zincirinin sonunda çağrılır. Yalnızca başka bir nesneye yetki vermek için tanımlanmış yöntemlerin bir listesine sahip olmadığınız için, bu da kodu temizler.

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.