2016-07-08 7 views
3

Я хотел бы создать класс Python, который внешне представляется подклассом другого класса, но на самом деле не наследует его атрибуты.Подкласс Python, который не наследует атрибуты

Например, если мой класс называется B, я хотел бы isinstance(B(), A) вернуться True, а также issubclass(B, A), но я не хочу B иметь атрибуты, определенные для А. Возможно ли это?

Примечание: Я не контролирую реализацию A.

Почему я забочусь: Модуль, в котором я работаю, проверяет, что переданный объект является подклассом A. Я хочу определить необходимые атрибуты в B, не наследуя лишние атрибуты, определенные в A (реализация которых я не контролирую), потому что я использую __getattr__ для передачи некоторых вызовов атрибутов на обернутый класс, и если эти атрибуты определяются наследованием от A, __getattr__ не будет вызываться.

+1

Возможно, вы захотите ознакомиться с абстрактными базовыми классами. Кроме того, вы нацеливаете python 2 или 3? – MisterMiyagi

+0

Ориентация на Python 3. –

+0

Btw. это оказалось проблемой [XY] (http://meta.stackexchange.com/a/66378/141542). Вы должны не спрашивать конкретно о своей единственной идее, чтобы решить вашу первоначальную проблему и вместо этого указать на свою оригинальную проблему. – poke

ответ

2

В Python3 переопределить специальный метод __getattribute__. Это дает вам почти полный контроль над поиском атрибутов. Есть несколько угловых случаев, поэтому внимательно проверяйте документы (это раздел 3.3.2 Справочного руководства по языку).

+0

Moreso, чем '__getattr__'? iirc, '__getattr__' не будет вызываться, если данный атрибут уже определен –

+0

' __getattribute__', в отличие от '__getattr__', вызывается для всех определенных и неопределенных атрибутов –

+1

@ LukeTaylor См. [этот вопрос] (http: // stackoverflow .com/q/3278077/216074), '__getattribute__' - это хардкорный способ остановить доступ к членам. При этом это не очень хорошее решение, так как вам нужно отказаться от каждого члена индивидуально (а также предотвратить подтипы от фактического переопределения этих членов). – poke

0

Пока вы определяете атрибуты в методе __init__ и переопределить этот метод, B не будет работать код из A «s __init__ и, таким образом, не определяют атрибуты и др. Методы удаления будут сложнее, но выходят за рамки вопроса.

+0

'A' кажется тонкой оболочкой вокруг C-реализованного класса, поэтому переопределение' __init__' ничего не делает. –

3

Использование abstract base classes сделать semingly неродственного класса B подкласса A не наследуя от него:

from abc import ABCMeta 
class A (metaclass=ABCMeta): 
    def foo (self): 
     print('foo') 

class B: 
    def bar (self): 
     print('bar') 

A.register(B) 

Затем используется, он дает желаемые результаты и выглядит как подтип, фактически не имея какой-либо из базового типа Участники:

>>> issubclass(B, A) 
True 
>>> b = B() 
>>> isinstance(b, A) 
True 
>>> b.foo() 
Traceback (most recent call last): 
    File "<pyshell#16>", line 1, in <module> 
    b.foo() 
AttributeError: 'B' object has no attribute 'foo' 

Я использую __getattr__ передать некоторые вызовы атрибутов на завернутый класс, и если эти атрибуты определяются наследованием от A, __getattr__ не будет вызываться.

__getattr__ не вызывается для членов, которые найдены с использованием нормального разрешения. Вместо этого вы можете использовать __getattribute__.

Однако, если то, что вы делаете, это перезапись поведение базового класса A, то я не понимаю, почему просто переписав методы не вариант:

class A: 
    def doSomething (self); 
     print('Do not do this!') 

class B: 
    def __init__ (self, wrapper): 
     self.wrapper = wrapper 

    def doSomething (self): 
     print('Doing something else instead!') 
     self.wrapper.doSomething() 
+1

A уже определен в библиотеке, которую я не контролирую. Спасибо, хотя –

2

Вы могли бы реализовать __getattribute__ поднять AttributeErrors для атрибутов, не входящих в B:

class A(object): 
    def __init__(self): 
     self.foo = 1 

    def bar(self): 
     pass 

class B(A): 
    def __init__(self): 
     self.baz = 42 

    def __getattribute__(self, attr): 
     if attr in ('foo', 'bar'): 
      raise AttributeError() 
     return super(B, self).__getattribute__(attr) 

Мне любопытно, почему вы это делаете?

+0

Я работаю с библиотекой, которая проверяет, является ли что-то подклассом, прежде чем он что-либо сделает с ней, поэтому мне нужно обмануть это, думая, что что-то со всеми необходимыми методами (но не с лишними) является подкласс, когда это не так. –

+0

@ LukeTaylor В этом случае, почему вас волнует, действительно ли эти оригинальные участники присутствуют или нет? Если вы уверены, что они не используются (иначе вы не могли надежно ожидать, что он будет работать без этих участников), тогда они, вероятно, не повредят никому, кроме * * там. – poke

+0

@poke Потому что он _uses_ их, но я реализую '__getattr__', чтобы читать их из обернутого класса. Если методы определены, '__getattr__' не вызывается. –

1

Я надеюсь, что это удовлетворяет вас (я думаю, что это немного грязный хак):

class A: 
    attribute = "Hello" 
    pass 

class B(A): 
    def __getattribute__(self, name): 
     if name == "__dict__": 
      return super().__getattribute__(name) 
     if name in type(self).__dict__: 
      return type(self).__dict__[name] 
     if name in self.__dict__: 
      return self.__dict__[name] 
     raise AttributeError("type object '{}' has no attribute '{}'".format(type(self).__name__, name)) 

Теперь давайте проверим это:

>>> a = A() 
>>> a.attribute 
'Hello' 
>>> b = B() 
>>> b.attribute 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "file.py", line 13, in __getattribute__ 
    raise AttributeError("type object '{}' has no attribute '{}'".format(type(self).__name__, name)) 
AttributeError: type object 'B' has no attribute 'attribute' 

К сожалению класс B сам унаследовал атрибуты так это происходит:

>>> B.attribute 
'Hello' 

I надеюсь, что это не имеет значения, если вам нужно использовать метаклассы (которые могут стать довольно неприятными).

+0

Подождите, как это поможет? Ваша реализация '__getattribute__' не похожа на то, что она меняет что-либо по сравнению с поведением по умолчанию ... –

+0

@ LukeTaylor Пол Корнелиус уже ответил, как это работает, я просто привел пример, как это сделать (не очень практично, но хорошо, например). – knowledge

+0

Хорошо. Большое спасибо ;) –

Смежные вопросы