Я хочу создать список, который может принимать только определенные типы.Поэтому я пытаюсь наследовать от списка в Python
Не лучший подход!В списках Python есть так много методов мутации, что вам придется переопределять группу (и, вероятно, некоторые из них забудут).
Скорее, обернуть список, наследовать от collections.MutableSequence
, идобавьте свои проверки к очень немногим методам «дросселирования», на которые опирается MutableSequence
для реализации всех остальных.
import collections
class TypedList(collections.MutableSequence):
def __init__(self, oktypes, *args):
self.oktypes = oktypes
self.list = list()
self.extend(list(args))
def check(self, v):
if not isinstance(v, self.oktypes):
raise TypeError, v
def __len__(self): return len(self.list)
def __getitem__(self, i): return self.list[i]
def __delitem__(self, i): del self.list[i]
def __setitem__(self, i, v):
self.check(v)
self.list[i] = v
def insert(self, i, v):
self.check(v)
self.list.insert(i, v)
def __str__(self):
return str(self.list)
Аргумент oktypes
обычно представляет собой набор типов, которые вы хотите разрешить, ноКонечно, все в порядке, чтобы передать один тип (и, сделав этот тип абстрактным базовым классом, ABC, вы можете легко выполнить любой тип проверки типа по вашему выбору таким способом - но это другая проблема).
Вот пример кода, использующего этот класс:
x = TypedList((str, unicode), 'foo', 'bar')
x.append('zap')
print x
x.append(23)
вывод:
['foo', 'bar', 'zap']
Traceback (most recent call last):
File "tl.py", line 35, in <module>
x.append(23)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py", line 556, in append
self.insert(len(self), value)
File "tl.py", line 25, in insert
self.check(v)
File "tl.py", line 12, in check
raise TypeError, v
TypeError: 23
Обратите внимание, в частности, что not переопределеноappend
- но приложение append существует и ведет себя так, как и ожидалось.
Не секрет, что за этим чудом раскрывается в трассировке: _abcoll.py
(модуль реализации абстрактных базовых классов).в модуле collections
), встрока 556, реализует добавление путем вызова нашего insert
- который у нас есть , конечно, должным образом переопределен.
Этот "шаблон проектирования метода шаблона" (абсолютноДрагоценный для всех видов ООП - посмотрите мои выступления по шаблонам проектирования на YouTube, и вы поймете, почему ;-), помимо других преимуществ, дает нам «эффект удушья», о котором я упоминал ранее: добавив несколько проверок вочень мало методов, которые вы должны реализовать, вы получаете преимущество в том, что эти проверки применяются к _all__ другим соответствующим методам (и у изменяемых последовательностей в Python их много; -).
Не удивительно, что мы в конечном итогес очень мощным и классическим шаблоном дизайна «за кулисами», потому что вся идея этой стратегии реализации взята из бессмертной классической книги «Шаблоны проектирования» (авторов которой все вместе называют бандой четырех);): предпочитает композицию объекта наследованию .Наследование (от конкретных классов) - это очень жесткий связующий механизм, полный «ловушек», как только вы пытаетесь использовать его, чтобы сделать что-либо, даже слегка выходящее за его строгие пределы;композиция очень гибкая и полезная, и наследование от соответствующих абстрактных классов может очень хорошо дополнить картину.
Скотт Мейерс отлично «Эффективный C ++», item 33 ,выражает это еще сильнее: делает не листовые классы абстрактными .Поскольку под «неконечным» он подразумевает «любой класс, который когда-либо наследуется», эквивалентное выражение будет «никогда не наследуется от конкретного класса».
Скотт, конечно, пишет в контексте C ++, но Пол Хаар дает точно такой же совет для Java, сформулированный как Не делите подклассы на конкретные классы - и я обычно поддерживаю его для Python, хотя я действительно одобряю банду четырехболее мягкая формулировка, предпочитают композицию, а не наследование (конкретный класс) (но я понимаю, что и Скотт, и Пол часто пишут для аудитории, которая нуждается в очень прямом и строго сформулированном совете, почти сформулированном как "заповеди", а не совет, не более мягко сформулированный, который они могли бы слишком легко игнорировать во имя своего удобства; -).