Материалы для дальнейшего чтения

  • PEP 255: Simple Generators
  • Understanding Python’s «with» statement
  • Closures in Python
  • Числа Фибоначчи
  • English Irregular Plural Nouns

Классы и итераторы

Погружение

Генераторы на самом деле — всего лишь частный случай итераторов. Функция, возвращающая (yields) значения, является простым и компактным способом получения функциональности итератора, без непосредственного создания итератора. Помните генератор чисел Фибоначчи? Вот набросок того, как мог бы выглядеть аналогичный итератор:

class Fib:
'''iterator that yields numbers in the Fibonacci sequence'''

def __init__(self, max):
self.max = max

def __iter__(self):
self.a = 0
self.b = 1
return self

def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib

Давайте рассмотрим этот пример более детально:

class Fib:

class? Что такое класс?

Определение классов

Python полностью объектно-ориентирован, то есть вы можете определять свои собственные классы, наследовать новые классы от своих или встроенных классов, и создавать экземпляры классов, которые уже определили.

Определить класс в Python просто. Также как и в случае с функциями, раздельное объявление интерфейса не требуется. Вы просто определяете класс и начинаете программировать. Определение класса в Python начинается с зарезервированного слова class, за которым следует имя (идентификатор) класса. Формально, это все, что необходимо, в случае, когда класс не должен быть унаследован от другого класса.

class PapayaWhip: [K 1]
pass [K 2]
  1. ↑ Определенный выше класс имеет имя PapayaWhip и не наследует никакой другой класс. Имена классов, как правило, пишутся с большой буквы, НапримерВотТак, но это всего лишь соглашение, а не требование.
  2. ↑ Вы наверное уже догадались, что каждая строка в определении класса имеет отступ, также как и в случае с функциями, оператором условного перехода if, циклом for или любым другим блоком кода. Первая строка без отступа находится вне блока class.

Класс PapayaWhip не содержит определений методов или атрибутов, но с точки зрения синтаксиса, тело класса не может оставаться пустым. В таких случаях используется оператор pass. В языке Python pass — зарезервированное слово, которое говорит интерпретатору: «идем дальше, здесь ничего нет». Это оператор не делающий ровным счетом ничего, но тем не менее являющийся удобным решением, когда вам нужно сделать заглушку для функции или класса.

Материалы для дальнейшего чтения - student2.ru Выражение pass в языке Python аналог пустого множества или фигурных скобок в языках Java или C++.

Многие классы наследуются от других классов, но не этот. Многие классы определяют свои методы, но не этот. Класс в Python не обязан иметь ничего, кроме имени. В частности, людям знакомым с C++ может показаться странным, что у класса в Python отсутствуют в явном виде конструктор и деструктор. Несмотря на то, что это не является обязательным, класс в Python может иметь нечто, похожее на конструктор: метод __init__().

Метод __init__()

В следующем примере демонстрируется инициализация класса Fib, с помощью метода __init__().

class Fib: '''iterator that yields numbers in the Fibonacci sequence''' [K 1]
def __init__(self, max): [K 2]
  1. ↑ Классы, по аналогии с модулями и функциями могут (и должны) иметь строки документации (docstrings).
  2. ↑ Метод __init__() вызывается сразу же после создания экземпляра класса. Было бы заманчиво, но формально неверно, считать его «конструктором» класса. Заманчиво, потому что он напоминает конструктор класса в языке C++: внешне (общепринято, что метод __init__() должен быть первым методом, определенным для класса), и в действии (это первый блок кода, исполняемый в контексте только что созданного экземпляра класса). Неверно, потому что на момент вызова __init__() объект уже фактически является созданным, и вы можете оперировать корректной ссылкой на него (self)

Первым аргументов любого метода класса, включая метод __init__(), всегда является ссылка на текущий экземпляр класса. Принято называть этот аргумент self. Этот аргумент выполняет роль зарезервированного слова this в C++ или Java, но, тем не менее, в Python self не является зарезервированным. Несмотря на то, что это всего лишь соглашение, пожалуйста не называйте этот аргумент как либо еще.

В случае метода __init__(), self ссылается на только что созданный объект; в остальных методах — на экземпляр, метод которого был вызван. И, хотя вам необходимо явно указывать self при определении метода, при вызове этого не требуется; Python добавит его для вас автоматически.

Создание экземпляров

Для создания нового экземпляра класса в Python нужно вызвать класс, как если бы он был функцией, передав необходимые аргументы для метода __init__(). В качестве возвращаемого значения мы получим только что созданный объект.

>>> import fibonacci2 >>> fib = fibonacci2.Fib(100)
>>> fib
<fibonacci2.Fib object at 0x00DB8810> >>> fib.__class__
<class 'fibonacci2.Fib'> >>> fib.__doc__
'iterator that yields numbers in the Fibonacci sequence'  
  1. Вы создаете новый экземпляр класса Fib (определенный в модуле fibonacci2) и присваиваете только что созданный объект переменной fib. Единственный переданный аргумент, 100, соответствует именованному аргументу max, в методе __init__() класса Fib.
  2. fib теперь является экземпляром класса Fib
  3. Каждый экземпляр класса имеет встроенный атрибут __class__, который указывает на класс объекта. Java программисты могут быть знакомы с классом Class, который содержит методы getName() и getSuperclass(), используемые для получения информации об объекте. В Python, метаданные такого рода доступны через соответствующие атрибуты, но используемая идея та же самая.
  4. Вы можете получить строку документации (docstring) класса, по аналогии с функцией и модулем. Все экземпляры класса имеют одну и ту же строку документации.
