Ћекции.ќрг


ѕоиск:




 атегории:

јстрономи€
Ѕиологи€
√еографи€
ƒругие €зыки
»нтернет
»нформатика
»стори€
 ультура
Ћитература
Ћогика
ћатематика
ћедицина
ћеханика
ќхрана труда
ѕедагогика
ѕолитика
ѕраво
ѕсихологи€
–елиги€
–иторика
—оциологи€
—порт
—троительство
“ехнологи€
“ранспорт
‘изика
‘илософи€
‘инансы
’ими€
Ёкологи€
Ёкономика
Ёлектроника

 

 

 

 


я знаю, используем регул€рные выражени€!




»так, вы смотрите на слова, и, по крайней мере в английском, это означает, что вы смотрите на последовательности символов. ” вас есть правила, которые говор€т, что нужно искать разные комбинации символов, потом совершать с ними различные действи€. ѕохоже, это работа дл€ регул€рных выражений!

import re

def plural(noun):
if re.search('[sxz]$', noun): UNIQd2f4acf57a9a5326-ref-00000003-QINU
return re.sub('$', 'es', noun) UNIQd2f4acf57a9a5326-ref-00000006-QINU
elif re.search('[^aeioudgkprt]h$', noun):
return re.sub('$', 'es', noun)
elif re.search('[^aeiou]y$', noun):
return re.sub('y$', 'ies', noun)
else:
return noun + 's'

  1. ↑ Ёто регул€рное выражение, но оно использует синтаксис, который вы не видели в главе –егул€рные ¬ыражени€.  вадратные скобки означают Ђнайти совпадени€ ровно с одним из этих символовї. ѕоэтому [sxz] означает Ђs или x, или zї, но только один из них. —имвол $ должен быть знаком вам. ќн ищет совпадени€ с концом строки. ¬се регул€рное выражение провер€ет, заканчиваетс€ ли noun на s, x или z.
  2. ↑ ”пом€нута€ функци€ re.sub() производит замену подстроки на основе регул€рного выражени€.

–ассмотрим замену с помощью регул€рного выражени€ внимательнее.

>>> import re
>>> re.search('[abc]', 'Mark') ①
<_sre.SRE_Match object at 0x001C1FA8>
>>> re.sub('[abc]', 'o', 'Mark') ②
'Mork'
>>> re.sub('[abc]', 'o', 'rock') ③
'rook'
>>> re.sub('[abc]', 'o', 'caps') ④
'oops'

  1. —одержит ли строка Mark символы a, b или c? ƒа, содержит a.
  2. ќтлично, теперь ищем a, b или c и замен€ем на o. Mark становитс€ Mork.
  3. “а же функци€ превращает rock в rook.
  4. ¬ы могли подумать, что этот код caps преобразует в oaps, но он этого не делает. re.sub замен€ет все совпадени€, а не только первое найденное. “ак что, данное регул€рное выражение превратит caps в oops, потому что оба символа c и a замен€ютс€ на o.

¬ернемс€ снова к функции plural()Е

def plural(noun):
if re.search('[sxz]$', noun):
return re.sub('$', 'es', noun) ①
elif re.search('[^aeioudgkprt]h$', noun): ②
return re.sub('$', 'es', noun)
elif re.search('[^aeiou]y$', noun): ③
return re.sub('y$', 'ies', noun)
else:
return noun + 's'

  1. «десь вы замен€ете конец строки (найденный с помощью символа $) на строку es. ƒругими словами, добавл€ете es к строке. ¬ы могли бы совершить то же самое с помощью конкатенации строк, например, как noun + 'es', но € предпочел использовать регул€рные выражени€ дл€ каждого правила, по причинам которые станут €сны позже.
  2. ¬згл€ните-ка, это регул€рное выражение содержит кое-что новое. —имвол ^ в качестве первого символа в квадратных скобках имеет особый смысл: отрицание. [^abc] означает Ђлюбой отдельный символ кроме a, b или cї. “ак что [^aeioudgkprt] означает любой символ кроме a, e, i, o, u, d, g, k, p, r или t. «атем за этим символом должен быть символ h, следом за ним Ч конец строки. ¬ы ищете слова, заканчивающиес€ на H, которую можно услышать.
  3. “о же самое здесь: найти слова, которые заканчиваютс€ на Y, в которых символ перед Y Ч не a, e, i, o или u. ¬ы ищете слова, заканчивающиес€ на Y, котора€ звучит как I.

