Главная страница

Сам_себе_программист._Как_научиться_программировать_и_устроиться. Guide to Programming Professionally


Скачать 3.94 Mb.
НазваниеGuide to Programming Professionally
Дата25.02.2023
Размер3.94 Mb.
Формат файлаpdf
Имя файлаСам_себе_программист._Как_научиться_программировать_и_устроиться.pdf
ТипGuide
#954463
страница10 из 15
1   ...   7   8   9   10   11   12   13   14   15
Глава 14. Еще об объектно-
ориентированном программировании
Относитесь к своему коду как к поэзии и сводите его к абсолютному минимуму.
Илья Дорман
В этой главе я описываю дополнительные концепции, относящиеся к объектно- ориентированному программированию.
Переменные класса и переменные экземпляра
В Python классы являются объектами. Идея пошла из Smalltalk, влиятельного языка программирования, положившего начало объектно ориентированному программи- рованию. Каждый класс в Python — объект, являющийся экземпляром «типа» класса.
Python_ex258.py
1
class Square:
2
pass
3
print(Square)
>>
В этом примере класс Square является объектом, и вы его вывели.
У классов есть два типа переменных — переменные класса и переменные эк-
земпляра класса. Переменные, которые вам встречались до этого, были перемен- ными экземпляра класса, определяемыми синтаксисом self. _   =
 _  . Переменные экземпляра класса принадлежат объектам.
Python_ex259.py
1
class Rectangle():
2
def __init__(
self
, w, l):
3
self
.width
= w
4
self
.len
= l
5
def print_size(
self
):
6
print("""{} {}
7
""".format(
self
.width,
8
self
.len))
9
my_rectangle
= Rectangle(10, 24)
10
my_rectangle.print_size()
>> 10 24
В этом примере width и len — переменные экземпляра класса.

128
Часть II
Переменные класса принадлежат объекту, который Python создает для каж- дого определения класса, и объектам, которые они создают. Переменные класса определяются как обычные переменные (но определение осуществляется вну- три класса). Вы можете получить к ним доступ с помощью объектов класса и объ- ектов, созданных объектами класса. Вы получаете к ним доступ так же, как и к переменным экземпляра, добавляя перед именем переменной self. Перемен- ные класса полезны — они позволяют обмениваться данными между всеми экзем- плярами класса без полагания на глобальные переменные.
Python_ex260.py
1
class Rectangle():
2
recs
= []
3
def __init__(
self
, w, l):
4
self
.width
= w
5
self
.len
= l
6
self
.recs.append((
self
.width,
7
self
.len))
8
def print_size(
self
):
9
print("""{} {}
10
""".format(
self
.width,
11
self
.len))
12
r1
= Rectangle(10, 24)
13
r2
= Rectangle(20, 40)
14
r3
= Rectangle(100, 200)
15
print(Rectangle.recs)
>> [(10, 24), (20, 40), (100, 200)]
В этом примере вы добавили переменную recs в класс Rectangle. Вы опре- делили ее вне метода __init__, поскольку Python вызывает метод __init__ только при создании объекта, а вы хотите получить доступ к переменной класса, используя объект класса (что не вызывает метод __init__).
Затем вы создали три объекта Rectangle. Всякий раз, как создается объект
Rectangle
, код в методе __init__ присоединяет кортеж, содержащий шири- ну и длину нового объекта, к списку recs. С помощью этого кода каждый но- вый объект Rectangle автоматически добавляется в список recs. Благодаря использованию переменной класса вы смогли обменяться данными между раз- личными объектами, созданными классом, без необходимости использовать гло- бальную переменную.

