Если я правильно понимаю ваши требования, некоторые из специальных "символов" являются двухсимвольными строками (в частности: "&&" и "||"). Лучший способ создать такую странную коллекцию - использовать регулярное выражение. Вы можете использовать класс символов, чтобы сопоставить что-нибудь, длиной в один символ, а затем использовать вертикальные черты для разделения некоторых альтернативных шаблонов, и они могут быть многосимвольными. Самая хитрая часть - это экранирование символов от обратной косой черты; например, чтобы соответствовать "||" вам нужно поставить г '\ | \ |' потому что вертикальная черта является особой в регулярном выражении. В классе символов обратная косая черта является особенной, как и '-' и ']'. Код:
import re
_s_pat = r'([\\+\-!(){}[\]^"~*?:]|&&|\|\|)'
_pat = re.compile(_s_pat)
def escape_query(query):
return re.sub(_pat, r'\\\1', query)
Я подозреваю, что вышеизложенное - самое быстрое решение вашей проблемы в Python, поскольку оно сводит работу к механизму регулярных выражений, который написан на C.
Если вам не нравится регулярное выражение, вы можете упростить его просмотр, используя подробный формат, и скомпилировать, используя флаг re.VERBOSE
. Затем вы можете растягивать регулярные выражения на несколько строк и размещать комментарии после любых частей, которые вы считаете запутанными.
Или вы можете создать свой список специальных символов, как вы уже сделали, и запустить его с помощью этой функции, которая автоматически скомпилирует шаблон регулярного выражения, который соответствует любой альтернативе в списке. Я удостоверился, что это ничего не будет соответствовать, если список пуст.
import re
def make_pattern(lst_alternatives):
if lst_alternatives:
temp = '|'.join(re.escape(s) for s in lst_alternatives)
s_pat = '(' + temp + ')'
else:
s_pat = '$^' # a pattern that will never match anything
return re.compile(s_pat)
Кстати, я рекомендую вам поместить строку и предварительно скомпилированный шаблон вне функции, как я показал выше. В вашем коде Python будет запускать код при каждом вызове функции, чтобы построить список и связать его с именем special_chars
.
Если вы не хотите помещать в пространство имен ничего, кроме функции, вот способ сделать это без каких-либо накладных расходов во время выполнения:
import re
def escape_query(query):
return re.sub(escape_query.pat, r'\\\1', query)
escape_query.pat = re.compile(r'([\\+\-!(){}[\]^"~*?:]|&&|\|\|)')
Вышеприведенное имя использует имя функции для поиска атрибута, который не будет работать, если вы перепривязаете имя функции позже. Здесь обсуждается это и хорошее решение: как функция python может получить доступ к своим собственным атрибутам?
(Примечание: вышеприведенный абзац заменяет некоторые вещи, включая вопрос, который обсуждался в комментариях к обсуждению ниже.)
На самом деле, после дальнейших размышлений, я думаю, что это чище и более Pythonic:
import re
_pat = re.compile(r'([\\+\-!(){}[\]^"~*?:]|&&|\|\|)')
def escape_query(query, pat=_pat):
return re.sub(pat, r'\\\1', query)
del(_pat) # not required but you can do it
Во время компиляции escape_query()
объект, связанный с именем _pat
, будет связан с именем внутри пространства имен функции (это имя pat
). Затем вы можете позвонить del()
, чтобы отменить привязку имени _pat
, если хотите. Это хорошо инкапсулирует шаблон внутри функции, совершенно не зависит от имени функции и позволяет передавать альтернативный шаблон, если вы хотите.
P.S. Если бы ваши специальные символы всегда были длиной в один символ, я использовал бы следующий код:
_special = set(['[', ']', '\\', '+']) # add other characters as desired, but only single chars
def escape_query(query):
return ''.join('\\' + ch if (ch in _special) else ch for ch in query)