Можно ли заменить Python 3+, если в и elif в Использование Switcher

Я знаю, как использовать словарь в качестве переключателя в Python. Я не уверен, как использовать один для моего конкретного случая. Я думаю, что мне просто нужно будет использовать if, elif, и еще, но, надеюсь, сообщество оказалось неправым:)

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

В основном я делаю следующее:

if non-breaking hyphen in string:  # string is a sentence with many words
  replace non-breaking hyphen with dash

elif en dash in string:
  replace en dash with dash

elif em dash in string:
  replace em dash with dash

elif non-breaking space in string:
  replace non-breaking space with space

.... и так далее

Единственное, о чем я могу думать, - это разбить строку на отдельные подстроки, а затем зациклить их, чтобы сработал переключатель словаря. Но это, очевидно, добавило бы много дополнительного времени обработки, и цель использования словарного переключателя - сэкономить время.

Я не мог найти что-либо на эту конкретную тему поиска везде.

Есть ли способ использовать переключатель в Python, используя if in и elif in?

3 ответа

Решение

Вот str.translate решение

replacements = {
    '\u2011': '-',  # non breaking hyphen
    '\u2013': '-',  # en dash
    '\u2014': '-',  # em dash
    '\u00A0': ' ',  # nbsp
}

trans = str.maketrans(replacements)
new_string = your_string.translate(trans)

Обратите внимание, что это работает, только если вы хотите заменить отдельные символы из ввода. {'a': 'bb'} является действительным replacements, но {'bb': 'a'} не является.

Просто чтобы показать, что регулярное выражение является допустимым решением, и некоторые моменты времени:

replacements = {
    '\u2011': '-',
    '\u2013': '-',
    '\u2014': '-',
    '\u00A0': ' ', 
}

import re
s = "1‑‑‑‑2–––––––3————————"

re.sub(
    '|'.join(re.escape(x) for x in replacements),
    lambda x: replacements[x.group()], s
)
# Result
1----2-------3--------

Сроки (str.trans побеждает и тоже чище)

s = "1‑‑‑‑2–––––––3————————"
s *= 10000

%timeit re.sub('|'.join(re.escape(x) for x in replacements), lambda x: replacements[x.group()], s)
90.7 ms ± 182 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [733]: %timeit s.translate(trans)
15.8 ms ± 59.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Хотя ответ Бенджамина может быть правильным, он зависит от конкретного случая, в то время как ваш вопрос имеет довольно общий тон. Существует универсальный функциональный подход (я добавил аннотации типа Python 3.5, чтобы сделать этот код понятным):

from typing import TypeVar, Callable, Iterable

A = TypeVar('A')
B = TypeVar('B')
Predicate = Callable[[A], bool]
Action = Callable[[A], B]
Switch = Tuple[Predicate, Action]

def switch(switches: Iterable[Switch], default: B, x: A) -> B:
    return next(
        (act(x) for pred, act in switches if pred(x)), default
    )

switches = [
    (lambda x: '\u2011' in x, lambda x: x.replace('\u2011', '-')),
    (lambda x: '\u2013' in x, lambda x: x.replace('\u2013', '-'))
]
a = "I'm–a–string–with–en–dashes"

switch(switches, a, a) # if no switches are matched, return the input

Это довольно излишне в вашем случае, потому что ваш пример сводится к операции регулярного выражения. Принять к сведению, в то время как switches может быть любым итеративным, вы можете использовать что-то с предсказуемым порядком итерации, т.е. Sequence тип (например list или же tuple), потому что будет использовано первое действие с соответствующим предикатом.

Другие вопросы по тегам