2013-03-22 11 views
5

Tanımlayıcıları/dekoratörleri yerleştirmeye çalıştığımda neler olduğunu anlamakta zorlanıyorum. Python 2.7 kullanıyorum.Python'da yuvalama tanımlayıcıları/dekoratörler

Örneğin, property ve classmethod aşağıdaki basitleştirilmiş versiyonları alalım:

class MyProperty(object): 
    def __init__(self, fget): 
     self.fget = fget 
    def __get__(self, obj, objtype=None): 
     print 'IN MyProperty.__get__' 
     return self.fget(obj) 

class MyClassMethod(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, objtype=None): 
     print 'IN MyClassMethod.__get__' 
     def f(*args, **kwargs): 
      return self.f(objtype, *args, **kwargs) 
     return f 

yuvamýza çalışılıyor:

class A(object): 
    # doesn't work: 
    @MyProperty 
    @MyClassMethod 
    def klsproperty(cls): 
     return 555 
    # works: 
    @MyProperty 
    def prop(self): 
     return 111 
    # works: 
    @MyClassMethod 
    def klsmethod(cls, x): 
     return x**2 

% print A.klsproperty 
IN MyProperty.__get__ 
... 
TypeError: 'MyClassMethod' object is not callable 

iç tanımlayıcısı MyClassMethod ait __get__ yöntemi denir almıyor.

class B(object): 
    # works: 
    @NoopDescriptor 
    @MyProperty 
    def prop1(self): 
     return 888 
    # doesn't work: 
    @MyProperty 
    @NoopDescriptor 
    def prop2(self): 
     return 999 

% print B().prop1 
IN NoopDescriptor.__get__ 
IN MyProperty.__get__ 
888 
% print B().prop2 
IN MyProperty.__get__ 
... 
TypeError: 'NoopDescriptor' object is not callable 

: yuvalama hiçbir-op açıklayıcısı/dekoratör kullanmaya çalışıyor

class NoopDescriptor(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, objtype=None): 
     print 'IN NoopDescriptor.__get__' 
     return self.f.__get__(obj, objtype=objtype) 

: (Ben düşündüğüm şey) bir no-op tanımlayıcısı atarak denedi neden anlamaya başarısız Neden B().prop1 çalışır ve B().prop2 neden anlamıyorum.

Sorular: Yanlış yapıyorum

  1. ? Neden object is not callable hatası alıyorum?
  2. Doğru olan nedir? Örneğin. dekoratörler dekoratör o olarak onun süsleyen fonksiyonu ile çağrılır, parametreler olmadan kullanıldığında MyClassMethod ve MyProperty (veya classmethod ve property) Bu durumda
+1

Sezgisel olarak bunu anlamanın anahtarı, "MyProperty" öğesinin bir _data_ özniteliği gibi çalışan bir yöntem oluşturmasıdır, bu nedenle bir yöntem tanımlayıcısıyla onu yuvalayamazsınız. Bu sezgisel anlayış sadece sizi şimdiye kadar götürür; Tam hikaye isedev'in cevabında. – abarnert

cevap

3

Sen kod iş yapabilirsiniz:

class MyProperty(object): 
    def __init__(self, fget): 
     self.fget = fget 
    def __get__(self, obj, objtype=None): 
     print('IN MyProperty.__get__') 
     try: 
      return self.fget.__get__(obj, objtype)() 
     except AttributeError: # self.fget has no __get__ method 
      return self.fget(obj) 

Şimdi örnek kod çalışır:

class A(object): 
    @MyProperty 
    @MyClassMethod 
    def klsproperty(cls): 
     return 555 

print(A.klsproperty) 

çıktısı:

Graham Dumpl'de kendi eski soruma kesin cevabı buldum
IN MyProperty.__get__ 
IN MyClassMethod.__get__ 
555 
+0

Teşekkürler, bu mantıklı. Ama bu önerilen yaklaşım mı? Eğer öyleyse, yerleşik 'mülk' ve 'classmethod' bu şekilde nasıl davranmaz (ve iç içe olamaz)? – shx2

+0

