Сообщить об ошибке.

Класс UserString() модуля collections в Python

Создание пользовательского класса строк str в Python

Синтаксис:

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'