Наследование от str или int
Почему у меня проблема с созданием класса, наследующего от str (или также от int)
class C(str):
def __init__(self, a, b):
str.__init__(self,a)
self.b = b
C("a", "B")
TypeError: str() takes at most 1 argument (2 given)
То же самое происходит, если я пытаюсь использовать int
вместо str
, но он работает с пользовательскими классами. Мне нужно использовать __new__
вместо __init__
? почему?
Ответы
Ответ 1
>>> class C(str):
... def __new__(cls, *args, **kw):
... return str.__new__(cls, *args, **kw)
...
>>> c = C("hello world")
>>> type(c)
<class '__main__.C'>
>>> c.__class__.__mro__
(<class '__main__.C'>, <type 'str'>, <type 'basestring'>, <type 'object'>)
Поскольку __init__
вызывается после создания объекта, слишком поздно изменять значение для неизменяемых типов. Обратите внимание, что __new__
- метод класса, поэтому я назвал первый параметр cls
Смотрите здесь для получения дополнительной информации
>>> class C(str):
... def __new__(cls, value, meta):
... obj = str.__new__(cls, value)
... obj.meta = meta
... return obj
...
>>> c = C("hello world", "meta")
>>> c
'hello world'
>>> c.meta
'meta'
Ответ 2
Наследование встроенных типов очень редко стоит. Вам нужно иметь дело с несколькими проблемами, и вы не получаете большую пользу.
Почти всегда лучше использовать композицию. Вместо наследования str
вы должны сохранить объект str
как атрибут.
class EnhancedString(object):
def __init__(self, *args, **kwargs):
self.s = str(*args, **kwargs)
вы можете отложить любые методы, которые хотите работать с базовым str
self.s
вручную или автоматически, используя __getattr__
.
Говоря, что ваш собственный тип строки - это то, что должно дать вам паузу. Существует много классов, которые должны хранить строку в качестве основных данных, но обычно вы хотите использовать str
или unicode
(последний, если вы представляете текст) для общего представления строк. (Одно из распространенных исключений - если вам нужно использовать тип строки инструментария пользовательского интерфейса.) Если вы хотите добавить функциональность в свои строки, попробуйте, если вы можете использовать функции, которые работают со строками, а не с новыми объектами, чтобы служить в качестве строк, которые сохраняют ваш код проще и более совместим со всеми другими программами.
Ответ 3
Когда вы создаете экземпляр класса, аргументы, которые вы передаете, передаются как в __new__
(конструктор), так и в методы __init__
(initializer) класса. Поэтому, если вы наследуете класс, который имеет ограничения на количество аргументов, которые могут быть предоставлены во время создания экземпляра, вы должны гарантировать, что ни его __new__
, ни его __init__
не получат больше аргументов, чем они ожидают получить. Так вот в чем проблема. Вы создаете свой класс с помощью C("a", "B")
. Интерпретатор ищет метод __new__
в C
. C
не имеет его, поэтому python заглядывает в свой базовый класс str
. И поскольку он имеет один, этот используется и поставляется с обоими аргументами. Но str.__new__
ожидает получить только один аргумент (помимо его объекта класса в качестве первого аргумента). Итак, TypeError
поднят. Вот почему вы должны расширять его в своем дочернем классе аналогично тому, что вы делаете с __init__
. Но имейте в виду, что он должен возвращать экземпляр класса и что это статический метод (независимо от того, установлен ли он с помощью декоратора @staticmethod
или нет), который учитывается, если вы используете функцию super
.
Ответ 4
После тщательного чтения this, вот еще одна попытка подклассификации str. Изменение из других ответов создает экземпляр в правильном классе, используя super(TitleText, cls).__new__
. Кажется, что это ведет себя как str, когда оно используется, но позволило мне переопределить метод:
class TitleText(str):
title_text=""
def __new__(cls,content,title_text):
o=super(TitleText, cls).__new__(cls,content)
o.title_text = title_text
return o
def title(self):
return self.title_text
>>> a=TitleText('name','A nice name')
>>> a
'name'
>>> a[0]
'n'
>>> a[0:2]
'na'
>>> a.title()
'A nice name'
Это позволяет правильно нарезать и подписать. Для чего это? Для переименования приложения Django на странице индекса администратора.
Ответ 5
Используйте __new__
в случае неизменяемых типов:
class C(str):
def __new__(cls, content, b):
return str.__new__(cls, content)
def __str__(self):
return str.__str__(self)
a=C("hello", "world")
print a
print возвращает hello
.
Строки Python являются неизменяемыми типами. Функция __new__
вызывается для создания нового экземпляра объекта C
. Функция python __new__
в принципе существует, чтобы разрешить наследование от неизменяемых типов.