2013-05-25 3 views
3

Есть ли способ для удалить атрибут из подкласса, который присутствует в родительском?Удалить атрибут из подкласса в Python

В следующем примере

class A(object): 
    foo = 1 
    bar = 2 

class B(A): 
    pass 

# <desired code here> 

b = B() 
assert hasattr(b, 'bar') == False 

Есть ли код, который мы можем написать, чтобы сделать утверждение пройти?

+5

Это нарушает принцип замещения Лискова. Другими словами, это ужасная идея, и B не должен быть подтипом A. – delnan

+0

Мне просто интересно: для чего вы хотите это сделать? – cyroxx

+0

@ delnan почему-то всякий раз, когда кто-нибудь упоминает, что я всегда думаю о [Ограничительном эффекте Блиновича] (http://en.m.wikipedia.org/wiki/Blinovitch_Limitation_Effect) от Doctor Who. –

ответ

4
class A(object): 
    foo = 1 
    bar = 2 


class B(A): 
    @property 
    def bar(self): 
     raise AttributeError 


>>> b = B() 
>>> b.bar 

Traceback (most recent call last): 
    File "<pyshell#17>", line 1, in <module> 
    b.bar 
    File "<pyshell#15>", line 4, in bar 
    raise AttributeError 
AttributeError 
+0

Одно очень незначительное отличие состоит в том, что 'bar' все еще присутствует в выводе' dir (b) ', хотя' 'bar 'в b .__ dict__' является ложным, и я не могу найти другой способ« доступа »' b .bar'. – chepner

0

Да, с использованием магии descriptors. См. Мой blog post об этом. Короткая версия:

class nosubclasses(object): 
    def __init__(self, f, cls): 
     self.f = f 
     self.cls = cls 
    def __get__(self, obj, type=None): 
     if type == self.cls: 
      if hasattr(self.f, '__get__'): 
       return self.f.__get__(obj, type) 
      return self.f 
     raise AttributeError 

Пример:

In [2]: class MyClass(object): 
    ...:  x = 1 
    ...: 

In [3]: MyClass.x = nosubclasses(MyClass.x, MyClass) 

In [4]: class MySubclass(MyClass): 
    ...:  pass 
    ...: 

In [5]: MyClass.x 
Out[5]: 1 

In [6]: MyClass().x 
Out[6]: 1 

In [80]: MySubclass.x 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-80-2b2f456dd101> in <module>() 
----> 1 MySubclass.x 

<ipython-input-51-7fe1b5063367> in __get__(self, obj, type) 
     8     return self.f.__get__(obj, type) 
     9    return self.f 
---> 10   raise AttributeError 

AttributeError: 

In [81]: MySubclass().x 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-81-93764eeb9948> in <module>() 
----> 1 MySubclass().x 

<ipython-input-51-7fe1b5063367> in __get__(self, obj, type) 
     8     return self.f.__get__(obj, type) 
     9    return self.f 
---> 10   raise AttributeError 

AttributeError: 

Но как комментатор @delnan отметило, это нарушает принцип заменимости Лиск. Мотивация в моем сообщении в блоге была оправдана, потому что атрибут не описал сам объект. Но в целом это полностью разрушает возможность подкласса в первую очередь, что на самом деле является целым рядом классов.

Кстати, разница между моим ответом и @ jamylak's заключается в том, что в ответе @ jamylak атрибуты удаляются по каждому подклассу. Если вы сделали class C(A), у него все равно будет атрибут bar. В моем ответе сам класс (ну, на самом деле атрибут) запрещает подклассы иметь атрибут, так что в одном махом все подклассы его не имеют.