ƒавайте внимательнее рассмотрим регул€рные выражени€ с участием отрицани€.

>>> import re
>>> re.search('[^aeiou]y$', 'vacancy') ①
<_sre.SRE_Match object at 0x001C1FA8>
>>> re.search('[^aeiou]y$', 'boy') ②
>>>
>>> re.search('[^aeiou]y$', 'day')
>>>
>>> re.search('[^aeiou]y$', 'pita') ③
>>>

  1. vacancy подходит, потому что оно заканчиваетс€ на cy, и c Ч не a, e, i, o или u.
  2. boy не подходит, потому что оно заканчиваетс€ на oy, а вы конкретно указали, что символ перед y не может быть o. day не подходит, потому что он заканчиваетс€ на ay.
  3. pita не подходит, потому что оно не заканчиваетс€ на y.

>>> re.sub('y$', 'ies', 'vacancy') ①
'vacancies'
>>> re.sub('y$', 'ies', 'agency') 'agencies'
>>> re.sub('([^aeiou])y$', r'\1ies', 'vacancy') ②
'vacancies'

  1. Ёто регул€рное выражение преобразует vacancy в vacancies, а agency Ч в agencies, что вам и нужно. «аметьте, что оно бы преобразовало boy в boies, но этого в функции никогда не произойдет, потому что вы сначала сделали re.search с целью вы€снить, следует ли делать re.sub.
  2. «амечу заодно, что возможно объединить эти два регул€рных выражени€ (одно чтобы вы€снить примен€етс€ ли правило, а другое чтобы собственно его применить) в одно регул€рное выражение. ¬от так выгл€дел бы результат. Ѕольша€ часть должна быть вам знакома: вы используете запоминаемую группу, о которой вы узнали из ”чебный пример: –азбор телефонного номера. √руппа используетс€ чтобы запомнить символ перед y. «атем в подстановочной строке, вы используете новый синтаксис, \1, который означает Ђэй, та перва€ группа, которую ты запомнил? положи ее сюдаї. “аким образом, вы помните c перед y; когда вы делаете подстановку, вы ставите c на место c, и ies на место y. (≈сли у вас более одной запоминаемой группы, можете использовать \2 и \3 и так далее.)

«амены с использованием регул€рных выражений €вл€ютс€ чрезвычайно мощным инструментом, а синтаксис \1 делает их еще более мощным. Ќо вс€ операци€, объединенна€ в одно регул€рное выражение, также становитс€ сложной дл€ чтени€, кроме того такой способ не соотноситс€ напр€мую с тем, как вы изначально описали правила формировани€ множественного числа. »значально вы спроектировали правила в форме Ђесли слово заканчиваетс€ на S, X или Z, то добавьте ESї. ј если вы смотрите на функцию, то у вас Ч две строки кода, которые говор€т Ђесли слово заканчиваетс€ на S, X или Z, то добавьте ESї. ≈ще ближе к оригинальному варианту приблизитьс€ никак не получитс€.

—писок функций

—ейчас вы добавите уровень абстракции. ¬ы начали с определени€ списка правил: если верно это, сделай то, иначе обращайтесь к следующему правилу. ƒавайте временно усложним часть программы, так что вы сможете упростить другую ее часть.

import re

def match_sxz(noun):
return re.search('[sxz]$', noun)

def apply_sxz(noun):
return re.sub('$', 'es', noun)

def match_h(noun):
return re.search('[^aeioudgkprt]h$', noun)

def apply_h(noun):
return re.sub('$', 'es', noun)

def match_y(noun): ①
return re.search('[^aeiou]y$', noun)

