import collections collections.UserString(seq)
seq
- любой объект, который можно преобразовать в строку.Класс UserString()
модуля collections
имитирует строковый объект. Содержимое экземпляра хранится в обычном строковом объекте, который доступен через атрибут экземпляра UserString.data
. Содержимое экземпляра изначально устанавливается на копию seq
. Аргументом seq
может быть любой объект, который можно преобразовать в строку с помощью встроенной функции str()
.
Класс collections.UserString()
был введен в Python, когда было невозможно наследоваться от встроенного типа str
напрямую. Возможность прямого наследования от str
означает, что нужда в UserString отпадает. Но этот класс по-прежнему доступен в стандартной библиотеке как для удобства, так и для обратной совместимости.
Помимо поддержки методов и операций строк, экземпляры collections.UserString
предоставляют атрибут UserString.data
- это объект str
, который используется для хранения содержимого класса collections.UserString
и дает доступ к обернутому строковому объекту. Этот атрибут может облегчить создание настраиваемых строк, особенно в тех случаях, когда желаемая настройка влияет на изменяемость строки.
collections.UserString
.Необходимость создания пользовательского строкового класса возникает, когда нужно расширить стандартные строки Python новым поведением. Предположим, что нужен строковый класс, который реализует новый метод для подсчета количества слов в базовой строке.
>>> from collections import UserString >>> class WordCountString(UserString): ... def words(self, separator=None): ... return len(self.split(separator))
Поверх унаследованного интерфейса добавляется новый метод с именем WordCountString.words()
. Этот метод принимает символ-разделитель separator
в качестве аргумента, который передается в str.split()
. Его значение по умолчанию None
, которое делит строку на список слов по последовательным пробелам. Наконец, используется функция len()
для определения количества слов.
Вместо класса collections.UserString
аналогичный класс можно реализовать наследуясь от встроенного типа str
:
>>> class WordCountString(str): ... def words(self, separator=None): ... return len(self.split(separator)) >>> cnt = WordCountString("Привет мир!") >>> cnt # 2
Предположим, что нужен класс, который всегда ПЕЧАТАЕТ свои буквы в верхнем регистре. Такое поведение можно реализовать, переопределив специальный метод UserString.__str__()
, который заботится о том, как печатаются строковые объекты.
>>> from collections import UserString >>> class UpperString(UserString): ... def __str__(self): ... return self.upper()
Аналогичный класс, наследованный от встроенного типа str
:
>>> class UpperString(str): ... def __str__(self): ... return self.upper() >>> upper = UpperString("Привет!") >>> upper # 'ПРИВЕТ!'
UserString
.Предположим, что нужен строковый класс, в котором все буквы ХРАНЯТСЯ (а не печатаются) в верхнем регистре. Для реализации такого поведения можно настроить процесс инициализации экземпляра в методе __init__()
, не переопределяя создателя экземпляра __new__()
.
Новая версия UpperString
которая будет ХРАНИТЬ строку в верхнем регистре:
>>> from collections import UserString >>> class UpperString(UserString): ... def __init__(self, string): ... super().__init__(string.upper()) ... >>> upper = UpperString("Привет!") >>> upper # 'ПРИВЕТ!'
Так как объект UserString
является оберткой класса str
, он обеспечивает гибкий и простой способ создания пользовательских строк с изменяемым поведением.
При создании аналогичного пользовательского класса, который наследуется от встроенного типа str
, нельзя просто взять и изменить значение в __init__()
. Это связано с тем, что во первых - тип str
неизменяем, а во вторых - значение устанавливается во время СОЗДАНИЯ ОБЪЕКТА, а не во время его инициализации. Единственный способ преобразовать значение переданной строки - в процессе создания экземпляра, т.е. переопределить реализацию магического метода __new__()
.
Версия UpperString
, использующая наследование от типа str
:
>>> class UpperString(str): ... def __new__(cls, string): ... instance = super().__new__(cls, string.upper()) ... return instance
collections.UserString
.Предположим, что нужен строковый класс, который можно изменять на месте в стиле списка Python. В отличие от списков list
и словарей dict
, для строк не предусмотрен специальный метод __setitem__()
, т.к. они неизменяемы. Этот метод понадобится для реализации пользовательского строкового класса, чтобы можно было обновлять символы и фрагменты по их индексам с помощью оператора присваивания.
Новому классу также необходимо изменить стандартное поведение обычных строковых методов. Чтобы пример был коротким, изменим только методы str.upper()
и str.lower()
, а также добавим метод .sort()
для сортировки строки на месте.
# файл mutable_string.py from collections import UserString class MutableString(UserString): def __setitem__(self, index, value): data_as_list = list(self.data) data_as_list[index] = value self.data = "".join(data_as_list) def __delitem__(self, index): data_as_list = list(self.data) del data_as_list[index] self.data = "".join(data_as_list) def upper(self): self.data = self.data.upper() def lower(self): self.data = self.data.lower() def sort(self, key=None, reverse=False): self.data = "".join(sorted(self.data, key=key, reverse=reverse))
Примечание:
self.data
- это тот самый атрибутUserString.data
, который дает доступ к обернутому строковому объекту (смотрите описание классаUserString
).Обратите внимание, что стандартные строковые методы не изменяют базовую строку. Они только возвращают новый строковый объект с необходимым преобразованием.
Описание работы методов пользовательского класса MutableString()
:
__setitem__()
вызывается при операции присваивания последовательности с использованием индекса, например, sequence[0] = value
. Эта реализация __setitem__()
превращает атрибут UserString.data
в список, заменяет элемент в индексе значением, создает окончательную строку с помощью [str.join()][str.join] и присваивает ее значение обратно
UserString.data`. Весь процесс имитирует трансформацию или мутацию на месте.__delitem__()
позволяет использовать оператор del
для удаления символов по индексу из изменяемой строки. Он реализован аналогично __setitem__()
. upper()
вызывает str.upper()
для атрибута UserString.data
. Затем он сохраняет результат обратно в UserString.data
. Опять же, эта операция имитирует мутацию на месте.lower()
использует ту же технику, что и для метода upper()
.sort()
объединяет встроенную функцию sorted()
с методом стоки str.join()
для создания отсортированной версии исходной строки. Обратите внимание, что этот метод имеет ту же сигнатуру, что и list.sort()
, и встроенная функция sorted()
.>>> line = MutableString("ABC def") >>> line # 'ABC def' >>> line[4] = "x" >>> line[5] = "y" >>> line[6] = "z" >>> line # 'ABC xyz' >>> del line[3] >>> line # 'ABCxyz' >>> line.upper() >>> line # 'ABCXYZ' >>> line.lower() >>> line # 'abcxyz' >>> line.sort(reverse=True) >>> line # 'zyxcba'