2011-07-14 23 views
9

kod sadece temel yapısı aşağıdaki gibidir:Çıkarma spesifik yöntemler

class FooType(type): 
    def __new__(cls, name, bases, classdict): 
     instance = type.__new__(cls, name, bases, classdict) 
     # What can I do here? 
     return instance 

class FooBase(object, metaclass=FooType): 
    def __init__(self): 
     pass 

class Foo(FooBase): 
    def __init__(self, name): 
     self.name = name 

    def method1(self): 
     pass 

    def method2(self): 
     pass 

    def specialmethod(self): 
     pass 

class A(Foo): 
    pass 

class B(Foo): 
    pass 

class C(Foo): 
    _disallowed_methods = ['specialmethod'] 

Ne yapmak istiyorum, sınıfın C örnekleri specialmethod sahip olmaması gerektiğini ancak Bu yöntem, A ve B örneklerine açık olmalıdır.

Bu yöntemi C sınıfında geçersiz kılabilir ve bir hata oluşturabilirim, ancak bunu yapmamayı tercih ederim.

ben instancedir(instance) çıkışında bunlardan herhangi varsa FooType yılında _disallowed_methods için ve o çek temelinde kontrol etmek kodunda ekleyebilir biliyoruz. Ancak, şu ana kadar denedim herhangi bir yöntem kullanarak yöntemi __dict__C10'dan kaldırmış gibi görünmüyorum. Denediğim yöntemler delattr(instance, 'specialmethod') ve del instance.__dict__['specialmethod']'dur. içinde

delattr yöntem sonuçları "AttributeError: specialmethod" ve içinde del yöntemi sonuçları "TypeError: 'dict_proxy' nesne öğesi silme desteklemiyor"

Temelde birçok farklı sınıflar Foo devralan, ancak bazı Onlara, C gibi, specialmethod kullanılabilir olmayan özel yöntemler bulunmamalıdır.

Neyi yanlış yapıyorum? Ya da bunu başka nasıl yapabilirim?

+2

BTW: Bu [Liskov İkame Prensibi] 'ni (http://en.wikipedia.org/wiki/Liskov_substitution_principle) ihlal eder. – pillmuncher

cevap

1

Or how else can I accomplish this?

multiple inheritance kullanarak benzer sonuçlar elde edebilirsiniz.

Yalnızca bazı çocukların Foo'dan ExtraFoo'a sahip olmasını istediğiniz yöntemleri taşıyın. Sonra class A(Foo, ExtraFoo) veya class C(Foo) kullanın. Bu şekilde, verilen bir yöntemi daha sonra çocuk hiyerarşisine doğru "yeniden atayabilirsiniz". yöntemi reattaching size ilgilenen bir şey değilse

, o zaman sadece Foo bir çocuk olarak ExtraFoo olabilir (bu yüzden: ekleme yöntemleri ayırarak çıkarın değil) class A(ExtraFoo) ve class C(Foo) var.

+0

Bunu, "Foo" ve "FooExtra" kullanarak ekstra sınıf ve alt sınıf oluşturarak, "özel" ve diğerlerine "Foo" gereksinimi olan sınıflara ulaşabileceğimi fark ettim. Metodun metaclass kullanılarak oluşturulduğu zaman belki de dict yöntemini kaldırabileceğimi düşündüm. Sanırım yanılıyordum ... – skulled

+1

@skulled - Python çok esnektir ve bir şekilde internals ile oynayabilmek için iyi bir şans var. Yine de en önemli nokta, kötü tasarım olması. OOP ilkelerinden biri, bir alt sınıfın ebeveynden "miras alması", yani bir alt sınıfı "disinherit" in bir uçağın kanatları üzerine inşa edilmesine benzemesidir. :) BTW: yararlı bulduğunuz soruları yanıtlamalısınız ve - eğer sorununuzu çözdüyseniz - bunu da "kabul" olarak işaretleyebilirsiniz (Sadece sitede yeni olduğunuzu fark ettiğim gibi, zaten biliyor olsanız beni affet)) o. – mac

+0

Teşekkürler! Yanıtların şüphelerimi doğruladığında, belki de yanlış yaklaşımı üstlendiğimi fark ettim. – skulled

6

C sınıfını değil,gerçekten içeren Foo sınıfını değiştirmeniz gerektiğinden, bunu böyle yapamazsınız. Ama aslında class global değişebilen nesne olduğundan ve Foo'daki tüm değişiklikler tüm alt sınıfları etkileyeceğinden bunu yapamazsınız.

Başka şekilde düşünmeye çalışın. Örneğin. Bu C('a').specialmethod() sonra

class C(Foo): 
    def __getattribute__(self, name): 
     if name in ['specialmethod']: 
      raise AttributeError('no such method') 
     return super(C, self).__getattribute__(name) 