def apply_y(noun): ②
return re.sub('y$', 'ies', noun)

def match_default(noun):
return True

def apply_default(noun):
return noun + 's'

rules = ((match_sxz, apply_sxz), ③
(match_h, apply_h),
(match_y, apply_y),
(match_default, apply_default)
)

def plural(noun):
for matches_rule, apply_rule in rules: ④
if matches_rule(noun):
return apply_rule(noun)

  1. “еперь каждое правило-условие совпадени€ €вл€етс€ отдельной функцией котора€ возвращает результаты вызова функции re.search().
  2.  аждое правило-действие также €вл€етс€ отдельной функцией, котора€ вызывает функцию re.sub() чтобы применить соответствующее правило формировани€ множественного числа.
  3. ¬место одной функции (plural()) с несколькими правилами у вас теперь есть структура данных rules, €вл€юща€с€ последовательностью пар функций.
  4. ѕоскольку правила развернуты в отдельной структуре данных, нова€ функци€ plural() может быть сокращена до нескольких строк кода. »спользу€ цикл for, из структуры rules можно извлечь правила услови€ и замены одновременно. ѕри первой итерации for цикла, match_rules станет match_sxz, а apply_rule станет apply_sxz. ¬о врем€ второй итерации, если мы до нее дойдем, matches_rule будет присвоено match_h, а apply_rule станет apply_h. ‘ункци€ гарантированно вернет что-нибудь по окончании работы, потому что последнее правило совпадени€ (match_default) просто возвращает True, подразумева€, что соответствующее правило замены (apply_default) всегда будет применено.

ѕричиной, по которой этот пример работает, €вл€етс€ тот факт, что в Python все €вл€етс€ объектом, даже функции. —труктура данных rules содержит функции Ч не имена функций, а фактические функции-объекты.  огда они присваиваютс€ в for цикле, matches_rule и apply_rule €вл€ютс€ насто€щими функци€ми, которые вы можете вызывать. ѕри первой итерации for цикла, это эквивалентно вызову matches_sxz(noun), и если она возвращает совпадение, вызову apply_sxz(noun).

-> ѕеременна€ Ђrulesї Ч это последовательность пар функций. [waр-robin.сom]

≈сли этот дополнительный уровень абстракции сбивает вас с толку, попробуйте развернуть функцию, чтобы увидеть, что мы получаем то же самое. ¬есь цикл for эквивалентен следующему:

def plural(noun):
if match_sxz(noun):
return apply_sxz(noun)
if match_h(noun):
return apply_h(noun)
if match_y(noun):
return apply_y(noun)
if match_default(noun):
return apply_default(noun)

ѕреимуществом здесь €вл€етс€ то, что функци€ plural() упрощена. ќна принимает последовательность правил, определенных где-либо, и проходит по ним.

  1. ѕолучить правило совпадени€
  2. ѕравило срабатывает? “огда применить правило замены и вернуть результат.
  3. Ќет совпадений? Ќачать с пункта 1.

ѕравила могут быть определены где угодно, любым способом. ƒл€ функции plural() абсолютно нет никакой разницы.

»так, добавление этого уровн€ абстракции стоило того? ¬ообще-то пока нет. ѕопробуем представить, что потребуетс€ дл€ добавлени€ нового правила в функцию. ¬ первом примере этого потребовало бы добавить новую конструкцию if в функцию plural(). ¬о втором примере, это потребовало бы добавить две функции, match_foo() и apply_foo(), а затем обновить последовательность rules чтобы указать, когда новые правила совпадени€ и замены должны быть вызваны по отношению к остальным правилам.

Ќо на самом деле это только средство, чтобы перейти к следующей главе. ƒвигаемс€ дальшеЕ

—писок шаблонов

ќпределение отдельных именованных функций дл€ каждого услови€ и правила замены вовсе не €вл€етс€ необходимостью. ¬ы никогда не вызываете их напр€мую; вы добавл€ете их в последовательность rules и вызываете их через нее. Ѕолее того, кажда€ функци€ следует одному из двух шаблонов. ¬се функции совпадени€ вызывают re.search(), а все функции замены вызывают re.sub(). ƒавайте исключим шаблоны, чтобы объ€вление новых правил было более простым.

