Подтвердить что ты не робот

Наследование от 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__? почему?

4b9b3361

Ответ 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__ в принципе существует, чтобы разрешить наследование от неизменяемых типов.