Так выглядит сервер, poem_pub.py, который отщипывает по одному слову стихотворения и публикует его в тему vowels, если оно начинается с гласной, и в тему five, если состоит из пяти букв. Некоторые слова могут оказаться в обеих темах, некоторые — ни в одной:
import string
import zmq
host = '127.0.0.1'
port = 6789
ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind('tcp://%s:%s' % (host, port))
with open('mammoth.txt', 'rt') as poem:
····words = poem.read()
for word in words.split():
····word = word.strip(string.punctuation)
····data = word.encode('utf-8')
····if word.startswith(('a','e','i','o','u','A','e','i','o','u')):
········pub.send_multipart([b'vowels', data])
····if len(word) == 5:
········pub.send_multipart([b'five', data])
Клиент poem_sub.py подписывается на темы vowels и five и выводит на экран тему и слово:
import string
import zmq
host = '127.0.0.1'
port = 6789
ctx = zmq.Context()
sub = ctx.socket(zmq.SUB)
sub.connect('tcp://%s:%s' % (host, port))
sub.setsockopt(zmq.SUBSCRIBE, b'vowels')
sub.setsockopt(zmq.SUBSCRIBE, b'five')
while True:
····topic, word = sub.recv_multipart()
····print(topic, word)
Если вы запустите эти программы, они не будут работать, хотя код выглядит хорошо. Вам нужно прочитать руководство ZeroMQ, чтобы узнать о проблеме медленного присоединившегося: даже если вы запустите клиент раньше сервера, сервер начнет отправлять данные сразу после запуска, а клиенту потребуется некоторое время, чтобы подключиться к серверу. Если вы публикуете сообщения постоянным потоком и не задумываетесь о том, когда к вам подключаются подписчики, это не проблема. Но в этом случае поток данных настолько короткий, что он заканчивается еще до того, как подписчик успеет моргнуть.
Простейший способ исправить это — заставить публикатора пропустить секунду после вызова метода bind() и до того, как он начнет отправлять сообщения. Назовем эту версию poem_pub_sleep.py:
import string
import zmq
from time import sleep
host = '127.0.0.1'
port = 6789
ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind('tcp://%s:%s' % (host, port))
sleep(1)
with open('mammoth.txt', 'rt') as poem:
····words = poem.read()
for word in words.split():
····word = word.strip(string.punctuation)
····data = word.encode('utf-8')
····if word.startswith(('a','e','i','o','u','A','e','i','o','u')):
········print('vowels', data)
········pub.send_multipart([b'vowels', data])
····if len(word) == 5:
········print('five', data)
········pub.send_multipart([b'five', data])
Запустите подписчика, а затем и сонного публикатора:
$ python poem_sub.py
$ python poem_pub_sleep.py
Теперь у подписчика есть время на то, чтобы получить сообщения по выбранным темам. Так выглядят первые строки выходной информации:
b'five' b'queen'
b'vowels' b'of'
b'five' b'Lying'
b'vowels' b'at'
b'vowels' b'ease'
b'vowels' b'evening'
b'five' b'flies'
b'five' b'seize'
b'vowels' b'All'
b'five' b'gaily'
b'five' b'great'
b'vowels' b'admired'
Если вы не можете добавить вызов sleep() в код публикатора, вы можете синхронизировать публикатора и подписчика с помощью сокетов REQ и REP. Примеры файлов publisher.py и subscriber.py вы можете найти на GitHub.
Приложение Е. Вспомогательные таблицы
Я обнаружил, что некоторые вещи мне приходится подсматривать слишком часто. Вот несколько таблиц, которые, надеюсь, окажутся вам полезны.
Приоритет операторов
Эта таблица — ремикс официальной документации о приоритетах для Python 3, операторы с самым высоким приоритетом находятся наверху.
Оператор Описание и примеры [v1, …], { v1, …}, { k1: v1, …}, (…) Создание или включение списка/множества/словаря/генератора, выражение в скобках seq [n], seq [n: m], func (args…), obj.attr Индекс, разбиение, вызов функции, ссылка на атрибут ** Экспонента '+'x, '-'x, '~'x Знаки «плюс» и «минус», битовое НЕ *, /, //, % Умножение, деление с плавающей точкой, целочисленное деление, напоминание +, - Сложение, вычитание <<, >> Битовый сдвиг вправо или влево & Битовое И | Битовое ИЛИ in, not in, is, is not, <, <=, >, >=,!=, == Проверка на членство и равенство not x Булево (логическое) НЕ and Булево И or Булево ИЛИ if … else Условное выражение lambda Лямбда-выражение
Строковые методы
Python предлагает строковые методы (могут быть использованы с любым объектом str) и модуль string, содержащий полезные определения. Воспользуемся проверочными переменными:
>>> s = "OH, my paws and whiskers!"
>>> t = "I'm late!"
Изменение регистра
>>> s.capitalize()
'Oh, my paws and whiskers!'
>>> s.lower()
'oh, my paws and whiskers!'
>>> s.swapcase()
'oh, MY PAWS AND WHISKERS!'
>>> s.title()
'Oh, My Paws And Whiskers!'
>>> s.upper()
'OH, MY PAWS AND WHISKERS!'
Поиск
>>> s.count('w')
2
>>> s.find('w')
9
>>> s.index('w')
9
>>> s.rfind('w')
16
>>> s.rindex('w')
16
>>> s.startswith('OH')
True
Изменение
>>> ''.join(s)
'OH, my paws and whiskers!'
>>> ' '.join(s)
'O H,···m y···p a w s···a n d···w h i s k e r s!'
>>> ' '.join((s, t))
"OH, my paws and whiskers! I'm late!"
>>> s.lstrip('HO')
', my paws and whiskers!'
>>> s.replace('H', 'MG')
'OMG, my paws and whiskers!'
>>> s.rsplit()
['OH,', 'my', 'paws', 'and', 'whiskers!']
>>> s.rsplit(' ', 1)
['OH, my paws and', 'whiskers!']
>>> s.split()
['OH,', 'my', 'paws', 'and', 'whiskers!']
>>> s.split(' ')
['OH,', 'my', 'paws', 'and', 'whiskers!']
>>> s.splitlines()
['OH, my paws and whiskers!']
>>> s.strip()
'OH, my paws and whiskers!'
>>> s.strip('s!')
'OH, my paws and whisker'
Форматирование
>>> s.center(30)
'··OH, my paws and whiskers!···'
>>> s.expandtabs()
'OH, my paws and whiskers!'
>>> s.ljust(30)
'OH, my paws and whiskers!·····'
>>> s.rjust(30)
'·····OH, my paws and whiskers!'
Тип строки
>>> s.isalnum()
False
>>> s.isalpha()
False
>>> s.isprintable()
True
>>> s.istitle()
False
>>> s.isupper()
False
>>> s.isdecimal()
False
>>> s.isnumeric()
False
Атрибуты модуля string