Bu durumun teşvik edilmediğinden veya yerleşik mülkün bunu neden yapmadığından emin değilim. Bunun düzeltilemeyeceği bazı sınırlamalar olduğunu biliyorum. Bir şey için, 'MyClassMethod' yi daha zekice yapmış olsanız bile, tanımlayıcıları ters sıraya yerleştiremezsiniz. Ve '__set__' betimleyici fonksiyonunun class değişken ataması için çağrıldığını sanmıyorum, bu yüzden çalışmak için yazılabilir classproperties için bazı metaclass sihrini yapmanız gerekir.Belki de daha fazla tanımlayıcı programlama deneyimine sahip olan biri diğer konular ve sınırlamalara dayandırılabilir. – Blckknght

5

yeniden kullanırken neyi MyClassProperty tanımlamak için en iyi yoldur parametre. Dekoratörün dönüş değeri, dekore edilmiş işlev yerine kullanılır. Yani: MyProperty yana

def prop(self): 
    ... 
prop = MyProperty(prop) 

aslında A.prop.__get__() arayacak A.prop erişen, açıklayıcısı protokolünü uygular ve bunun içinde (dekore edilmiş nesneyi aramaya __get__ tanımladığınız: için

@MyProperty 
def prop(self): 
    ... 

eşdeğerdir durumda, orijinal işlev/yöntem), her şey iyi çalışıyor.

Şimdi, iç içe durumda:

@MyProperty 
@MyClassMethod 
def prop(self): 
    ... 

eşdeğerdir:

def prop(self): 
    ... 
prop = MyClassMethod(prop) # prop is now instance of MyClassMethod 
prop = MyProperty(prop)  # prop is now instance of MyProperty 
          # (with fget == MyClassMethod instance) 

Şimdi daha önce olduğu gibi, A.prop erişen aslında (MyProperty olarak) A.prop.__get__() arayacak sonra çağrı çalışırMyClassMethod örneği (fget özniteliğinde dekore edilmiş ve saklanan nesne).

MyClassMethod, __call__ yöntemi tanımlı değildir, dolayısıyla MyClassMethod is not callable hatasını edinirsiniz.


Ve ikinci soru adres: Bir özellik zaten sınıf niteliktir - sizin örnekte, sınıf nesnesi özelliğinin değeri döndürecektir A.prop erişip A().prop bir in özelliğinin değeri döndürecektir örnek nesnesi (örnek, bunu geçersiz kılmadıysa, sınıf nesnesi ile aynı olabilir). onun sarılı nesneye açıklayıcısı protokolünü uygulamak MyProperty yaparsanız

+0

Açıksa açıklamanız. Teşekkür ederim. İkinci cevaba göre: Ne demek istediğini anlayamadığımdan emin değilim. Ben "classmethod" ile paralel bir şey arıyorum, yani "A.prop" veya "A(). Prop" olarak adlandırabilirim, ve her iki durumda da temel işlevi bir "cls" arg iletilir. – shx2

+0

Bu durumda, 'obj' argümanının türünü kontrol edebilirsiniz: eğer bir sınıf örneğiyse, sınıfı ondan ayıklayabilir ve bunu örnek nesnesinin yerine dekore edilmiş işleve iletebilirsiniz. – isedev

+0

Noktayı kaçırıyorsunuz ... Benim sorum bir 'classproperty' dekoratörünün nasıl uygulanacağıyla ilgili değil. İç içe geçmiş tanımlayıcılarla ilgili. Önerdiğiniz mantık zaten classmethod'da uygulanmaktadır. İçinde mantığı yeniden uygulamak yerine, 'property' ve' classmethod''u yeniden kullanmak/birleştirmek için temiz bir yol olması gerektiğini umuyorum. Ama ben orada düşünmüyorum ... – shx2

0

eton'un büyüleyici blog. Kısacası

, onların __get__() arayarak doğrudan sarılmış fonksiyon/nesneyi aramaya çalışarak, tanımlayıcılar protokolü onurlandırmak yerine ilk onlara "açıklayıcısı sihirli" gerçekleştirmek için bir şans vermek yok yazdım dekoratörler (ilk).