bir Traceback üretir: Eğer C sınıfın erişen niteliklerin mantığını değiştirebilir

Traceback (most recent call last): 
    File "meta.py", line 37, in <module> 
    C('a').specialmethod() 
    File "meta.py", line 34, in __getattribute__ 
    raise AttributeError('no such method') 
AttributeError: no such method 
+0

Bunu denedim (ilk gönderiimde bahsetmediğim için özür dilerim) ve bu rotayı son çare olarak planlamayı planlıyorum. Anlayışımdan bu yaklaşım, 'C' sınıfı örneklerinin bir özniteliğinin kullanıldığı her zaman, bu yöntemden geçtiği ve bu nedenle benim endişemimin hız/performans üzerindeki etkisinin olduğu anlamına gelir. Bu konuda endişelenmem gerekecek kadar ihmal edilebilir mi? – skulled

+0

@skulled Evet, performansı etkileyebilir. Ama senin durumunda önemli olacağını inanamıyorum. Eğer bunun için endişeleniyorsanız sadece 'timeit'. –

0

Eğer değiştirilecek istemeyen bir ebeveyn varsa ve Bir veya daha fazla kalıtsal yöntemle çocuğunuz erişilemez olmak istiyorsanız, bunu tanımlayıcılarla yapabilirsiniz.En basit yaklaşım biri kullanmaktır property yerleşik:

class Parent: 
    def good_method(self): 
     print('Good one') 

    def bad_method(self): 
     print('Bad one') 

class Child(Parent): 
    bad_method = property(doc='(!) Disallowed inherited') 

one = Parent() 
one.good_method() # > 'Good one' 
one.bad_method() # > 'Bad one' 

two = Child() 
two.good_method() # > 'Good one' 
two.bad_method() # > AttributeError: unreadable attribute 
two.bad_method  # > AttributeError: unreadable attribute 
two.bad_method = 'Test' # > AttributeError: can't set attribute 

yardımı (iki) basar Nasıl:

class Child(Parent) 
| Method resolution order: 
|  Child 
|  Parent 
|  builtins.object 
| 
| Data descriptors defined here: 
| 
| bad_method 
|  (!) Disallowed inherited 
| 
| ---------------------------------------------------------------------- 
| Methods inherited from Parent: 
| 
| good_method(self) 
| 
| ---------------------------------------------------------------------- 
| Data descriptors inherited from Parent: 
| 
| __dict__ 
|  dictionary for instance variables (if defined) 
| 
| __weakref__ 
|  list of weak references to the object (if defined) 

Gayet iyi, bence. Ancak, diğer yöntemler bunlara güvenirse, bu şekilde miras alınan yöntemleri tanımlamamaya dikkat etmelisiniz (bu, ebeveynden miras alınan vekil sınıfı kullanılarak önlenebilir ve super().bad_method()'un super().bad_method() yerine super().bad_method() kullanılmasına izin vermeyebilir ve bad_method'un kendiliğinden bitmesini önleyebilir açıklayıcısı). Gerekirse daha karmaşık tanımlayıcı mantığını kodlayabilirsiniz

0

Aynı sorunlarla karşılaştım ve testlerle çalıştım.

Devralınan sınıftan 'gereksiz yöntemleri' kaldırmak için yalnızca bir tane doğru yol buldum: onu ana sayfasından kaldır. (Bu, tüm üst sınıfın tüm örneklerini ve bu işlev en az bir kez çağrılırsa, tüm kalıtsal sınıfların tüm örneklerini keseceği için kötü bir fikirdir).

örnek kod:

class Base(object): 
    def excessive(self): 
     pass 

class Inher(Base): 
    def __init__(self): 
     #import pdb 
     #pdb.set_trace() 
     del Base.excessive 

b=Base() 
example = Inher() 
try: 
    example.excessive() 
except AttributeError: 
    print("Removed!") 
else: 
    raise Exception("Not removed!") 
try: 
    b.excessive() 
except AttributeError: 
    print("Unfortunately, all instances of Base no longer have .excessive() method") 

sebebi, kalıtsal 'yöntemleri (kod veya bağlantılar gibi) ana saklanmaz ama ana içinde tutulmasını olmasıdır. Birisi bir yöntemi çağırdığında, python bir tane buluncaya veya duruncaya kadar tüm üst sınıflar arasında dolaşır.

Benim durumumda bu tekniği kullanabildim çünkü amacım için diğer erkek testlerini kullandım ve 'setUp/tearDown' ve aksiller yöntemler kullanıyorum ama tüm testlerini kaldırdım.

Herhangi bir gerçek yaşam uygulaması bu tekniği kullanmamalıdır.