import re

def build_match_and_apply_functions(pattern, search, replace):
def matches_rule(word): ①
return re.search(pattern, word)
def apply_rule(word): ②
return re.sub(search, replace, word)
return (matches_rule, apply_rule) ③

  1. build_match_and_apply_functions() Ч это функци€, котора€ динамически создает другие функции. ќна принимает pattern, search и replace, затем определ€ет функцию matches_rule(), котора€ вызывает re.search() с шаблоном pattern, переданный функции build_match_and_apply_functions() в качестве аргумента, и word, который передан функции matches_rule(), которую вы определ€ете.
  2. —троим функцию apply тем же способом. ‘ункци€ apply Ч это функци€, котора€ принимает один параметр, и вызывает re.sub() с search и replace параметрами, переданными функции build_match_and_apply_functions, и word, переданным функции apply_rule(), которую вы создаете. ѕодход, заключающийс€ в использовании значений внешних параметров внутри динамической функции называетс€ замыкани€ми. ѕо сути вы определ€ете константы в функции замены: он принимает один параметр (word), но затем действует использу€ его и два других значени€ (search и replace), которые были установлены в момент определени€ функции замены.
  3. ¬ конце концов функци€ build_match_and_apply_functions() возвращает кортеж с двум€ значени€ми, двум€ функци€ми, которые вы только что создали.  онстанты, которые вы определили внутри тех функций (pattern внутри функции match_rule()), search и replace в функции apply_rule()) остаютс€ с этими функци€ми, даже. Ёто безумно круто.

≈сли это сбивает вас с толку (и так и должно быть, это весьма странное поведение), картина может про€снитьс€, когда вы увидите как использовать этот подход.

patterns = \ ①
(
('[sxz]$', '$', 'es'),
('[^aeioudgkprt]h$', '$', 'es'),
('(qu|[^aeiou])y$', 'y$', 'ies'),
('$', '$', 's') ②
)
rules = [build_match_and_apply_functions(pattern, search, replace) ③
for (pattern, search, replace) in patterns]

  1. Ќаши правила формировани€ множественного числа теперь определены как кортеж кортежей строк (не функций). ѕерва€ строка в каждой группе Ч это регул€рное выражение, которое вы бы использовали в re.search() чтобы определить, подходит ли данное правило. ¬тора€ и треть€ строки в каждой группе Ч это выражени€ дл€ поиска и замены, которые вы бы использовали в re.sub() чтобы применить правило и преобразовать существительное во множественное число.
  2. ¬ альтернативном правиле есть небольшое изменение. ¬ прошлом примере функци€ match_default() просто возвращает True, подразумева€, что если ни одно конкретное правило не применилось, код должен просто добавить s в конец данного слова. ‘ункционально данный пример делает то же самое. ќкончательное регул€рное выражение узнает, заканчиваетс€ ли слово ($ ищет конец строки).  онечно же, у каждой строки есть конец, даже у пустой, так что выражение всегда срабатывает. “аким образом, она служит той же цели, что и функци€ match_default(), котора€ всегда возвращала True: она гарантирует, что если нет других конкретных выполненных правил, код добавл€ет s в конец данного слова.
  3. Ёто волшебна€ строка. ќна принимает последовательность строк в patterns и превращает их в последовательность функций.  ак? Ђќтображениемї строк в функцию build_and_apply_functions(). “о есть она берет каждую тройку строк и вызывает функцию build_match_and_apply_functions() с этими трем€ строками в качестве аргументов. ‘ункци€ build_match_and_apply_functions() возвращает кортеж из двух функций. Ёто означает, что rules в конце концов функционально становитс€ эквивалентной предыдущему примеру: список кортежей, где каждый кортеж Ч это пара функций. ѕерва€ функци€ Ч это функци€ совпадени€, котора€ вызывает re.search(), а втора€ функци€ Ч применение правила (замена), котора€ вызывает re.sub().

