наследование от str или int - PullRequest
45 голосов
/ 20 апреля 2010

Почему у меня проблемы с созданием класса, наследуемого от 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__? почему?

Ответы [ 5 ]

67 голосов
/ 20 апреля 2010
>>> 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'
11 голосов
/ 20 апреля 2010

Наследование встроенных типов очень редко стоит. Вам приходится иметь дело с несколькими вопросами, и вы не получаете особой выгоды.

Почти всегда лучше использовать композицию. Вместо того, чтобы наследовать str, вы бы сохранили объект str в качестве атрибута.

class EnhancedString(object):
     def __init__(self, *args, **kwargs):
         self.s = str(*args, **kwargs)

вы можете отложить любые методы, которые вы хотите работать с базовым str self.s, вручную или автоматически, используя __getattr__.

Это, как говорится, необходимость вашего собственного строкового типа - это то, что должно дать вам паузу. Есть много классов, которые должны хранить строку как свои основные данные, но вы обычно хотите использовать str или unicode (последний, если вы представляете текст) для общего представления строк. (Одним из распространенных исключений является необходимость использования строкового типа набора инструментов пользовательского интерфейса.) Если вы хотите добавить функциональность в свои строки, попробуйте, если можете, использовать функции, которые работают со строками вместо новые объекты, служащие строками , что делает ваш код проще и более совместимым со всеми остальными программами.

8 голосов
/ 28 декабря 2013

Когда вы создаете экземпляр класса, передаваемые вами аргументы передаются как __new__ (конструктор), так и затем __init__ (инициализатор) методам класса. Поэтому, если вы наследуете от класса, который имеет ограничения на число аргументов, которые могут быть предоставлены во время создания экземпляра, вы должны гарантировать, что ни его __new__, ни его __init__ не получат больше аргументов, чем они ожидают получить. Так что это проблема, которая у вас есть. Вы создаете экземпляр своего класса с C("a", "B"). Интерпретатор ищет метод __new__ в C. У C его нет, поэтому python заглядывает в свой базовый класс str. И так как он есть, этот используется и предоставляется с обоими аргументами. Но str.__new__ ожидает получить только один аргумент (кроме объекта класса в качестве первого аргумента). Итак, TypeError поднято. Вот почему вы должны расширить его в своем дочернем классе аналогично тому, что вы делаете с __init__. Но имейте в виду, что он должен возвращать экземпляр класса и что это статический метод (независимо от того, определен ли он с помощью @staticmethod декоратора или нет), который считается, если вы используете функцию super.

5 голосов
/ 20 апреля 2010

Используйте __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

печать возвращает hello.

Строки Python являются неизменяемыми типами. Функция __new__ вызывается для создания нового экземпляра объекта C. python __new__ функция в основном существует, чтобы позволить наследование от неизменяемых типов.

4 голосов
/ 14 февраля 2014

После тщательного прочтения этого , вот еще одна попытка создания подкласса 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 на странице индекса администратора.

...