2014-11-12 2 views
0

Возможно, это слишком общий вопрос, но это меня беспокоит. В общем, что лучше (и почему): проксирование или подклассов базового класса? К base Я имею в виду один из стандартных классов, который обычно даже не реализован в Python.Проксирование или подклассы базовых классов?

Более конкретный вопрос заключается в следующем: я хочу создать объект для графов графа; край графа - это по существу пара двух вершин. Вершиной может быть любой хешируемый объект. Я хочу, чтобы ребро выставило еще много методов, чем раскрывает frozenset. Так интересно, я должен

class Edge(frozenset): 
    def my_method(self, *args): 
     return 3 

или

class Edge(object): 
    def __init__(self, *args): 
     self._frozenset = frozenset(*args) 

    def __len__(self): 
     return len(self._frozenset) 

    def ... 

Польза первого метода заключается в том, что я должен написать меньше (так как я не должен дублировать все оригинальные методы). Второй способ выглядит, однако, более безопасным в некотором смысле. Он также более гибкий, поскольку он позволяет мне избежать некоторых методов, которые frozenset предоставляет (например, difference), если я желаю.

Первый метод также вводит проблему с количеством переданных аргументов. Я, вероятно, должен перезаписать frozenset.__new__, если я хочу это контролировать. С другой стороны, первый метод, вероятно, будет быстрее в целом, поскольку проксирование создает некоторые накладные расходы.

Я не знаю, имеет ли это значение, но я обычно пишу для Python 2.7.

ответ

1

Вопрос, который вы должны задать: my class is или my class has?, То есть типичное наследование вопроса против композиции. Другими словами, ваш Edge a frozenset или просто имеет его для внутреннего использования?

Подумайте об этом как о стороннем пользователе вашего класса. Вы получаете объект Edge. Вы посмотрите на его API. Что вы хотите увидеть там? Вероятно, методы, которые позволяют взаимодействовать с Edge логической областью, как, гмммы ... .do_what_edges_do(), а не вещи, как .union() или .isdisjoint(),

В этом случае ясно мне, что вы должны пойти на композицию. Когда вы сомневаетесь, отправляйтесь на композицию.

Однако, возможно, вы захотите разоблачить некоторые методы frozenset непосредственно в вашем API. У Python есть большие механизмы для этого, но вы должны отделяться между обычными y и магическими методами.

Нормальные методы:

объявите список методов, которые вы хотели бы выставить, а затем использовать __getattr__. Например, предположим, что вы хотите, чтобы разоблачить методов «ABC» и «CDE» (они не существуют в frozenset, это только для целей обучения)

class Edge(object): 
    .. 

    _exposed_methods = ['abc', 'cde'] 

    def __getattr__(self, item): 
    if item in self._exposed_methods: 
     return getattr(self, item) 

    raise AttributeError 

Магические методы:

Предыдущий механизм не работает для магических методов, Python пропускает его для производительности. В этом случае вы должны декларировать их явно.Например, полезно один __len__

class Edge(object): 
    .. 

    def __len__(self): 
    return len(self._my_set) 

EDIT ПОСЛЕ ЧТЕНИЯ КОММЕНТАРИИ

Вы должны сделать смысловое решение. Я не знаю, какие объекты должен содержать Edge, так скажем Item objs. Теперь вопрос в том, какой метод вы хотите предложить в API своего кода?

  • что-то вроде get_items? это подчеркивает концепцию Item. В этом случае совершенно правильно возвращать коллекцию как набор, заполненный Item s. Поскольку вы говорите, что хотите добавить больше методов в эту коллекцию, это, вероятно, не то, что вы хотите сделать.

  • или get_edge? Это придает большее значение концепции Edge. В этом случае вы должны вернуть экземпляр собственного класса Edge. Если все методы, выставленные frozenset, имеют смысл для вашей концепции Edge, идите для наследования. В противном случае идите на композицию и выставляйте только то, что имеет смысл (используйте описанный выше механизм).

+0

Спасибо. Однако для меня это не так понятно. Я считаю, что люди с более абстрактным мышлением хотели бы иметь возможность проверять такие вещи, как 'if edge1() & edge2()' (например, 'edge1' и' edge2' пересекаются - как множества). – Bach

+0

В любом случае, для меня Edge ** является ** неизменным набором. Тем не менее, существует довольно много суеты с подклассом «frozenset», поэтому я не уверен, что это правильная вещь. – Bach

+0

Ну, если это всего лишь набор, то ... используйте набор. Если не требуется никакого специального смысла или функциональности, зачем даже создавать собственный класс? – bgusach

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