«авершим эту версию скрипта главной точкой входа, функцией plural().

def plural(noun):
for matches_rule, apply_rule in rules: ①
if matches_rule(noun):
return apply_rule(noun)

  1. ѕоскольку список rules Ч тот же самый, что и в предыдущем примере (да, так и есть), нет ничего удивительного в том, что функци€ plural() совсем не изменилась. ќна €вл€етс€ полностью обобщенной; она принимает список функций-правил и вызывает их по пор€дку. ≈е не волнует, как определены правила. ¬ предыдущем примере они были определены как отдельные именованные функции. “еперь же они создаютс€ динамически сопоставлением результата функции build_match_and_apply_functions() списку обычных строк. Ёто не играет никакой роли. функци€ plural() продолжает работать как и раньше.

‘айл шаблонов

¬ы вынесли весь дублирующийс€ код и добавили достаточно абстракций дл€ возможности хранить правила формировани€ множественного числа в списке строк. —ледующий логический этап Ч вз€ть эти строки и расположить их в отдельном файле, где они могут поддерживатьс€ отдельно от использующего их кода.

¬о-первых, давайте создадим текстовый файл, содержащий нужные нам правила. Ќикаких сложных структур данных, просто разделенные на три колонки данные. Ќазовем его plural4-rules.txt

[sxz]$ $ es
[^aeioudgkprt]h$ $ es
[^aeiou]y$ y$ ies
$ $ s

“еперь давайте посмотрим, как вы можете использовать этот файл с правилами.

import re

def build_match_and_apply_functions(pattern, search, replace): ①
def matches_rule(word):
return re.search(pattern, word)
def apply_rule(word):
return re.sub(search, replace, word)
return (matches_rule, apply_rule)

rules = []
with open('plural4-rules.txt', encoding='utf-8') as pattern_file: ②
for line in pattern_file: ③
pattern, search, replace = line.split(None, 3) ④
rules.append(build_match_and_apply_functions(⑤
pattern, search, replace))

  1. ‘ункци€ build_match_and_apply_functions() не изменилась. ¬ы по-прежнему используете замыкани€, чтобы динамически создать две функции, которые будут использовать переменные из внешней функции.
  2. √лобальна€ функци€ open() открывает файл и возвращает файловый объект. ¬ данном случае файл, который мы открываем, содержит строки-шаблоны дл€ правил формировани€ множественного числа. ”тверждение with создает то, что называетс€ контекстом: когда блок with заканчиваетс€, Python автоматически закроет файл, даже если внутри блока with было выброшено исключение. ѕодробнее о блоках with и файловых объектах вы узнаете из главы ‘айлы.
  3. ‘орма Ђfor line in <fileobject>ї читает данные из открытого файла построчно и присваивает текст переменной line. ѕодробнее про чтение файлов вы узнаете из главы ‘айлы.
  4.  ажда€ строка в файле действительно содержит три значени€, но они разделены пустым пространством (табул€цией или пробелами, без разницы). „тобы разделить их, используйте строковый метод split(). ѕервый аргумент дл€ split() Ч None, что означает Ђразделить любым символом свободного пространства (табул€цией или пробелом, без разницы)ї. ¬торой аргумент Ч 3, что означает Ђразбить свободным пространством 3 раза, затем оставить остальную часть строкиї —трока вида Ђ[sxz]$ $ esї будет разбита и преобразована в список ['[sxz]$', '$', 'es'], что означает что pattern станет '[sxz]$', search Ч '$', а replace получит значение 'es'. Ёто довольно мощно дл€ одной маленькой строки кода
  5. ¬ конце концов, вы передаете pattern, search и replace функции build_match_and_apply_function(), котора€ возвращает кортеж функций. ¬ы добавл€ете этот кортеж в список rules, и в завершении rules хранит список функций поиска совпадений и выполнени€ замен, который ожидает функци€ plural().

