Подавление обработки строки как iterable

ОБНОВИТЬ:

В 2006 году на python.org была предложена идея создания встроенных строк без повторения. Мой вопрос отличается тем, что я пытаюсь только подавлять эти функции время от времени; все же вся эта тема весьма актуальна.

Вот критические замечания Гвидо, которые внедрили неистребимую страницу на пробной основе:

[…] Я реализовал это (это было действительно просто сделать), но затем я обнаружил, что мне нужно было исправить множество мест, которые перебирают строки. Например:

  • Sre parser и compiler используют такие вещи, как set ("0123456789"), а также перебирают символы входного regexp для его синтаксического анализа.

  • У difflib есть API, определенный для двух списков строк (типичный линейный разброс файла) или двух строк (типичный внутрилинейный diff) или даже двух списков чего угодно (для обобщенной последовательности diff) ,

  • небольшие изменения в optparse.py, textwrap.py, string.py.

И я даже не знаю, как работает среда regrtest.py (из-за проблемы с difflib).

Я отказываюсь от этого проекта; патч – SF patch 1471291. Я больше не сторонник этой идеи; это просто непрактично, и предпосылка о том, что есть несколько веских причин для итерации по строке, была опровергнута случаями использования, которые я нашел как в sre, так и в difflib.

ОРИГИНАЛЬНЫЙ ВОПРОС:

Хотя это четкая особенность языка, что строка является итерируемой, в сочетании с утиным набором текста, это может привести к катастрофе:

 # record has to support [] operation to set/retrieve values # fields has to be an iterable that contains the fields to be set def set_fields(record, fields, value): for f in fields: record[f] = value set_fields(weapon1, ('Name', 'ShortName'), 'Dagger') set_fields(weapon2, ('Name',), 'Katana') set_fields(weapon3, 'Name', 'Wand') # I was tired and forgot to put parentheses 

Никакое исключение не будет поднято, и нет простого способа поймать его, кроме как путем тестирования на isinstance(fields, str) в бесчисленных местах. В некоторых случаях эта ошибка займет очень много времени.

Я хочу отключить строки, которые будут обрабатываться целиком в моем проекте. Это хорошая идея? Можно ли это сделать легко и безопасно?

Возможно, я мог бы подклассировать встроенную str , что мне нужно было бы явно вызвать get_iter() если бы я хотел, чтобы его объект рассматривался как итерируемый. Тогда, когда мне нужен строковый литерал, я бы вместо этого создал объект этого класса.

Вот некоторые касательные вопросы:

Как определить, является ли переменная python строкой или списком?

как указать переменную, является итерируемой, но не строкой

5 Solutions collect form web for “Подавление обработки строки как iterable”

К сожалению, нет никаких способов сделать это автоматически. Решение, которое вы предлагаете (класс подкласса str который не является итерируемым), страдает от той же проблемы, что isinstance() … а именно, вы должны помнить, что используете его везде, где используете строку, потому что нет возможности использовать Python вместо родного класса. И, конечно же, вы не можете обезвредить встроенные объекты.

Я мог бы предположить, что если вы обнаружите, что пишете функцию, которая принимает либо итерируемый контейнер, либо строку, возможно, что-то не так с вашим дизайном. Однако вы не можете этого избежать.

На мой взгляд, наименее навязчивой задачей является поставить чек в функцию и назвать это, когда вы попадаете в цикл. Это, по крайней мере, приводит к изменению поведения, когда вы, скорее всего, его увидите: в инструкции for , не скрытой где-то в классе.

 def iterate_no_strings(item): if issubclass(item, str): # issubclass(item, basestring) for Py 2.x return iter([item]) else: return iter(item) for thing in iterate_no_strings(things): # do something... 

Чтобы расширить и получить ответ:

Нет, ты не должен этого делать.

  1. Он изменяет функциональность, которую люди ожидают от строк.
  2. Это означает дополнительные накладные расходы во всей вашей программе.
  3. Это во многом не нужно.
  4. Типы проверок очень несовместимы.

Вы можете это сделать, и методы, которые вы указали, вероятно, являются лучшими способами ( для записи я считаю, что подкласс является лучшим вариантом. Если вам нужно это сделать, см. Метод @ kindall), но это просто не стоит делать, и это не очень pythonic. Избегайте ошибок в первую очередь. В вашем примере вы можете спросить себя, является ли это более важной проблемой с ясностью в ваших аргументах и ​​могут ли более эффективные решения или аргументы splat.