129
Введение в объектно-ориентированное программирование
Магические методы
Каждый класс в Python наследуется от родительского класса Object. Python ис- пользует методы, унаследованные от Object, в различных ситуациях — напри- мер, когда вы выводите объект.
Python_ex261.py
1
class Lion:
2
def __init__(self, name):
3
self.name = name
4
lion
= Lion("")
5
print(lion)
>> <__main__.Lion object at 0x101178828>
Когда вы выводите объект Lion, Python вызывает в нем магический метод
__repr__
, унаследованный этим объектом от Object, и выводит то, что воз- вращает __repr__. Можно переопределить унаследованный метод __repr__, чтобы изменить его вывод.
Python_ex262.py
1
class Lion:
2
def __init__(
self
, name):
3
self
.name
= name
4
def __repr__(
self
):
5
return
self
.name
6
lion
= Lion("")
7
print(lion)
>> 
Поскольку вы переопределили метод __repr__, унаследованный от Object, и изменили его таким образом, чтобы он выводил имя объекта Lion, то когда вы выводите этот объект, выводится его имя — в этом случае  — вместо чего-то вроде <__main__.Lion object at 0x101178828>, что возвращал бы метод __repr__.
Операнды в выражении должны иметь магический метод, который опера- тор может использовать для оценки выражения. Например, в выражении 2 + 2 каждый объект целого числа имеет магический метод __add__, вызываемый
Python при оценке выражения. Если вы определите метод __add__ в классе, то сможете использовать создаваемые им объекты как операнды в выражении с оператором сложения.

130
Часть II
Python_ex263.py
1
class AlwaysPositive:
2
def __init__(
self
, number):
3
self
.n
= number
4
def __add__(
self
, other):
5
return abs(
self
.n
+
6
other.n)
7
x
= AlwaysPositive(-20)
8
y
= AlwaysPositive(10)
9
print(x
+ y)
>> 10
Объекты AlwaysPositive могут использоваться в качестве операндов в выражении с оператором сложения, поскольку вы определили метод __add__.
Когда Python определяет значение выражения с оператором сложения, он вы- зывает метод __add__ в первом объекте операнда, передает второй объект опе- ранда в __add__ в качестве параметра и возвращает результат.
В этом случае __add__ использует встроенную функцию abs
, чтобы вернуть абсолютное значение двух сложенных в выражении чисел. Поскольку вы опре- делили __add__ таким образом, два объекта AlwaysPositive в выражении с оператором сложения всегда будут давать абсолютную величину суммы двух объ- ектов, следовательно, результат выражения всегда будет положительным.
Ключевое слово is
Ключевое слово is возвращает значение True, если два объекта являются од- ним и тем же объектом, и False в противном случае.
Python_ex264.py
1
class Person:
2
def __init__(
self
):
3
self
.name
= '‚ '
4
bob
= Person()
5
same_bob
= bob
6
print(bob is same_bob)
7
another_bob
= Person()
8
print(bob is another_bob)
>> True
>> False