—деланное улучшение заключаетс€ в том, что вы полностью вынесли правила во внешний файл, так что он может поддерживатьс€ отдельно от использующего его кода.  од Ч это код, данные Ч это данные, а жизнь хороша.

√енераторы

Ќо ведь будет круто если обобщенна€ функци€ plural() будет разбирать файл с правилами? »звлеки правила, найди совпадени€, примени соответствующие изменени€, переходи к следующему правилу. Ёто все, что функции plural() придетс€ делать, и больше ничего от нее не требуетс€.

def rules(rules_filename):
with open(rules_filename, encoding='utf-8') as pattern_file:
for line in pattern_file:
pattern, search, replace = line.split(None, 3)
yield build_match_and_apply_functions(pattern, search, replace)

def plural(noun, rules_filename='plural5-rules.txt'):
for matches_rule, apply_rule in rules(rules_filename):
if matches_rule(noun):
return apply_rule(noun)
raise ValueError('no matching rule for {0}'.format(noun))

 ак черт возьми это работает? ƒавайте сначала посмотрим на пример с по€снени€ми.

>>> def make_counter(x):
... print('entering make_counter')
... while True:
... yield x ①
... print('incrementing x')
... x = x + 1
...
>>> counter = make_counter(2) ②
>>> counter ③
<generator object at 0x001C9C10>
>>> next(counter) ④
entering make_counter
2
>>> next(counter) ⑤
incrementing x
3
>>> next(counter) ⑥
incrementing x
4

  1. ѕрисутствие ключевого слова yield в make_counter означает, что это не обычна€ функци€. Ёто особый вид функции, котора€ генерирует значени€ по одному. ¬ы можете думать о ней как о продолжаемой функции. ≈Є вызов вернЄт генератор, который может быть использован дл€ генерации последующих значений x.
  2. „тобы создать экземпл€р генератора make_counter, просто вызовите его как и любую другую функцию. «аметьте, что фактически это не выполн€ет кода функции. ¬ы можете так сказать, потому что перва€ строка функции make_counter() вызывает print(), но ничего до сих пор не напечатано.
  3. ‘ункци€ make_counter() возвращает объект-генератор.
  4. ‘ункци€ next() принимает генератор и возвращает его следующее значение. ѕервый раз, когда вы вызываете next() с генератором counter, он исполн€ет код в make_counter() до первого утверждени€ yield, затем возвращает значение, которое было возвращено yield. ¬ данном случае, это будет 2, поскольку изначально вы создали генератор вызовом make_counter(2).
  5. ѕовторный вызов next() с тем же генератором продолжает вычислени€ точно там, где они были прерваны, и продолжает до тех пор, пока не встретит следующий yield. ¬се переменные, локальные состо€ни€ и т. д. сохран€ютс€ во врем€ yield, и восстанавливаютс€ при вызове next(). —ледующа€ строка кода, ожидающа€ исполнени€, вызывает print(), который печатает incrementing x. ѕосле этого следует утверждение x = x + 1. «атем снова исполн€етс€ цикл while, и первое, что в нЄм встречаетс€ Ч утверждение yield x, которое сохран€ет все состо€ние и возвращает текущее значение x (сейчас это 3).
  6. ѕосле второго вызова next(counter) происходит всЄ то же самое, только теперь x становитс€ равным 4.

ѕоскольку make_counter входит в бесконечный цикл, вы бы теоретически могли заниматьс€ этим бесконечно, а он продолжал бы увеличивать x и возвращать его значени€. Ќо вместо этого давайте посмотрим на более продуктивное использование генераторов.





ѕоделитьс€ с друзь€ми:


ƒата добавлени€: 2016-11-18; ћы поможем в написании ваших работ!; просмотров: 459 | Ќарушение авторских прав


ѕоиск на сайте:

Ћучшие изречени€:

Ќеосмысленна€ жизнь не стоит того, чтобы жить. © —ократ
==> читать все изречени€...

516 - | 451 -


© 2015-2023 lektsii.org -  онтакты - ѕоследнее добавление

√ен: 0.024 с.