Например: измените порядок.

 def set_fields(record, value, *fields): for f in fields: record[f] = value set_fields(weapon1, 'Dagger', *('Name', 'ShortName')) #If you had a tuple you wanted to use. set_fields(weapon2, 'Katana', 'Name') set_fields(weapon3, 'Wand', 'Name') 

Например: Именованные аргументы.

 def set_fields(record, fields, value): for f in fields: record[f] = value set_fields(record=weapon1, fields=('Name', 'ShortName'), value='Dagger') set_fields(record=weapon2, fields=('Name'), value='Katana') set_fields(record=weapon3, fields='Name', value='Wand') #I find this easier to spot. 

Если вы действительно хотите, чтобы порядок был таким же, но не думайте, что идея с именованными аргументами достаточно ясна, то как сделать каждую запись диктоподобным элементом вместо dict (если это еще не так) и иметь:

 class Record: ... def set_fields(self, *fields, value): for f in fileds: self[f] = value weapon1.set_fields("Name", "ShortName", value="Dagger") 

Единственная проблема здесь – это введенный класс и тот факт, что параметр значения должен выполняться с ключевым словом, хотя он сохраняет четкость.

Кроме того, если вы используете Python 3, у вас всегда есть возможность использовать расширенную распаковку кортежей:

 def set_fields(*args): record, *fields, value = args for f in fields: record[f] = value set_fields(weapon1, 'Name', 'ShortName', 'Dagger') set_fields(weapon2, 'Name', 'Katana') set_fields(weapon3, 'Name', 'Wand') 

Или, для моего последнего примера:

 class Record: ... def set_fields(self, *args): *fields, value = args for f in fileds: self[f] = value weapon1.set_fields("Name", "ShortName", "Dagger") 

Однако при чтении вызовов функций это оставляет некоторые странности из-за того, что обычно предполагается, что аргументы не будут обрабатываться таким образом.

Проверка типов в этом случае не является беспризорной или плохой. Просто выполните:

 if isinstance(var, (str, bytes)): var = [var] 

В начале вызова. Или, если вы хотите обучить вызывающего:

 if isinstance(var, (str, bytes)): raise TypeError("Var should be an iterable, not str or bytes") 

Что вы думаете о создании строки без итераций?

 class non_iter_str(str): def __iter__(self): yield self >>> my_str = non_iter_str('stackoverflow') >>> my_str 'stackoverflow' >>> my_str[5:] 'overflow' >>> for s in my_str: ... print s ... stackoverflow 

Вместо того чтобы пытаться сделать ваши строки неистребимыми, переключите способ поиска проблемы: один из ваших параметров – либо итерабельный, либо …

  • строка
  • ИНТ
  • пользовательский класс
  • и т.п.

Когда вы пишете свою функцию, первое, что вы делаете, – это проверить ваши параметры, не так ли?

 def set_fields(record, fields, value): if isinstance(fields, str): fields = (fields, ) # tuple-ize it! for f in fields: record[f] = value 

Это послужит вам хорошо, поскольку вы имеете дело с другими функциями и параметрами, которые могут быть как сингулярными, так и множественными.

Interesting Posts

Невозможно записать файл (setuptools) в журналах

как я могу использовать pip с pypy, установленным с панели запуска?

Не удается установить Scipy through pip

Как использовать скрипинг с подключением к Интернету через прокси с аутентификацией

ImportError: невозможно импортировать имя HTTPSHandler с помощью PIP

ткань против pexpect

Как остановить процесс работы сельдерея

Как установить размер буфера отправки для сокетов в python

Python Scrapy – фильтр на основе mimetype, чтобы избежать загрузки нетекстовых файлов

Как я могу напечатать только угаданную букву из слова в их соответствующих указаниях?

urrlib2.urlopen: «Имя или услуга не известна» сохраняется при запуске скрипта без подключения к Интернету

эффективно проверяя, что строка состоит из одного символа в Python

Есть ли способ создать отдельный дисплей и вход на том же терминале с использованием проклятия?

Как сделать CSV-файл из нескольких списков?

Как изменить строку на Unicode в Python 2?

Python - лучший язык программирования в мире.