Материалы для дальнейшего чтения - student2.ru Для создания нового экземпляра класса в Python, просто вызовите класс, как если бы он был функцией, явные операторы, как например new в С++ или Java, в языке Python отсутствуют.

Переменные экземпляра

Перейдем к следующей строчке:

class Fib: def __init__(self, max): self.max = max 1.
  1. Что такое self.max? Это переменная экземпляра. Она не имеет ничего общего с переменной max, которую мы передали в метод __init__() в качестве аргумента. self.max является «глобальной» для всего экземпляра. Это значит, что вы можете обратиться к ней из других методов.
class Fib: def __init__(self, max): self.max = max 1.
. . . def __next__(self): fib = self.a if fib > self.max: 2.
  1. self.max определена в методе __init__()...
  2. …и использована в методе __next__().

Переменные экземпляра связаны только с одним экземпляром класса. Например, если вы создадите два экземпляра класса Fib с разными максимальными значениями, каждый из них будет помнить только свое собственное значение.

>>> import fibonacci2
>>> fib1 = fibonacci2.Fib(100)
>>> fib2 = fibonacci2.Fib(200)
>>> fib1.max
100
>>> fib2.max
200

Итератор чисел Фибоначчи

Теперь вы готовы узнать как создать итератор. Итератор это обычный класс, который определяет метод __iter__().

class Fib: ①
def __init__(self, max): ②
self.max = max

def __iter__(self): ③
self.a = 0
self.b = 1
return self

def __next__(self): ④
fib = self.a
if fib > self.max:
raise StopIteration ⑤
self.a, self.b = self.b, self.a + self.b
return fib ⑥

① Чтобы построить итератор с нуля,Fib должна быть классом, а не функцией.

Подробнее об итераторах

Погружение

HAWAII + IDAHO + IOWA + OHIO == STATES. Или, если записать это по-другому, 510199 + 98153 + 9301 + 3593 == 621246. Думаете, я брежу? Нет, это просто головоломка.

Позвольте мне привести разгадку.

HAWAII + IDAHO + IOWA + OHIO == STATES
510199 + 98153 + 9301 + 3593 == 621246

H = 5
A = 1
W = 0
I = 9
D = 8
O = 3
S = 6
T = 2
E = 4

Такие головоломки называются криптарифмами. Буквы составляют существующие слова, а если заменить каждую букву цифрой от 0 до 9, получится еще и правильное математическое равенство. Весь фокус в том, чтобы выяснить какая буква соответствует каждой цифре. Все вхождения каждой буквы должны заменяться одной и той же цифрой, одной цифре не могут соответствовать несколько букв и «слова» не могут начинаться с цифры 0.

В этой главе мы познакомимся с потрясающей программой на языке Python, написанной Рэймондом Хейттингером. Эта программа решает криптарифмические головоломки и состоит всего из 14 строк кода.

Наиболее известная криптарифмическая головоломка SEND + MORE = MONEY.

import re
import itertools

def solve(puzzle):
words = re.findall('[A-Z]+', puzzle.upper())
unique_characters = set(''.join(words))
assert len(unique_characters) <= 10, 'Too many letters'
first_letters = {word[0] for word in words}
n = len(first_letters)
sorted_characters = ''.join(first_letters) + \
''.join(unique_characters - first_letters)
characters = tuple(ord(c) for c in sorted_characters)
digits = tuple(ord(c) for c in '0123456789')
zero = digits[0]
for guess in itertools.permutations(digits, len(characters)):
if zero not in guess[:n]:
equation = puzzle.translate(dict(zip(characters, guess)))
if eval(equation):
return equation

if __name__ == '__main__':
import sys
for puzzle in sys.argv[1:]:
print(puzzle)
solution = solve(puzzle)
if solution:
print(solution)

Вы можете запустить программу из командной строки. В Linux это будет выглядеть так. (Выполнение программы может потребовать некоторого времени, зависящего от быстродействия вашего компьютера, а индикатор выполнения в программе отсутствует. Поэтому просто наберитесь терпения.)

you@localhost:~/diveintopython3/examples$ python3 alphametics.py "HAWAII + IDAHO + IOWA + OHIO == STATES"
HAWAII + IDAHO + IOWA + OHIO = STATES
510199 + 98153 + 9301 + 3593 == 621246
you@localhost:~/diveintopython3/examples$ python3 alphametics.py "I + LOVE + YOU == DORA"
I + LOVE + YOU == DORA
1 + 2784 + 975 == 3760
you@localhost:~/diveintopython3/examples$ python3 alphametics.py "SEND + MORE == MONEY"
SEND + MORE == MONEY
9567 + 1085 == 10652

Наши рекомендации