131
Введение в объектно-ориентированное программирование
Когда вы используете ключевое слово is в выражении с объектами bob и same_
bob в качестве операндов, выражение принимает значение True, так как обе пере- менные указывают на один и тот же объект Person. Когда вы создаете новый объ- ект Person и сравниваете его с исходным объектом bob, выражение принимает значение False, поскольку переменные указывают на различные объекты Person.
Используйте ключевое слово is, чтобы проверить, присвоено ли перемен- ной значение None.
Python_ex265.py
1
x
= 10 2
if x is
None
:
3
print("x   None :( ")
4
else:
5
print("x    None")
6
x
=
None
7
if x is
None
:
8
print("x   None")
9
else:
10
print("x   None :( ")
>> x    None
>> x   None :(
Словарь терминов
Закрытый метод: метод, к которому объект может получить доступ, а клиент — нет.
Закрытая переменная: переменная, к которой объект может получить доступ, а клиент — нет.
Открытая переменная: переменная, к которой клиент может получить доступ.
Переменная класса: переменные класса принадлежат объекту класса и объек- там, которые он создает.
Переменная экземпляра: переменная экземпляра принадлежит объекту.
Практикум
1. Добавьте переменную square_list в класс Square так, чтобы всякий раз, когда вы создаете новый объект Square, он добавлялся в список.
2. Измените класс
Square так, чтобы когда вы выводите объект Square, выво- дилось сообщение с длинами всех четырех сторон фигуры. Например, если вы создадите квадрат при помощи Square(29) и осуществите вывод, Python должен вывести строку 29 29 29 29.
3. Напишите функцию, которая принимает два объекта в качестве параметров и возвращает True, если они являются одним и тем же объектом, и False в противном случае.
Решения: chall_1.py — chall_3.py.

132
Часть II
Глава 15. Практикум. Часть II
Пока код не запустится, все это лишь разговоры.
Уорд Каннингем
В этой главе вы создадите популярную карточную игру «Пьяница». В этой игре каждый игрок берет из колоды по карте — побеждает тот, у которого карта стар- ше. При создании игры вы будете определять классы, представляющие карту, ко- лоду, игрока и, наконец, саму игру.
Карты
Ниже показан класс, который моделирует игру в карты.
Python_ex266.py
1
class Card:
2
suits
= ["",
3
"",
4
"",
5
"ƒ"]
6
values
= [
None
,
None
,"2", "3",
7
"4", "5", "6", "7",
8
"8", "9", "10",
9
"  ", " ",
10
"  ", "B "]
11
def __init__(
self
, v, s):
12
"""suit  value — Y  """
13
self
.value
= v
14
self
.suit
= s
15
def __lt__(
self
, c2):
16
if self
.value < c2.value:
17
return
True
18
if self
.value
== c2.value:
19
if self
.suit < c2.suit:
20
return
True
21
else:
22
return
False
23
return
False
24
def __gt__(
self
, c2):

133
Введение в объектно-ориентированное программирование
25
if self
.value > c2.value:
26
return
True
27
if self
.value
== c2.value:
28
if self
.suit > c2.suit:
29
return
True
30
else:
31
return
False
32
return
False
33
def __repr__(
self
):
34
v
= self
.values[
self
.value]
+ " of " \
35
+ self
.suits[
self
.suit]
36
return v
У класса Card есть две переменные класса, suits и values. suits — это список строковых значений, представляющих все масти, которые могут быть у карты: , , , ƒ. values — это список строковых значе- ний, представляющих различные номиналы карт: 2 — 10,   ,  ,   -

и B . Элементы под первыми двумя индексами в списке values являются
None
— для того, чтобы строки в списке совпадали с индексами, которые они представляют (так строка "2" в списке values имеет индекс 2).
Объекты Card имеют две переменные экземпляра — suit и value, каждая из них представлена целым числом. Вместе эти переменные экземпляра класса представляют вид карты объекта Card. К примеру, вы создаете 2  путем создания объекта Card и передачи в него параметров 2 (для значения) и 1 (для масти, поскольку индекс червей в списке suits — 1).
Определения в магических методах __lt__ и __gt__ позволяют вам срав- нивать два объекта Card в выражении при помощи операторов больше и мень- ше. Код в этих методах выясняет, больше или меньше карта, чем другая карта, пе- реданная в качестве параметра. Код в магических методах также обрабатывает ситуации, когда значение карт одинаковое, например, если обе карты — десятки.
В таком случае методы используют масти, чтобы избежать ничьей. Масти упоря- дочены в списке suits по возрастанию силы — самая сильная масть располагает- ся последней, то есть она назначена наибольшему индексу, а самая слабая масть назначена наименьшему индексу.
Python_ex267.py
1
card1
= Card(10, 2)
2
card2
= Card(11, 3)
3
print(card1 < card2)
>> True

134
Часть II
Python_ex268.py
1
card1
= Card(10, 2)
2
card2
= Card(11, 3)
3
print(card1 > card2)
>> False
Последний метод в классе Card — магический метод __repr__
. Он исполь- зует переменные экземпляра value и suit для нахождения значения и масти карты в списках values и suit, и возвращает их, чтобы вы могли вывести карту, которую представляет объект Card.
Python_ex269.py
1
card
= Card(3, 2)
2
print(card)
>> 3 
Колода
Теперь нужно определить класс, представляющий колоду карт.
Python_ex270.py
1
from random import shuf e
2
class Deck:
3
def __init__(
self
):
4
self
.cards
= []
5
for i in range(2, 15):
6
for j in range(4):
7
self
.cards.append(Card(i, j))
8
shuf e(
self
.cards)
9
def rm_card(
self
):
10
if len(
self
.cards) == 0:
11
return
12
return self
.cards.pop()
Когда вы создаете объект Deck, два цикла for в __init__ создают объек- ты, представляющие все карты в 52-карточной колоде, и добавляют их в список cards
. Первый цикл перебирает от 2 до 15, поскольку первое значение для кар- ты — это 2, а последнее — 14 (туз). При каждом прохождении внутреннего цикла создается новая карта путем использования целого числа из внешнего цикла в качестве значения (например, 14 для туза) и целого числа из внутреннего цик- ла в качестве масти (например, 2 для червей). Так создаются 52 карты, по карте

135
Введение в объектно-ориентированное программирование на каждую комбинацию масти и значения. После того как метод создает карты, метод shuf e из модуля random случайным образом перемешивает элементы в списке cards, имитируя перетасовку колоды карт.
У нашей колоды есть еще один метод, rm_card, который изымает и возвра- щает карту из списка cards, или возвращает None, если список пуст. Вы можете использовать класс Deck для создания новой колоды карт и вывода каждой кар- ты в ней.
Python_ex271.py
1
deck
= Deck()
2
for card in deck.cards:
3
print(card)
>> 4 
>> 8 

Игрок
Нужен класс для представления каждого игрока, чтобы отслеживать их карты и количество выигранных ими раундов.
Python_ex272.py
1
class Player:
2
def __init__(
self
, name):
3
self
.wins = 0 4
self
.card = None
5
self
.name = name
У класса Player есть три переменных экземпляра: wins для отслеживания количества раундов, выигранных игроком; card для представления карты, кото- рую в данный момент держит игрок; name для отслеживания имени игрока.
Игра
Наконец, вы создаете класс, представляющий игру.
Python_ex273.py
1
class Game:
2
def __init__(
self
):
3
name1
= input("   1: ")
4
name2
= input("   2: ")
5
self
.deck
= Deck()
6
self
.p1
= Player(name1)
7
self
.p2
= Player(name2)

136
Часть II
8
def wins(
self
, winner):
9
w
= "{} B    "
10
w
= w.format(winner)
11
print(w)
12
def draw(
self
, p1n, p1c, p2n, p2c):
13
d
= "{}   {}, {}   {}"
14
d
= d.format(p1n, p1c, p2n, p2c)
15
print(d)
16
def play_game(
self
):
17
cards
= self
.deck.cards
18
print(" G !")
19
while len(cards) >= 2:
20
m
= "$ &   G  . $ & WW W
 *    ."
21
response
= input(m)
22
if response == '':
23
break
24
p1c
= self
.deck.rm_card()
25
p2c
= self
.deck.rm_card()
26
p1n
= self
.p1.name
27
p2n
= self
.p2.name
28
self
.draw(p1n, p1c, p2n, p2c)
29
if p1c > p2c:
30
self
.p1.wins
+= 1 31
self
.wins(
self
.p1.name)
32
else:
33
self
.p2.wins
+= 1 34
self
.wins(
self
.p2.name)
35
win
= self
.winner(
self
.p1, self.p2)
36
print("R   . {}  !".format(win))
37
def winner(
self
, p1, p2):
38
if p1.wins > p2.wins:
39
return p1.name
40
if p1.wins < p2.wins:
41
return p2.name
42
return "$!"

137
Введение в объектно-ориентированное программирование
Когда вы создаете объект игры, Python вызывает метод __init__, а функ- ция input принимает имена двух игроков и сохраняет их в переменных name1 и name2. Затем вы создаете новый объект Deck, сохраняете его в переменной эк- земпляра класса deck и создаете два объекта Player, используя имена в name1 и name2.
Метод play_game в классе Game начинает игру. В методе есть цикл, который продолжает ведение игры до тех пор, пока в колоде остается две или более кар- ты, и пока переменная response не примет значение . При каждом прохожде- нии цикла вы назначаете переменную response пользовательскому вводу. Игра продолжается до тех пор, пока либо пользователь не введет "", либо в колоде останется менее двух карт.
При каждом прохождении цикла вынимаются две карты, и метод play_
game присваивает первую карту p1, а вторую — p2. Затем он выводит имя каждо- го игрока и карты, которую он вынул, сравнивает две карты, выясняя, какая из них старше, увеличивает значение переменной экземпляра wins для игрока со старшей картой и выводит сообщение о том, кто победил.
У класса Game также есть метод winner, который принимает два объекта игроков, «смотрит» на количество раундов, выигранных каждым игроком, и воз- вращает игрока, который выиграл больше раундов.
Когда в объекте Deck заканчиваются карты, метод play_game выводит со- общение о том, что игра окончена, вызывает метод winner (передавая в него p1 и p2) и выводит сообщение с итогом игры — именем победившего игрока.
«Пьяница»
Ниже приведен полный код игры.
Python_ex274.py
1
from random import shuf e
2
class Card:
3
suits
= ["",
4
"",
5
"",
6
"ƒ"]
7
values
= [
None
,
None
,"2", "3",
8
"4", "5", "6", "7",
9
"8", "9", "10",
10
"  ", " ",
11
"  ", "B "]
12
def __init__(
self
, v, s):
13
"""suit  value — Y  """

138
Часть II
14
self
.value
= v
15
self
.suit
= s
16
def __lt__(
self
, c2):
17
if self
.value < c2.value:
18
return
True
19
if self
.value
== c2.value:
20
if self
.suit < c2.suit:
21
return
True
22
else:
23
return
False
24
return
False
25
def __gt__(
self
, c2):
26
if self
.value > c2.value:
27
return
True
28
if self
.value
== c2.value:
29
if self
.suit > c2.suit:
30
return
True
31
else:
32
return
False
33
return
False
34
def __repr__(
self
):
35
v
= self
.values[
self
.value]
+ " " \
36
+ self
.suits[
self
.suit]
37
return v
38
class Deck:
39
def __init__(
self
):
40
self
.cards
= []
41
for i in range(2, 15):
42
for j in range(4):
43
self
.cards.append(Card(i, j))
44
shuf e(
self
.cards)
45
def rm_card(
self
):
46
if len(
self
.cards)
== 0:
47
return
48
return self
.cards.pop()

139
Введение в объектно-ориентированное программирование
50
class Player:
51
def __init__(
self
, name):
52
self
.wins
= 0 53
self
.card
=
None
54
self
.name
= name
55
class Game:
56
def __init__(
self
):
57
name1
= input("   1: ")
58
name2
= input("   2: ")
59
self
.deck
= Deck()
60
self
.p1
= Player(name1)
61
self
.p2
= Player(name2)
62
def wins(
self
, winner):
63
w
= "{} B    "
64
w
= w.format(winner)
65
print(w)
66
def draw(
self
, p1n, p1c, p2n, p2c):
67
d
= "{}   {}, {}   {}"
68
d
= d.format(p1n, p1c, p2n, p2c)
69
print(d)
70
def play_game(
self
):
71
cards
= self
.deck.cards
72
print(" G !")
73
while len(cards) >= 2:
74
m
= "$ &   G  . $ & WW W
 *    ."
75
response
= input(m)
76
if response == '':
77
break
78
p1c
= self
.deck.rm_card()
79
p2c
= self
.deck.rm_card()
80
p1n
= self
.p1.name
81
p2n
= self
.p2.name
82
self
.draw(p1n, p1c, p2n, p2c)
83
if p1c > p2c:
84
self
.p1.wins
+= 1

85
self
.wins(self.p1.name)
86
else:
87
self
.p2.wins
+= 1 88
self
.wins(
self
.p2.name)
89
win
= self
.winner(
self
.p1, self.p2)
90
print("R   .{}  !".format(win))
91
def winner(
self
, p1, p2):
92
if p1.wins > p2.wins:
93
return p1.name
94
if p1.wins < p2.wins:
95
return p2.name
96
return "$!"
97
game
= Game()
98
game.play_game()
>> "   1: "


141
1   ...   7   8   9   10   11   12   13   14   15


написать администратору сайта