Декораторы. Полиморфизм, перегрузка операций. Наследование
Классы и перегрузка операций, повторение
Класс «vector» на скорую руку
- сложение векторов и с числом
- выяснить тип операнда
- умножение на вектор и на число
в т. ч. __rmul__
Не забыть делать именно вектор, type(self)(...)
Декораторы
- Преобразование списков... а функций?
- Применяются обычно для добавления к функции (многим функциям) однотипных действий
Пример — отладочная выдача параметров и возвращаемого значения
- ручная реализация обёртки
прозрачная обёртка вида fun = wrapper(fun)
⇒ @wrapper
этот wrapper можно вешать на много разных функций
1 """Пример декоратора, заставляющего функцию 2 выводить свои параметры и возвращаемое значение 3 """ 4 import sys 5 6 def dumper(f): 7 def dumpf(*ap, **an): 8 print("→", f, ap, an, file=sys.stderr) 9 res = f(*ap, **an) 10 print("←", res, file=sys.stderr) 11 return res 12 return dumpf 13 14 @dumper 15 def fun(a, b, c): 16 return a+b*2+c*3 17 18 #fun = dumper(fun) 19 20 print(fun(1,2,3)) 21 print(fun("Q","W","E"))
Пример: @integer (одно значение или последовательность)
1 # написать декоратор, превращающий вещественные параметры 2 # и вещественное возвращаемое значение функции в целочисленные 3 # (также список вещественных в список целых) 4 5 def integer(f): 6 def intf(*args): 7 newargs = (int(a) if type(a) is not int else a for a in args) 8 res = f(*newargs) 9 if type(res) in (list, tuple): 10 return type(res)(int(c) if type(c) is not int else c for c in res) 11 else: 12 return int(res) if type(res) is not int else res 13 return intf 14 15 @integer 16 def newrange(a,b,n): 17 return [a+(b-a)*i/(n-1) for i in range(n)] 18 19 print(newrange(10.1000,20.111,"7")) 20 21 @integer 22 def sqsum(*args): 23 return sum(a*a for a in args) 24 25 print(sqsum(3.345, 4.4102, 5.503))
- Декораторы классов и параметрические декораторы
Вывод изображений и текста в PyGame
- surface
загрузка изображений, .convert()
- Font, render()
Ввод текста в PyGame — ??
если будет время, pyginput.py
Инкапсуляция, наследование и полиморфизм
- Инкапсуляция — иерархизация пространств имён
- Наследование
пример теоретический
1 ndir = lambda obj: [l for l in dir(obj) if not l.startswith("_")] 2 3 class C: 4 value = None 5 def __init__(self, arg): 6 self.value = arg 7 self.doux = arg*2 8 def __str__(self): 9 return "C: {}/{}".format(self.value, self.doux) 10 def __add__(self, other): 11 return type(self)(self.value+other.value) 12 13 class D(C): 14 def __init__(self, arg): 15 C.__init__(self, arg) 16 self.another = arg 17 18 def newfun(self): 19 return self.doux 20 21 def __str__(self): 22 return "D: {}/{}/{}".format(self.value, self.doux, self.another) 23 24 class Fake: 25 value = 100500 26 doux = 500100 27 def __init__(self, *args): 28 pass 29 30 a = C(123) + C(567) 31 print(type(a),a) 32 33 b = D(432) + D(123) 34 print(type(b),b) 35 36 f = Fake() 37 print(C(100)+f) 38 print(C.__add__(f,f))
перепишем последнюю программу с классом Square так, чтобы он был унаследован от pygame.Rect
- Полиморфизм — в питоне сам собой по причине duck typing
Оконная система
- Класс «окно» — Rect + bg + show() (надо ли? + resize())
- Класс «окно с рамочкой»
- Класс «окно с заголовком»
- Класс «окно с рамочкой и заголовком» (двойное наследование)
- как их таскать или ресайзить
1 # Класс «окно» — size + bg + show(pos) 2 # Класс «окно с рамочкой» 3 # Класс «окно с заголовком» 4 # Класс «окно с рамочкой и заголовкам» (двойное наследование) 5 # как их таскать 6 7 import pygame 8 9 class BaseWindow: 10 '''Просто окно''' 11 def __init__(self, size, bgcolor): 12 self.size = size 13 self.color = pygame.Color(*bgcolor) 14 15 def collide(self, coords, pos): 16 return pygame.Rect(coords, self.size).collidepoint(pos) 17 18 def show(self, surface, pos): 19 '''Нарисовать окно на экране surface 20 по координатам pos''' 21 pygame.draw.rect(surface, self.color, (pos, self.size)) 22 23 class FramedWindow(BaseWindow): 24 BSIZE = 4 25 BCOLOR = (200,0,0) 26 27 def show(self, surface, pos): 28 BaseWindow.show(self, surface, pos) 29 pygame.draw.rect(surface, self.BCOLOR, (pos, self.size), self.BSIZE) 30 31 class DotWindow(BaseWindow): 32 CCOLOR = (0,0,200) 33 34 def show(self, surface, pos): 35 BaseWindow.show(self, surface, pos) 36 pygame.draw.ellipse(surface, self.CCOLOR, pygame.Rect(pos, self.size)) 37 38 class FDotWindow(FramedWindow, DotWindow): 39 def show(self, surface, pos): 40 FramedWindow.show(self, surface, pos) 41 DotWindow.show(self, surface, pos) 42 43 W, H = 640, 480 44 pygame.init() 45 screen = pygame.display.set_mode((W,H)) 46 47 Windows = [ [(10,10),BaseWindow((100,100),(0,0,100))], 48 [(40,110),FramedWindow((100,100),(0,100,100))], 49 [(110,20),BaseWindow((100,100),(100,0,100))], 50 [(210,220),DotWindow((100,100),(100,0,100))], 51 [(310,320),FDotWindow((100,100),(100,0,100))], 52 ] 53 Drag, NDrag = None, 0 54 55 while True: 56 e = pygame.event.wait() 57 if e.type == pygame.QUIT: 58 break 59 if e.type == pygame.MOUSEBUTTONDOWN: 60 for num,(pos,w) in enumerate(Windows): 61 if w.collide(pos, e.pos): 62 Drag, NDrag = w, num 63 if e.type == pygame.MOUSEMOTION and e.buttons[0]: 64 if Drag: 65 Windows[NDrag][0]=e.pos 66 if e.type == pygame.MOUSEBUTTONUP: 67 Drag = None 68 69 screen.fill((0,0,0)) 70 for pos,w in Windows: 71 w.show(screen, pos) 72 pygame.display.flip()
Структура аркадной (real-time) игровой программы
- Игровой мир
- Актёры
- События с ними и миром
- Свои единицы измерения
- Часы (как минимум одни), понятие такта
- Отображение мира
- Низкоуровневые события (мышь, клавиатура, таймер и т. п.)
- Координаты на экране
- Отрисовка
- Один такт работы:
- Аккумуляция низкоуровневых событий (иногда с некоторой непосредственной реакцией)
- Превращение их в события мира (в т. ч. выбор актёров, которым они предназначаются)
- Обработка событий актёрами
- Самостоятельная активность актёров и мира
- Отрисовка результата
Режимы работы игры (например, intro, gameplay и gameover) — такт выглядит так же, а обработчики разные, => общие названия методов
Игра в шары (к зачёту),
- минимум:
- Несколько шаров
- Замедление скорости
- Разлёт при соударении (массы одинаковы, а вот углы!)
- Бросаем любой (но не несём, т. е. несём не больше 1/5 сек, дальше отпускаем со скоростью, вычисляемой из event.rel)
- Считаем отскоки до полного останова
Конец игры, когда количество бросков > количество отскоков/коэффициент
- Дополнения
- Крутящиеся шары
- гравитация
- Входной и Gameover экраны, повышение коэффициента при усложнении уровня
- минимум: