8 | Мой первый сайт — {{ isclever|random }} (а вообще может быть
9 | {%- for line in isclever %}
10 | {{ line -}}
11 | {% endfor %}), но такой милый!
12 | Вот этот кусочек страницы общий для многих (всех) страниц.
13 | Дальше пойдут уже разные.
14 |
15 |
16 | {% block content %}
17 | -- This should not be rendered --
18 | {% endblock %}
19 |
20 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/multiprocessing_effect.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import time
5 | import multiprocessing
6 |
7 | def n_randoms(n):
8 | s = 123
9 | m = 2**16+1
10 | a = 75
11 | c = 74
12 | for i in range(n):
13 | s = (a*s + c) % m
14 |
15 | return s
16 |
17 |
18 | def test_all(pool):
19 | l = pool.map(n_randoms, range(1_000_000, 1_000_000 + 50))
20 | return l
21 |
22 |
23 | if __name__ == '__main__':
24 | pool = multiprocessing.Pool()
25 | t0 = time.time()
26 | print(test_all(pool))
27 | print("Time spent:", time.time() - t0)
28 | else:
29 | print("__name__:", __name__)
30 |
--------------------------------------------------------------------------------
/docs/examples/01_5.Turtle/fractal2.py:
--------------------------------------------------------------------------------
1 | import turtle as tl
2 |
3 | def draw_fractal(size):
4 | if size >= 5:
5 | tl.pensize(max(size / 50, 1))
6 | tl.forward(size)
7 | tl.left(35)
8 | draw_fractal(size / 2)
9 | tl.right(65)
10 | draw_fractal(size / 1.5)
11 | tl.left(30)
12 | tl.penup()
13 | tl.backward(size)
14 | tl.pendown()
15 | else:
16 | tl.pensize(3)
17 | tl.dot()
18 |
19 | size = 300
20 |
21 | tl.delay(1) # уменьшение задержки для скорости
22 | tl.penup()
23 | tl.color('green')
24 | tl.goto(0, -size * 2)
25 | tl.setheading(90)
26 | tl.pendown()
27 |
28 | draw_fractal(size)
29 | tl.done()
30 |
--------------------------------------------------------------------------------
/docs/slides/common/media/fonts/Alegreya/alegreya.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Alegreya';
3 | src: url(Alegreya-Regular.ttf) format('truetype');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'Alegreya';
10 | src: url(Alegreya-Bold.ttf) format('truetype');
11 | font-weight: bold;
12 | font-style: normal;
13 | }
14 |
15 | @font-face {
16 | font-family: 'Alegreya';
17 | src: url(Alegreya-Italic.ttf) format('truetype');
18 | font-weight: normal;
19 | font-style: italic;
20 | }
21 |
22 | @font-face {
23 | font-family: 'Alegreya';
24 | src: url(Alegreya-BoldItalic.ttf) format('truetype');
25 | font-weight: bold;
26 | font-style: italic;
27 | }
28 |
--------------------------------------------------------------------------------
/docs/slides/common/media/fonts/OpenSans/opensans.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'OpenSans';
3 | src: url(OpenSans-Regular.ttf) format('truetype');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'OpenSans';
10 | src: url(OpenSans-Bold.ttf) format('truetype');
11 | font-weight: bold;
12 | font-style: normal;
13 | }
14 |
15 | @font-face {
16 | font-family: 'OpenSans';
17 | src: url(OpenSans-Italic.ttf) format('truetype');
18 | font-weight: normal;
19 | font-style: italic;
20 | }
21 |
22 | @font-face {
23 | font-family: 'OpenSans';
24 | src: url(OpenSans-BoldItalic.ttf) format('truetype');
25 | font-weight: bold;
26 | font-style: italic;
27 | }
28 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/coroutines_sync.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import time
5 |
6 | def c1():
7 | print("c1-0")
8 | time.sleep(0.5)
9 | yield "a"
10 | print("c1-1")
11 | time.sleep(0.5)
12 | yield "b"
13 | print("c1-2")
14 | time.sleep(0.5)
15 | yield "c"
16 | print("c1-3")
17 |
18 | def c2():
19 | for n in range(3):
20 | print("c2-%d" % (n))
21 | time.sleep(0.5)
22 | yield n
23 | print("c2-3")
24 |
25 | def test2coroutines1():
26 | r1 = c1()
27 | r2 = c2()
28 | for i in range(3):
29 | print("R1:", next(r1), "R2:", next(r2))
30 |
31 | def test2coroutines2():
32 | for e1, e2 in zip(c1(), c2()):
33 | ... # print("R1:", e1, "R2:", e2)
34 |
35 | if __name__=='__main__':
36 | # test2coroutines1()
37 | test2coroutines2()
38 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/mpi4py/mpitest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from mpi4py import MPI
4 | import math
5 | import time
6 |
7 | comm = MPI.COMM_WORLD
8 | cluster_size = comm.Get_size()
9 | process_rank = comm.Get_rank()
10 |
11 | def slow_function(arg):
12 | s = 0.0
13 | for i in range(3_000_000):
14 | s += math.sin(i*arg) * process_rank
15 | return s
16 |
17 | if process_rank == 0:
18 | root_args = list(range(cluster_size))
19 | else:
20 | root_args = None
21 |
22 | # Взять исходные данные root_args у 0-го, раскидать по всем процессам
23 | local_args = comm.scatter(root_args, root=0)
24 |
25 | # У всех вызвать функцию от данных
26 | t0 = time.time()
27 | local_response = slow_function(local_args)
28 | t1 = time.time()
29 |
30 | # Собрать у всех и отдать в root_response 0-му
31 | root_response = comm.gather(local_response, root=0)
32 |
33 | if process_rank == 0:
34 | print("Time spent:", t1 - t0)
35 | print(root_response)
36 |
--------------------------------------------------------------------------------
/docs/examples/06.GUI/main_dialog_qt6.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # pip install pyqt6 pyqt6_tools
4 |
5 | import os
6 | import sys
7 |
8 | from PyQt6 import QtWidgets, uic
9 |
10 | _scriptdir = os.path.dirname(os.path.realpath(__file__))
11 | uifile = os.path.join(_scriptdir, 'ui', 'main_dialog.ui')
12 |
13 | class MainDialog(*uic.loadUiType(uifile)):
14 | def __init__(self, parent=None):
15 | super().__init__(parent)
16 | self.setupUi(self)
17 | self.bindEvents()
18 |
19 | def bindEvents(self):
20 | self.okButton.clicked.connect(self.close)
21 | self.helloButton.clicked.connect(self.hello)
22 |
23 | def hello(self):
24 | self.helloLabel.setText(
25 | "Привет" + ("!" if self.excCheckBox.isChecked() else "")
26 | )
27 |
28 |
29 | if __name__ == '__main__':
30 | app = QtWidgets.QApplication(sys.argv)
31 | w = MainDialog()
32 | w.show()
33 |
34 | sys.exit(app.exec())
35 |
--------------------------------------------------------------------------------
/docs/examples/13.Evolutionary_Moon_Lander/try_something.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from __future__ import annotations
3 | from typing import List
4 |
5 | import numpy as np
6 | from numpy import array as vec
7 | import matplotlib.pyplot as plt
8 |
9 | import model
10 | import captain
11 |
12 |
13 | def trackship(m: model.Model) -> List[vec]:
14 | poss = []
15 | while m.step():
16 | poss.append(m.spaceship.position.copy())
17 | return poss
18 |
19 | if __name__ == '__main__':
20 | rel = model.Relief("surface_heights.csv")
21 |
22 | m = model.Model(
23 | rel,
24 | model.Spaceship(1000.0, vec([20.0, 0.0]), vec([0.0, 200.0])),
25 | captain.CarefulCaptain()
26 | # captain.BraveCaptain()
27 | )
28 |
29 | xs = np.arange(0.0, rel.get_width())
30 | plt.plot(xs, [rel.get_height(x) for x in xs])
31 |
32 | poss = trackship(m)
33 | tx, ty = tuple(zip(*poss))
34 | plt.plot(tx, ty)
35 |
36 | plt.show()
37 |
--------------------------------------------------------------------------------
/docs/examples/06.GUI/main_dialog_pyside6.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # pip install pyside6
4 |
5 | import os
6 | import sys
7 |
8 | from PySide6 import QtWidgets
9 | from PySide6.QtUiTools import loadUiType
10 |
11 | _scriptdir = os.path.dirname(os.path.realpath(__file__))
12 | uifile = os.path.join(_scriptdir, 'ui', 'main_dialog.ui')
13 |
14 | class MainDialog(*loadUiType(uifile)):
15 | def __init__(self, parent=None):
16 | super().__init__(parent)
17 | self.setupUi(self)
18 | self.bindEvents()
19 |
20 | def bindEvents(self):
21 | self.okButton.clicked.connect(self.close)
22 | self.helloButton.clicked.connect(self.hello)
23 |
24 | def hello(self):
25 | self.helloLabel.setText(
26 | "Привет" + ("!" if self.excCheckBox.isChecked() else "")
27 | )
28 |
29 |
30 | if __name__ == '__main__':
31 | app = QtWidgets.QApplication(sys.argv)
32 | w = MainDialog()
33 | w.show()
34 |
35 | sys.exit(app.exec())
36 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/coroutines_async_311.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.11
2 |
3 | # https://makina-corpus.com/blog/metier/2015/python-http-server-with-the-new-async-await-syntax
4 |
5 | # Python >= 3.11
6 |
7 | import asyncio
8 |
9 | async def c1():
10 | print("c1-0")
11 | await asyncio.sleep(1)
12 | print("c1-1")
13 | await asyncio.sleep(1)
14 | print("c1-2")
15 | await asyncio.sleep(1)
16 | print("c1-3")
17 |
18 | async def c2():
19 | for n in range(3):
20 | await asyncio.sleep(1)
21 | print("c2-%d" % (n))
22 | print("c2-3")
23 |
24 |
25 | async def c3():
26 | for n in range(3):
27 | await asyncio.sleep(1)
28 | print("c3-%d" % (n))
29 | print("c3-3")
30 |
31 | async def test3coroutines():
32 | async with asyncio.TaskGroup() as tg:
33 | for c in [c1(), c2(), c3()]:
34 | tg.create_task(c)
35 |
36 | if __name__=='__main__':
37 | loop = asyncio.get_event_loop()
38 | loop.run_until_complete(test3coroutines())
39 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/coroutines_async_310.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.10
2 | # -*- coding: utf-8 -*-
3 |
4 | # Python <= 3.10
5 |
6 | # https://makina-corpus.com/blog/metier/2015/python-http-server-with-the-new-async-await-syntax
7 |
8 | import asyncio
9 |
10 | @asyncio.coroutine
11 | def c1():
12 | print("c1-0")
13 | yield from asyncio.sleep(1)
14 | print("c1-1")
15 | yield from asyncio.sleep(1)
16 | print("c1-2")
17 | yield from asyncio.sleep(1)
18 | print("c1-3")
19 |
20 |
21 | @asyncio.coroutine
22 | def c2():
23 | for n in range(3):
24 | yield from asyncio.sleep(1)
25 | print("c2-%d" % (n))
26 | print("c2-3")
27 |
28 |
29 | async def c3():
30 | for n in range(3):
31 | await asyncio.sleep(1)
32 | print("c3-%d" % (n))
33 | print("c3-3")
34 |
35 | @asyncio.coroutine
36 | def test3coroutines():
37 | yield from asyncio.wait([
38 | c1(),
39 | c2(),
40 | c3()
41 | ])
42 |
43 | if __name__=='__main__':
44 | loop = asyncio.get_event_loop()
45 | loop.run_until_complete(test3coroutines())
46 |
--------------------------------------------------------------------------------
/docs/slides/common/media/fonts/Merriweather/Merriweather.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Merriweather';
3 | src: url(Merriweather-Regular.ttf) format('truetype');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'Merriweather';
10 | src: url(Merriweather-Italic.ttf) format('truetype');
11 | font-weight: normal;
12 | font-style: italic;
13 | }
14 |
15 | @font-face {
16 | font-family: 'Merriweather';
17 | src: url(Merriweather-Bold.ttf) format('truetype');
18 | font-weight: bold;
19 | font-style: normal;
20 | }
21 |
22 | @font-face {
23 | font-family: 'Merriweather';
24 | src: url(Merriweather-BoldItalic.ttf) format('truetype');
25 | font-weight: bold;
26 | font-style: italic;
27 | }
28 |
29 | @font-face {
30 | font-family: 'Merriweather';
31 | src: url(Merriweather-Black.ttf) format('truetype');
32 | font-weight: 900;
33 | font-style: normal;
34 | }
35 |
36 | @font-face {
37 | font-family: 'Merriweather';
38 | src: url(Merriweather-BlackItalic.ttf) format('truetype');
39 | font-weight: 900;
40 | font-style: italic;
41 | }
42 |
--------------------------------------------------------------------------------
/docs/examples/04.web/sample_app.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | # A very simple Flask Hello World app for you to get started with...
5 |
6 | import flask
7 | from flask import Flask, request
8 |
9 | app = Flask(__name__, static_folder="static", static_url_path="", template_folder="templates")
10 |
11 |
12 | @app.context_processor
13 | def inject_globals():
14 | return {
15 | "isclever": [
16 | "глупый",
17 | "умный",
18 | "маленький"
19 | ]
20 | }
21 |
22 | @app.route('/hello/')
23 | @app.route('/hello')
24 | def hello_world(text=None):
25 | return 'Just a plain text: "Hello from Flask!"' + (' With path .../' + text if text else '')
26 |
27 |
28 | @app.route('/')
29 | def root():
30 | return flask.render_template(
31 | 'index.html'
32 | )
33 |
34 |
35 | @app.route('/anon_game', methods = ['GET', 'POST'])
36 | def hello_name():
37 | if request.method == 'GET':
38 | name_param=request.args.get('name')
39 | elif request.method == 'POST':
40 | name_param=request.form.get('name')
41 |
42 | if name_param is None:
43 | name_param="Анонимус"
44 |
45 | return flask.render_template(
46 | 'hello_anon.html',
47 | name=name_param,
48 | method=request.method
49 | )
50 |
51 | if __name__ == '__main__':
52 | app.run(debug = True)
53 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/mpi4py/mpitest_arbitrary_data_size.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from mpi4py import MPI
4 | import math
5 | import itertools
6 | import time
7 |
8 | comm = MPI.COMM_WORLD
9 | cluster_size = comm.Get_size()
10 | process_rank = comm.Get_rank()
11 |
12 | def slow_function(arg_list):
13 |
14 | def compute(arg):
15 | s = 0.0
16 | for i in range(3_000_000):
17 | s += math.sin(i*arg) * process_rank
18 | return s
19 |
20 | return [(-a * process_rank, compute(a)) for a in arg_list]
21 |
22 | if process_rank == 0:
23 | # Пусть у нас есть сколько-то данных, которые нам надо обработать независимо от того, сколько процессов считает
24 | data_size = 100
25 | source_data = [1] * data_size
26 |
27 | # Но их надо раскидать по cluster_size процессам, примерно равномерно
28 | root_args = [ source_data[round(rank * data_size / cluster_size) : round((rank + 1) * data_size / cluster_size) ] for rank in range(cluster_size) ]
29 | else:
30 | root_args = None
31 |
32 | # Взять исходные данные root_args у 0-го, раскидать по всем процессам
33 | local_args = comm.scatter(root_args, root=0)
34 |
35 | # У всех вызвать функцию от данных
36 | t0 = time.time()
37 | local_response = slow_function(local_args)
38 | t1 = time.time()
39 |
40 | # Собрать у всех и отдать в root_response 0-му
41 | root_response = comm.gather(local_response, root=0)
42 |
43 | if process_rank == 0:
44 | result = list(itertools.chain.from_iterable(root_response))
45 | print("Time spent:", t1 - t0)
46 | print(result)
47 |
--------------------------------------------------------------------------------
/docs/examples/06.GUI/main_dialog_qt6_autobind_task.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # pip install pyqt6 pyqt6_tools
4 |
5 | from functools import partial
6 | import os
7 | import sys
8 |
9 | from PyQt6 import QtWidgets, uic
10 | from PyQt6.QtCore import pyqtBoundSignal
11 |
12 | _scriptdir = os.path.dirname(os.path.realpath(__file__))
13 | uifile = os.path.join(_scriptdir, 'ui', 'main_dialog.ui')
14 |
15 | class AutoBindingWidget:
16 | def autoBindEvents(self):
17 | for wn, w in self.__dict__.items():
18 | match type(w):
19 | case QtWidgets.QPushButton: # Сделать для любого Виджета
20 | # И поискать события в w.__dir__()
21 | clh = type(self).__dict__.get(wn + '_clicked')
22 | if clh:
23 | print(type(w.clicked)) # # И для всех сигналов
24 | w.clicked.connect(partial(clh, self))
25 |
26 | class MainDialog(*uic.loadUiType(uifile), AutoBindingWidget):
27 | def __init__(self, parent=None):
28 | super().__init__(parent)
29 | self.setupUi(self)
30 | self.autoBindEvents()
31 |
32 | def okButton_clicked(self):
33 | self.close()
34 |
35 | def helloButton_clicked(self):
36 | self.helloLabel.setText(
37 | "Привет" + ("!" if self.excCheckBox.isChecked() else "")
38 | )
39 |
40 |
41 | if __name__ == '__main__':
42 | app = QtWidgets.QApplication(sys.argv)
43 | w = MainDialog()
44 | w.show()
45 |
46 | sys.exit(app.exec())
47 |
--------------------------------------------------------------------------------
/docs/examples/04.web/README.md:
--------------------------------------------------------------------------------
1 | # Простое web-приложение с использованием Flask
2 |
3 | Нам потребуется: `pip install flask`
4 |
5 | ## Общая структура приложения
6 |
7 | [`sample_app.py`](sample_app.py) содержит набор функций, которые при помощи декораторов регистрируются, как обработчики HTTP-запросов.
8 | Запрос имеет следующий вид: `протокол://сервер[:порт]/путь?параметр1=значение1&параметр2=значение2`.
9 | Например, `https://www.google.com/search?q=python`.
10 |
11 | Параметры могут быть частью URL, как в примере выше (для GET-запростов), или передаваться в заголовках запроса (для POST-запросов).
12 | Во втором случае их не видно в строке адреса, поэтому такой способ предпочтительнее для секрктных данных.
13 |
14 | ## Статические файлы
15 |
16 | В каталоге `/static` (это настраивается) расположены файлы, которые сервер будет отдавать, «как есть». Это полезно для файлов,
17 | которые не изменяются, в т.ч. для больших файлов — архивов, видео и т.д.
18 |
19 | ## Шаблоныштву
20 |
21 | Flask использует язык шаблонов Jinja2. Язык поддерживает «наследование». Т.е. общий макет сайта можно задать отдельно
22 | (см. [base.html](templates/base.html)), а уже конкретные страницы (см. [index.html](templates/index.html) и [name.html](templates/name.html)) могут доопределять или переопределять «страниц-предков».
23 |
24 | Генерация веб-страницы по шаблону вызывается при помощи функции `flask.render_template`.
25 |
26 | ## Развёртывание
27 |
28 | Конечно можно сервер развернуть самому, но для этого нужен, собственно, сервер. Более «профессиональный» вариант — использовать хостинг, например, https://www.pythonanywhere.com/ — они, как и многие другие, позволяют маленькие веб-приложения хостить бесплатно.
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | #
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # pyenv
77 | .python-version
78 |
79 | # celery beat schedule file
80 | celerybeat-schedule
81 |
82 | # SageMath parsed files
83 | *.sage.py
84 |
85 | # Environments
86 | .env
87 | .venv
88 | env/
89 | venv/
90 | ENV/
91 | env.bak/
92 | venv.bak/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
--------------------------------------------------------------------------------
/docs/examples/05.multiple_inheritance/multiple_unheritance_demo.py:
--------------------------------------------------------------------------------
1 | class SimpleA:
2 | def __init__(self, p):
3 | print(f'SimpleA constructor for object of {type(self)} with param {p}')
4 |
5 | class SimpleB(SimpleA):
6 | def __init__(self, p):
7 | super().__init__(p)
8 | print(f'SimpleB constructor for object of {type(self)} with param {p}')
9 |
10 | class SimpleC(SimpleA):
11 | def __init__(self, p):
12 | super().__init__(p + 1)
13 | print(f'SimpleC constructor for object of {type(self)} with param {p}')
14 |
15 | class SimpleD(SimpleB, SimpleC):
16 | def __init__(self):
17 | super().__init__(1)
18 | print(f'SimpleD constructor for object of {type(self)}')
19 |
20 | print("Simple case:")
21 | sd = SimpleD()
22 | print("============")
23 |
24 | class ComplicatedC1(SimpleA):
25 | def __init__(self, vc1, vc2):
26 | super().__init__()
27 | print(f'ComplicatedC1 constructor for object of {type(self)} with params {vc1}, {vc2}')
28 |
29 | class ComplicatedC2(SimpleA):
30 | def __init__(self, vc1, vc2):
31 | super().__init__()
32 | print(f'ComplicatedC2 constructor for object of {type(self)} with params {vc1}, {vc2}')
33 |
34 | class ComplicatedD(SimpleB, ComplicatedC1, ComplicatedC2):
35 | def __init__(self):
36 | try:
37 | SimpleB.__init__(self, -1)
38 | except TypeError as te:
39 | print(f"Eah, everything goes as [un]expected: {te}")
40 | print("See https://stackoverflow.com/a/34885285/539470")
41 | # super(ComplicatedC1, self).__init__('[vc1]', '[vc2]')
42 | # super(ComplicatedC2, self).__init__('[vc1]', '[vc2]')
43 | print(f'ComplicatedD constructor for object of {type(self)}')
44 |
45 | print("Complicated case:")
46 | cd = ComplicatedD()
47 | print("============")
48 |
--------------------------------------------------------------------------------
/docs/examples/01.quality/test_gauss.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env -S python3
2 |
3 | from unicodedata import name
4 | import pytest
5 |
6 | N=100
7 | SMALL = 1e-5
8 |
9 | @pytest.fixture(scope='module', name='solver_function')
10 | def import_solver_funciton():
11 | import impl_gauss as ig
12 | if 'vgauss' in ig.__dict__:
13 | return ig.vgauss
14 | elif 'vector_gauss' in ig.__dict__:
15 | return ig.vector_gauss
16 | elif 'gauss' in ig.__dict__:
17 | return ig.gauss
18 | else:
19 | assert False, "No (v)gauss function(s)"
20 |
21 | @pytest.mark.parametrize('n,small', [(N,SMALL)])
22 | def test_sane_error(solver_function, n, small):
23 | from numpy.linalg import norm, det
24 | from numpy.linalg import solve as solve_out_of_the_box
25 | from numpy.random import uniform
26 |
27 | # Сгенерируем хорошо обусловленную систему
28 | while True:
29 | a = uniform(0.0, 1.0, (n, n))
30 | b = uniform(0.0, 1.0, (n, ))
31 |
32 | d = det(a)
33 | if abs(d) <= small: # Определитель маленький — плохо
34 | # print(f"det: {d}")
35 | continue # Попробуем ещё
36 | if d < 0.0: # Отрицательный — поменяем знак
37 | # print(f"det: {d}")
38 | a = -a
39 |
40 | try:
41 | oob_solution = solve_out_of_the_box(a, b)
42 | except Exception as e: # Всё-таки система плохая
43 | # print(f"exc: {e}")
44 | continue # Попробуем ещё
45 |
46 | sb = a @ oob_solution
47 | if norm(sb - b, ord=1) > small:
48 | # print("Bad solution...")
49 | continue # Всё же не очень система получилась =)
50 |
51 | break # Всё, считаем, что мы случайно сгенерировали хорошую систему
52 |
53 | tested_solution = solver_function(a, b)
54 | e = norm(tested_solution - oob_solution, ord=1)
55 | assert e <= small, f"Error is too big: {e}"
56 |
--------------------------------------------------------------------------------
/docs/slides/02.5.quality.md:
--------------------------------------------------------------------------------
1 |
2 | Качество и работоспособность кода
3 |
4 | # Нам потребуются
5 |
6 | ```
7 | pip install pylint nbqa mypy
8 | ```
9 |
10 | = = = = = =
11 |
12 | # Статические анализаторы
13 |
14 | - - - - - -
15 |
16 | ## Что это вообще такое?
17 |
18 | * [Статические анализаторы кода](https://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis) — «придирчивые инструменты»,
19 | которые находят в программах, не запуская их, реальные и потенциальные дефекты — от ошибок, до нарушения стандартов и рекомендаций
20 | * PyLint — один из популярных общих статических анализаторов для Python
21 | * NBQA — обёртка для PyLint и других инструментов лдя работы с блокнотами
22 | * MyPy — один из популярных статических анализаторов соответствия типов для Python
23 |
24 | - - - - - - -
25 |
26 | ## Примеры
27 |
28 | См. [пример](../examples/01.quality/rabin_karp_with_tests.py)
29 |
30 | * PyLint заставляет писать докумментацию (хотя в настройках можно отключить)
31 | * MyPy почти ничего не заставляет, потому что мы тут не использовали аннотации типов
32 | * NBQA тоже нет, потому что не блокнот =)
33 |
34 | = = = = = =
35 |
36 | # Модульные тесты
37 |
38 | - - - - - -
39 |
40 | ## Предпосылки
41 |
42 | * Код изменчив
43 | * Точный статический анализ затруднителен
44 | * Хотя свойства алгоритмов и программ в целом возможно доказывать
45 | * Даже для статического анализа надо специфицировать результаты
46 |
47 | - - - - - -
48 |
49 | ## Модульный тест
50 |
51 | * Вид теста, при котором функциональность программного модуля вызывается с заранее определёнными входными данными
52 | * Изначально предназначен для обнаружения регресса
53 | * Сейчас иногда пишется до «боевого» кода
54 |
55 | = = = = = =
56 |
57 | # Статическая типизация
58 |
59 | - - - - - -
60 |
61 | Позволяет сделать Python отчасти более похожим на статически типизированные языки
62 |
63 | @pause@
64 |
65 | Но об этом позже
66 |
--------------------------------------------------------------------------------
/docs/examples/13.Evolutionary_Moon_Lander/captain.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from __future__ import annotations
3 |
4 | import model
5 |
6 | class BraveCaptain(model.Captain):
7 | def control(self):
8 | spaceship = self.model.spaceship
9 | time = self.model.time
10 |
11 | drained = False
12 | if 5.0 < time < 10.0:
13 | spaceship.thrust = model.Spaceship.Thrust.UP
14 | elif 26.5 < time < 27.0 or 30.0 < time < 30.5:
15 | if not drained:
16 | spaceship.drain_fuel(200)
17 | spaceship.thrust = model.Spaceship.Thrust.UP | model.Spaceship.Thrust.LEFT
18 | else:
19 | spaceship.thrust = model.Spaceship.Thrust.NOPE
20 |
21 |
22 | class CarefulCaptain(model.ResponsiveCaptin):
23 | def __init__(self):
24 | super().__init__()
25 | self.count = 0
26 | self.verbose = False
27 |
28 | def control(self):
29 | if self.controlled_from_outside:
30 | return
31 | spaceship = self.model.spaceship
32 | time = self.model.time
33 | left = spaceship.velocity[0] > spaceship.maxlandingvelocity / 2.0 ** 0.5
34 | up = spaceship.velocity[1] < -spaceship.maxlandingvelocity / 2.0 ** 0.5
35 |
36 | spaceship.thrust = \
37 | (model.Spaceship.Thrust.LEFT if left else model.Spaceship.Thrust.NOPE) | \
38 | (model.Spaceship.Thrust.UP if up else model.Spaceship.Thrust.NOPE)
39 |
40 | if self.verbose and self.count % 1000 == 0:
41 | print(time, spaceship.thrust, spaceship.mass)
42 | self.count += 1
43 |
44 | def instruct(self, what_engine: model.Spaceship.Thrust, turn_on: bool):
45 | if not self.controlled_from_outside:
46 | self.model.spaceship.thrust = model.Spaceship.Thrust.NOPE
47 | print("Captain turns thrust off and passes controls to you")
48 | super().instruct(what_engine, turn_on)
49 |
50 | if turn_on:
51 | self.model.spaceship.thrust |= what_engine
52 | else:
53 | self.model.spaceship.thrust &= ~what_engine
54 |
--------------------------------------------------------------------------------
/docs/examples/01_5.Turtle/two_turtles.py:
--------------------------------------------------------------------------------
1 | import turtle as tl
2 | import time
3 | import math
4 | import numpy as np
5 |
6 | MODEL_DT = 0.01
7 | MODEL_G = 9.81
8 | MODEL_T = 15
9 |
10 | sc = tl.Screen() # показ экрана
11 | tl.hideturtle() # спрятать черепашку по умолчанию
12 | tl.tracer(0,0) # отключение автоматического обновления экрана
13 | tl.speed(0) # максимальная скорость прорисовки
14 | tl.delay(0) # отключение 10-мс задержки перед прорисовкой
15 |
16 | class Body:
17 | def __init__(self, x, y, vx, vy, color = 'black'):
18 | self.x = x
19 | self.y = y
20 | self.vx = vx
21 | self.vy = vy
22 | self.turtle = tl.Turtle()
23 | self.turtle.hideturtle()
24 | self.turtle.color(color)
25 | self.turtle.penup()
26 | self.turtle.goto(self.x, self.y)
27 | self.turtle.pendown()
28 | self.turtle.radians() # Переключились на радианы
29 | self.draw()
30 | self.turtle.showturtle()
31 |
32 | def draw(self):
33 | self.turtle.setheading(math.atan2(self.vy, self.vx))
34 | self.turtle.goto(self.x, self.y)
35 |
36 | def advance(self):
37 | self.x += self.vx * MODEL_DT
38 | self.y += self.vy * MODEL_DT
39 |
40 | self.vy -= MODEL_G * MODEL_DT
41 |
42 | bodies = [
43 | Body(-300, 0, 50.0, 50.0, 'blue'),
44 | Body(-300, 0, 55.0, 45.0, 'red')
45 | ]
46 |
47 | def up():
48 | bodies[0].vy += 3
49 |
50 | sc.onkey(up, "Up")
51 | sc.listen()
52 |
53 | t0 = time.time()
54 | for t in np.arange(t0, t0 + MODEL_T + MODEL_DT, MODEL_DT):
55 | for b in bodies:
56 | b.advance()
57 | # Смотрим, сколько должно быть времени, и сколько есть, и какова разница
58 | time_to_pause = t + MODEL_DT - time.time()
59 | if time_to_pause > 0: # Если у нас профицит
60 | time.sleep(time_to_pause) # ждём разницу
61 | for b in bodies: # обновляем черепашек, рисующих тела
62 | b.draw()
63 | tl.update() # и перерисовываем всё
64 | else: # А если дефицит, досадуем...
65 | print(f"Too slow, need {-time_to_pause} sec. more")
66 | tl.done()
67 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/pyqt6_sync.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys
5 | import PyQt6.QtWidgets as qw
6 | import time
7 |
8 | import requests
9 |
10 | urls = [
11 | "https://google.com/",
12 | "https://yandex.ru/",
13 | "http://dluciv.name/",
14 | "https://edu.dluciv.name/",
15 | "https://spbau.ru/",
16 | "https://spbu.ru/",
17 | "https://mail.ru/",
18 | "http://mil.ru/",
19 | "https://github.com/"
20 | ]
21 |
22 |
23 | class MainWindow(qw.QWidget):
24 | def __init__(self):
25 | super().__init__()
26 | self.initUI()
27 |
28 | def initUI(self):
29 | self.resize(250, 250)
30 | self.move(300, 300)
31 | self.setWindowTitle('Sync')
32 |
33 | self.goBtn = qw.QPushButton('Go', self)
34 | self.goBtn.move(50, 50)
35 | self.goBtn.clicked.connect(self.performLongOperation)
36 |
37 | self.goFileBtn = qw.QPushButton('File Go', self)
38 | self.goFileBtn.move(50, 100)
39 | self.goFileBtn.clicked.connect(self.performLongFileOperation)
40 |
41 | self.goNetBtn = qw.QPushButton('Net Go', self)
42 | self.goNetBtn.move(50, 150)
43 | self.goNetBtn.clicked.connect(self.performLongNetOperation)
44 |
45 | self.quitBtn = qw.QPushButton('Quit', self)
46 | self.quitBtn.move(50, 200)
47 | self.quitBtn.clicked.connect(self.close)
48 |
49 | def performLongOperation(self, evt):
50 | print("Going...")
51 | for c in range(10):
52 | print(c)
53 | time.sleep(1)
54 | print("Done.")
55 |
56 | def performLongFileOperation(self, evt):
57 | with open("/etc/passwd", 'rb') as f:
58 | bts = f.read()
59 | print("Got", len(bts), "bytes")
60 |
61 |
62 | def performLongNetOperation(self, evt):
63 | for u in urls:
64 | try:
65 | r = requests.get(u)
66 | print("Got", u, "of", len(r.content), "bytes")
67 | except Exception as e:
68 | print(f"Error while getting {u}: {e}")
69 |
70 |
71 | if __name__ == '__main__':
72 | app = qw.QApplication(sys.argv)
73 | w = MainWindow()
74 | w.show()
75 |
76 | sys.exit(app.exec())
77 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/pyside6_sync.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys
5 | import PySide6.QtWidgets as qw
6 | import time
7 |
8 | import requests
9 |
10 | urls = [
11 | "https://google.com/",
12 | "https://yandex.ru/",
13 | "http://dluciv.name/",
14 | "https://edu.dluciv.name/",
15 | "https://spbau.ru/",
16 | "https://spbu.ru/",
17 | "https://mail.ru/",
18 | "http://mil.ru/",
19 | "https://github.com/"
20 | ]
21 |
22 |
23 | class MainWindow(qw.QWidget):
24 | def __init__(self):
25 | super().__init__()
26 | self.initUI()
27 |
28 | def initUI(self):
29 | self.resize(250, 250)
30 | self.move(300, 300)
31 | self.setWindowTitle('Sync')
32 |
33 | self.goBtn = qw.QPushButton('Go', self)
34 | self.goBtn.move(50, 50)
35 | self.goBtn.clicked.connect(self.performLongOperation)
36 |
37 | self.goFileBtn = qw.QPushButton('File Go', self)
38 | self.goFileBtn.move(50, 100)
39 | self.goFileBtn.clicked.connect(self.performLongFileOperation)
40 |
41 | self.goNetBtn = qw.QPushButton('Net Go', self)
42 | self.goNetBtn.move(50, 150)
43 | self.goNetBtn.clicked.connect(self.performLongNetOperation)
44 |
45 | self.quitBtn = qw.QPushButton('Quit', self)
46 | self.quitBtn.move(50, 200)
47 | self.quitBtn.clicked.connect(self.close)
48 |
49 | def performLongOperation(self, evt):
50 | print("Going...")
51 | for c in range(10):
52 | print(c)
53 | time.sleep(1)
54 | print("Done.")
55 |
56 | def performLongFileOperation(self, evt):
57 | with open("/etc/passwd", 'rb') as f:
58 | bts = f.read()
59 | print("Got", len(bts), "bytes")
60 |
61 |
62 | def performLongNetOperation(self, evt):
63 | for u in urls:
64 | try:
65 | r = requests.get(u)
66 | print("Got", u, "of", len(r.content), "bytes")
67 | except Exception as e:
68 | print(f"Error while getting {u}: {e}")
69 |
70 |
71 | if __name__ == '__main__':
72 | app = qw.QApplication(sys.argv)
73 | w = MainWindow()
74 | w.show()
75 |
76 | sys.exit(app.exec())
77 |
--------------------------------------------------------------------------------
/docs/examples/02.5.typing/README.md:
--------------------------------------------------------------------------------
1 | # Python и аннотация типов
2 |
3 | Для этих примеров вам понядобится Python 3.10 или новее. Любители Conda, Ubintu и прочих источников более старого Python — извините.
4 |
5 | Pyhton поддерживает аннотации переменных, а также параметров и возвращаемых значений функций, ну и ещё кой-чего по мелочи.
6 | Эти аннотации в актуальных версиях Pyhton используются для указания типов переменных, параметров функций и параметров обобщённых типов:
7 |
8 | * [для функций](https://docs.python.org/3/library/typing.html) (чаще всего);
9 | * [для обобщённых типов](https://docs.python.org/3/library/stdtypes.html#types-genericalias);
10 | * [для переменных](https://peps.python.org/pep-0526/).
11 |
12 | То есть, этот механизм позволяет добавить в Python подобие статической типизации. Но Python сам при этом соответствие типов не проверяет. Однако:
13 |
14 | 1. Типы можно проверить при помощи статических анализаторов и проверок времени выполнения.
15 | Эти анализаторы и проверялки хорошо помогают работать, хотя и сами не лишены ошибок.
16 | 2. Аннотации типов распознаются многиим IDE (VS Code, PyCharm и т.д.), и улучшают автодополнение и, опять же, проверку кода.
17 |
18 | ## Статический анализатор [MyPy](https://pypi.org/project/mypy/)
19 |
20 | * Инсталлируется: `pip3 install mypy`
21 | * Запускается: `mypy my_program.py`
22 |
23 | Примеры:
24 |
25 | * `mypy_test.py` — две ошибки, которые действительно есть;
26 | * `mypy_bug.py` — ложноположительное срабатываение, которое [обсуждается с 2017](https://github.com/python/mypy/issues/3186) (т.е. не всё так просто!) года.
27 |
28 | ## Системы проверки времени выполнения
29 |
30 | ### [TypeGuard](https://pypi.org/project/typeguard/)
31 |
32 | * Инсталлируется: `pip3 install typeguard`
33 | * Запускается сам вместе с программой, отключается — для экономии можно запустить Python с опцией оптимизации: `python -O my_program.py`
34 |
35 | Проверяет во время выполнения функции, декорированные `@typechecked`.
36 |
37 | * `typeguard_test` — ругается, и по делу;
38 | * `typeguard_bug` — [пропускает ошибку](https://github.com/agronholm/typeguard/issues/276), использующую синтаксис Python 3.10+ — ещё не научился.
39 |
40 | ### Ещё
41 |
42 | * [BearType](https://pypi.org/project/beartype/) — шустрый, но тоже с особенностями;
43 | * и ещё найдутся =).
--------------------------------------------------------------------------------
/tools/studgit/uhw.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # Encoding: utf-8
3 |
4 | require 'open3'
5 | require 'yaml'
6 | require 'uri'
7 | require 'os'
8 | # require 'tqdm'
9 |
10 | def get_stud_info
11 | if File.exist?('repos.yml')
12 | YAML.load_file('repos.yml')
13 | elsif File.exist?('.git')
14 | { '.' => [{
15 | 'url' => '.',
16 | 'path' => '.',
17 | 'branch' => `git branch --show-current`.strip # git branch --show-current
18 | }]}
19 | end
20 | end
21 |
22 | def o3c3 *ca, **kw
23 | o, e, c = Open3::capture3 *ca
24 | if c.exitstatus != 0
25 | if kw[:verbose]
26 | STDERR.puts "#{ca.join(' ')} ended with error: #{e}"
27 | end
28 | if kw[:verbose].kind_of?(Integer) && kw[:verbose] > 1
29 | puts "Output: #{o}"
30 | end
31 | false
32 | else
33 | true
34 | end
35 | end
36 |
37 | def iter_repos
38 | get_stud_info.each do | s, rr | # get_stud_info.tqdm.each
39 | puts "Студент: #{s}"
40 | if rr.size == 0
41 | puts " - !! не опубликовал репозиториев..."
42 | else
43 | Dir.mkdir(s) unless File.exist?(s)
44 | Dir.chdir s do
45 | rr.each do | r |
46 | url = r['url']
47 | path = if r['path']
48 | r['path'] # любители назвать репозиторий "-."
49 | else
50 | URI(url).path.split('/').last.sub(".git", "") # обычные люди
51 | end
52 | branch = r.fetch('branch', 'main')
53 | yield url, path, branch
54 | end
55 | end
56 | end
57 | end
58 | end
59 |
60 | def lf_here
61 | if OS.posix?
62 | `find . -type f -name '*.py' -print0 | xargs -0 dos2unix -q --keepdate --`
63 | `find . -type f -name '*.c*' -print0 | xargs -0 dos2unix -q --keepdate --`
64 | end
65 | end
66 |
67 | def init_repo r, rpa, br
68 | puts " - клонируем <#{r}> -> <#{rpa}>..."
69 | if o3c3 'git', 'clone', '--', r, rpa
70 | Dir::chdir rpa do
71 | o3c3 'git', 'config', 'core.autocrlf', 'input'
72 | o3c3 'git', 'branch', '-a'
73 | o3c3 'git', 'switch', br
74 | lf_here
75 | end
76 | end
77 | end
78 |
79 | def rcu_repo r, rpa, br
80 | puts " - обновляем <#{r}> ..."
81 | Dir::chdir rpa do
82 | o3c3 'git', 'clean', '-fdX'
83 | o3c3 'git', 'restore', '.'
84 | o3c3 'git', 'switch', br
85 | o3c3 'git', 'pull', '--all', '--tags', '--rebase'
86 | lf_here
87 | end
88 | end
89 |
90 | # p ARGV
91 |
92 | case ARGV[0]
93 | when 'i'
94 | iter_repos &method(:init_repo)
95 | when 'u'
96 | iter_repos &method(:rcu_repo)
97 | end
98 |
--------------------------------------------------------------------------------
/docs/examples/01.quality/rabin_karp_with_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """
4 | Реализация алгоритма Рабина-Карпа с модульными тестами
5 | """
6 |
7 | import unittest
8 |
9 | def rabin_karp(text, pattern):
10 | """
11 | Поиск всех вхождений алгоритмом Рабина-Карпа
12 |
13 | Параметры:
14 | ----------
15 | text: str
16 | текст
17 | pattern: str
18 | образец
19 |
20 | Возвращаемое значение
21 | ---------------------
22 | список позиций в тексте, с которых начинаются вхождения образца
23 | """
24 | result = []
25 |
26 | # Менять отсюда =) ---- vvvvv ----
27 |
28 | pass # вот тут пожалуйста тело функции =)
29 |
30 | # Менять до сюда =) ---- ^^^^^ ----
31 | return result
32 |
33 |
34 | class RabinKarpTest(unittest.TestCase):
35 | """Тесты для метода Рабина-Карпа"""
36 |
37 | def setUp(self):
38 | """Инициализация"""
39 | self.text1 = 'axaxaxax'
40 | self.pattern1 = 'xax'
41 | self.text2 = 'bababab'
42 | self.pattern2 = 'bab'
43 |
44 | def test_return_type(self):
45 | """Проверка того, что функция возвращает список"""
46 | self.assertIsInstance(
47 | rabin_karp(self.text1, "x"), list,
48 | msg="Функция должна возвращать список"
49 | )
50 |
51 | def test_returns_empty(self):
52 | """Проверка того, что функция, когда следует, возвращает пустой список"""
53 | self.assertEqual(
54 | [], rabin_karp(self.text1, "z"),
55 | msg="Функция должна возвращать пустой список, если нет вхождений"
56 | )
57 | self.assertEqual(
58 | [], rabin_karp("", self.pattern1),
59 | msg="Функция должна возвращать пустой список, если текст пустой"
60 | )
61 | self.assertEqual(
62 | [], rabin_karp("", ""),
63 | msg="Функция должна возвращать пустой список, если текст пустой, даже если образец пустой"
64 | )
65 |
66 | def test_finds(self):
67 | """Проверка того, что функция ищет все вхождения непустых образцов"""
68 | self.assertEqual(
69 | [1, 3, 5], rabin_karp(self.text1, self.pattern1),
70 | msg="Функция должна искать все вхождения"
71 | )
72 | self.assertEqual(
73 | [0, 2, 4], rabin_karp(self.text2, self.pattern2),
74 | msg="Функция должна искать все вхождения"
75 | )
76 |
77 | def test_finds_all_empties(self):
78 | """Проверка того, что функция ищет все вхождения пустого образца"""
79 | self.assertEqual(
80 | list(range(len(self.text1))), rabin_karp(self.text1, ""),
81 | msg="Пустая строка должна находиться везде"
82 | )
83 |
84 | # Должно выдать:
85 | # --------------
86 | # Ran ... tests in ...s
87 | # OK
88 |
89 | # Запуск тестов
90 | if __name__ == '__main__':
91 | unittest.main()
92 |
--------------------------------------------------------------------------------
/docs/examples/06.GUI/ui/main_dialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 441
10 | 273
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 |
18 |
19 |
20 | Qt::Vertical
21 |
22 |
23 |
24 | 20
25 | 40
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Qt::Vertical
41 |
42 |
43 |
44 | 20
45 | 40
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | QFrame::StyledPanel
54 |
55 |
56 | QFrame::Raised
57 |
58 |
59 |
60 |
61 |
62 | Qt::Horizontal
63 |
64 |
65 |
66 | 192
67 | 20
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Воск знак
76 |
77 |
78 |
79 |
80 |
81 |
82 | Привет
83 |
84 |
85 |
86 |
87 |
88 |
89 | OK
90 |
91 |
92 | true
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/docs/examples/02.75.NedoForth/nedoforth.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from abc import ABC, abstractmethod
4 | import sys
5 |
6 | class LineInterpreter(ABC):
7 | '''
8 | Интерпретатор произвольного языка c 1 операцией на строку
9 | '''
10 |
11 | def __init__(self, source_file_name: str) -> None:
12 | '''
13 | Создать интерпретатор для данного исходного файла
14 |
15 | :param str source_file_name: Имя исходного файла
16 | '''
17 | self.source_file_name: str = source_file_name
18 | with open(source_file_name, 'r', encoding='utf8') as source_file:
19 | self._lines: list[str] = [l.rstrip() for l in source_file]
20 | self._current_line_idx: int = 0 if len(self._lines) else -1
21 | if len(self._lines) == 0:
22 | self.quit(f"Empty source file: {source_file_name}")
23 |
24 | def quit(self, message: str = ""):
25 | '''
26 | Завершить работу интерпретатора
27 | '''
28 | if message != "":
29 | print(message, file=sys.stderr)
30 | sys.exit(0)
31 |
32 | def current_line(self) -> str:
33 | if not (0 <= self._current_line_idx < len(self._lines)):
34 | self.quit("Program is over")
35 | return self._lines[self._current_line_idx]
36 |
37 | def abs_jump(self, line_no: int) -> None:
38 | self._current_line_idx = line_no
39 |
40 | def rel_jump(self, offset: int) -> None:
41 | self._current_line_idx += offset
42 |
43 | def step(self) -> None:
44 | self.rel_jump(1)
45 |
46 | @abstractmethod
47 | def execute_current_line(self) -> None:
48 | ...
49 |
50 | def run(self) -> None:
51 | while True:
52 | self.execute_current_line()
53 | self.step()
54 |
55 |
56 | class NedoForth(LineInterpreter):
57 | def __init__(self, source_file_name: str) -> None:
58 | super().__init__(source_file_name)
59 | self.stack: list[float] = []
60 |
61 | def __repr__(self) -> str:
62 | return "Stack: " + repr(self.stack)
63 |
64 | def execute_current_line(self) -> None:
65 | # print(f"{self._current_line_idx}: {self.current_line()}")
66 | l = self.current_line()
67 |
68 | if len(l) == 0 or l.startswith('#'):
69 | return # Пустая строка или комментарий или shebang
70 | else:
71 | match l:
72 | case 'стек' | 'stack':
73 | print(self.stack)
74 | case 'вершина' | 'top':
75 | print(self.stack[-1])
76 | case 'ввод' | 'input':
77 | self.stack.append(float(input("> ")))
78 | case '+':
79 | y = self.stack.pop()
80 | x = self.stack.pop()
81 | self.stack.append(x + y)
82 | case '**':
83 | y = self.stack.pop()
84 | x = self.stack.pop()
85 | self.stack.append(x ** y)
86 | case 'peek':
87 | self.stack.append(self.stack[-self.stack.pop()])
88 | case 'reljump' | 'отпрыг':
89 | self.rel_jump(int(self.stack.pop()) - 1)
90 | case other:
91 | self.stack.append(float(l))
92 |
93 |
94 | if __name__ == '__main__':
95 | fi = NedoForth(sys.argv[1])
96 | fi.run()
97 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/pyside6_async.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys
5 | import asyncio
6 | import PySide6.QtWidgets as qw
7 | import aiofiles # https://pypi.org/project/aiofiles/
8 | import aiohttp
9 |
10 | # Problem here
11 | # https://github.com/gmarull/asyncqt/blob/5cb38a417608e04256db4bc7eb16dc4db88f7db0/asyncqt/__init__.py#L202
12 | # import asyncqt
13 | # from asyncqt import asyncSlot
14 |
15 | # Fixed here:
16 | # https://github.com/codelv/asyncqtpy/blob/def6207aa44c7de794345816fff514103c2440bb/asyncqtpy/__init__.py#L196
17 | # import asyncqtpy as asq
18 | # from asyncqtpy import asyncSlot
19 |
20 | # ... or here:
21 | # https://github.com/CabbageDevelopment/qasync/blob/58882735229b0d17836621d7d09ce02a6f80789d/qasync/__init__.py#L259
22 | import qasync as asq
23 | from qasync import asyncSlot
24 |
25 | urls = [
26 | "https://google.com/",
27 | "https://yandex.ru/",
28 | "http://dluciv.name/",
29 | "https://edu.dluciv.name/",
30 | "https://spbau.ru/",
31 | "https://spbu.ru/",
32 | "https://mail.ru/",
33 | "http://mil.ru/",
34 | "https://github.com/"
35 | ]
36 |
37 |
38 | class MainWindow(qw.QWidget):
39 | def __init__(self):
40 | super().__init__()
41 | self.initUI()
42 |
43 | def initUI(self):
44 | self.resize(250, 250)
45 | self.move(300, 300)
46 | self.setWindowTitle('Sync')
47 |
48 | self.goBtn = qw.QPushButton('A Go', self)
49 | self.goBtn.move(50, 50)
50 | self.goBtn.clicked.connect(self.performLongOperation)
51 |
52 | self.goFileBtn = qw.QPushButton('A File Go', self)
53 | self.goFileBtn.move(50, 100)
54 | self.goFileBtn.clicked.connect(self.performLongFileOperation)
55 |
56 | self.goNetBtn = qw.QPushButton('A Net Go', self)
57 | self.goNetBtn.move(50, 150)
58 | self.goNetBtn.clicked.connect(self.performLongNetOperation)
59 |
60 | self.quitBtn = qw.QPushButton('Quit', self)
61 | self.quitBtn.move(50, 200)
62 | self.quitBtn.clicked.connect(self.close)
63 |
64 |
65 | @asyncSlot()
66 | async def performLongOperation(self):
67 | self.goBtn.setEnabled(False)
68 | print("A Going...")
69 | for c in range(10):
70 | print("A", c)
71 | await asyncio.sleep(1)
72 | print("A Done.")
73 | self.goBtn.setEnabled(True)
74 |
75 | @asyncSlot()
76 | async def performLongFileOperation(self):
77 | async with aiofiles.open("/etc/passwd", 'rb') as f: # put your file here
78 | bts = await f.read()
79 | print("Got", len(bts), "bytes")
80 |
81 |
82 | @asyncSlot()
83 | async def performLongNetOperation(self):
84 | async with aiohttp.ClientSession() as session:
85 | for u in urls:
86 | try:
87 | r = await session.get(u)
88 | c = await r.read()
89 | print("Got", u, "of", len(c), "bytes")
90 | except Exception as e:
91 | print(f"Error while getting {u}: {e}")
92 |
93 | if __name__ == '__main__':
94 | app = qw.QApplication(sys.argv)
95 | loop = asq.QEventLoop(app)
96 | asyncio.set_event_loop(loop)
97 |
98 | w = MainWindow()
99 | w.show()
100 |
101 | with loop:
102 | sys.exit(loop.run_forever())
103 |
--------------------------------------------------------------------------------
/docs/examples/03.parallel/pyqt6_async.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys
5 | import asyncio
6 | import PyQt6.QtWidgets as qw
7 | import aiofiles # https://pypi.org/project/aiofiles/
8 | import aiohttp
9 |
10 | # Problem here
11 | # https://github.com/gmarull/asyncqt/blob/5cb38a417608e04256db4bc7eb16dc4db88f7db0/asyncqt/__init__.py#L202
12 | # import asyncqt
13 | # from asyncqt import asyncSlot
14 |
15 | # Fixed here:
16 | # https://github.com/codelv/asyncqtpy/blob/def6207aa44c7de794345816fff514103c2440bb/asyncqtpy/__init__.py#L196
17 | # import asyncqtpy as asq
18 | # from asyncqtpy import asyncSlot
19 |
20 | # ... or here:
21 | # https://github.com/CabbageDevelopment/qasync/blob/58882735229b0d17836621d7d09ce02a6f80789d/qasync/__init__.py#L259
22 | import qasync as asq
23 | from qasync import asyncSlot
24 |
25 | urls = [
26 | "https://google.com/",
27 | "https://yandex.ru/",
28 | "http://dluciv.name/",
29 | "https://edu.dluciv.name/",
30 | "https://spbau.ru/",
31 | "https://spbu.ru/",
32 | "https://mail.ru/",
33 | "http://mil.ru/",
34 | "https://github.com/"
35 | ]
36 |
37 |
38 | class MainWindow(qw.QWidget):
39 | def __init__(self):
40 | super().__init__()
41 | self.initUI()
42 |
43 | def initUI(self):
44 | self.resize(250, 250)
45 | self.move(300, 300)
46 | self.setWindowTitle('Sync')
47 |
48 | self.goBtn = qw.QPushButton('A Go', self)
49 | self.goBtn.move(50, 50)
50 | self.goBtn.clicked.connect(self.performLongOperation)
51 |
52 | self.goFileBtn = qw.QPushButton('A File Go', self)
53 | self.goFileBtn.move(50, 100)
54 | self.goFileBtn.clicked.connect(self.performLongFileOperation)
55 |
56 | self.goNetBtn = qw.QPushButton('A Net Go', self)
57 | self.goNetBtn.move(50, 150)
58 | self.goNetBtn.clicked.connect(self.performLongNetOperation)
59 |
60 | self.quitBtn = qw.QPushButton('Quit', self)
61 | self.quitBtn.move(50, 200)
62 | self.quitBtn.clicked.connect(self.close)
63 |
64 |
65 | @asyncSlot(bool)
66 | async def performLongOperation(self, evt):
67 | self.goBtn.setEnabled(False)
68 | print("A Going...")
69 | for c in range(10):
70 | print("A", c)
71 | await asyncio.sleep(1)
72 | print("A Done.")
73 | self.goBtn.setEnabled(True)
74 |
75 | @asyncSlot(bool)
76 | async def performLongFileOperation(self, evt):
77 | async with aiofiles.open("/etc/passwd", 'rb') as f: # put your file here
78 | bts = await f.read()
79 | print("Got", len(bts), "bytes")
80 |
81 |
82 | @asyncSlot(bool)
83 | async def performLongNetOperation(self, evt):
84 | async with aiohttp.ClientSession() as session:
85 | for u in urls:
86 | try:
87 | r = await session.get(u)
88 | c = await r.read()
89 | print("Got", u, "of", len(c), "bytes")
90 | except Exception as e:
91 | print(f"Error while getting {u}: {e}")
92 |
93 |
94 | if __name__ == '__main__':
95 | app = qw.QApplication(sys.argv)
96 | loop = asq.QEventLoop(app)
97 | asyncio.set_event_loop(loop)
98 |
99 | w = MainWindow()
100 | w.show()
101 |
102 | with loop:
103 | sys.exit(loop.run_forever())
104 |
--------------------------------------------------------------------------------
/tools/plagiarism-reporter/report_export.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | import itertools
4 | import sys
5 | import csv
6 | from typing import Iterable, Tuple, Dict, List, Union
7 |
8 | from odf.opendocument import OpenDocumentSpreadsheet
9 | from odf.style import Style, TextProperties, ParagraphProperties, TableCellProperties
10 | from odf.style import TableColumnProperties
11 | from odf.text import P, Number
12 | from odf.table import Table, TableColumn, TableRow, TableCell
13 |
14 |
15 | def export_odf_report(
16 | filename: str, borrowing_facts: Iterable[Tuple[str, str, float]]):
17 | # Thanks to https://joinup.ec.europa.eu/svn/odfpy/tags/release-0.9/api-for-odfpy.odt
18 | # for example of this API
19 |
20 | doc = OpenDocumentSpreadsheet()
21 |
22 | # -------- add styles -----------
23 | vertcell = Style(name="vcell", family="table-cell")
24 | vertcell.addElement(TableCellProperties(rotationangle=90))
25 |
26 | # -------- create table ----------
27 |
28 | # Start the table, and describe the columns
29 | table = Table(name="Borrowing")
30 |
31 | # ------ process some data ---------
32 |
33 | scols = set(gfn for bfn, gfn, bo in borrowing_facts)
34 | cols = sorted(scols)
35 | rows = sorted(set(bfn for bfn, gfn, bo in borrowing_facts))
36 |
37 | rc: Dict[str, int] = {}
38 | rr: Dict[str, int] = {}
39 | for c, i in zip(cols, itertools.count()):
40 | rc[c] = i
41 | for r, i in zip(rows, itertools.count()):
42 | rr[r] = i
43 |
44 | data = [[0.0] * len(cols) for r in rows] # to make different list
45 |
46 | for bfn, gfn, bo in borrowing_facts:
47 | dr = rr[bfn]
48 | dc = rc[gfn]
49 | data[dr][dc] = bo
50 |
51 | # --------- go! ----------
52 |
53 | tr = TableRow()
54 |
55 | tc = TableCell()
56 | tc.addElement(P(text=r"Borrower \ Source"))
57 | tr.addElement(tc)
58 |
59 | for c in cols:
60 | tc = TableCell(stylename=vertcell)
61 | tc.addElement(P(text=c))
62 | tr.addElement(tc)
63 |
64 | table.addElement(tr)
65 |
66 | for r, rd in zip(rows, data):
67 | tr = TableRow()
68 |
69 | tc = TableCell()
70 | tc.addElement(P(text=r))
71 | tr.addElement(tc)
72 |
73 | for cd in rd:
74 | if cd > 0:
75 | tc = TableCell(valuetype='percentage', value=cd)
76 | else:
77 | tc = TableCell()
78 | tr.addElement(tc)
79 |
80 | table.addElement(tr)
81 |
82 | doc.spreadsheet.addElement(table)
83 | doc.save(filename, False)
84 |
85 |
86 | def _export_csv_report(
87 | filename: str, borrowing_facts: Iterable[Tuple[str, str, float]]):
88 | scols = set(gfn for bfn, gfn, bo in borrowing_facts)
89 | cols = sorted(scols)
90 | rows = sorted(set(bfn for bfn, gfn, bo in borrowing_facts))
91 |
92 | rc = {}
93 | for c, i in zip(cols, itertools.count()):
94 | rc[c] = i
95 | rr = {}
96 | for r, i in zip(rows, itertools.count()):
97 | rr[r] = i
98 |
99 | data = [[0.0] * len(cols) for r in rows] # to make different list
100 |
101 | for bfn, gfn, bo in borrowing_facts:
102 | dr = rr[bfn]
103 | dc = rc[gfn]
104 | data[dr][dc] = bo
105 |
106 | with open(filename, 'w+', encoding='utf-8', newline='') as csvf:
107 | cw = csv.writer(csvf, dialect='unix', delimiter='\t')
108 | cw.writerow([r"Borrower \ Source"] + cols)
109 |
110 | for r, rd in zip(rows, data):
111 | row: List[Union[str, float]] = [r] + rd
112 | cw.writerow(row)
113 |
114 |
115 | if __name__ == '__main__':
116 | print("This is not a script, just a module", file=sys.stderr)
117 | exit(-1)
118 |
--------------------------------------------------------------------------------
/docs/jupiter-notebooks/08.just-in-time.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Just-in-Time компиляция\n",
8 | "\n",
9 | "«Как раз вовремя» — компиляция, при которой машинный код геенрируется по мере исполнения программы для её критических с точки зрения производительности участков — обычно функций, реже — циклов.\n",
10 | "\n",
11 | "Компиляция происходит при первом вызове, поэтому для того, чтобы измерить производительность JIT, следует сперва вызвать функцию «вхолостую», можно на данных небольшого объёма.\n",
12 | "\n",
13 | "Некоторые реализации Python ([PyPy](https://www.pypy.org/)) содержат JIT-компилятор в дистрибутиве. Для CPython можно воспользоваться пакетом [Numba](https://numba.pydata.org/): `pip install numba`."
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": 7,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "from __future__ import annotations\n",
23 | "from numba import jit, prange\n",
24 | "import numpy\n",
25 | "import numpy.random\n",
26 | "\n",
27 | "random_m = numpy.matrix(numpy.random.rand(300, 30000))\n",
28 | "heat_m = numpy.zeros((1, 1))\n",
29 | "\n",
30 | "def sum_matrix_nojit(m: numpy.matrix)-> numpy.float:\n",
31 | " s = 0.0\n",
32 | " d0, d1 = m.shape\n",
33 | " for r in range(d0):\n",
34 | " for c in range(d1):\n",
35 | " s += m[r, c]\n",
36 | " return s\n",
37 | "\n",
38 | "\n",
39 | "@jit(nopython=True, fastmath=True)\n",
40 | "def sum_matrix_jit(m: numpy.matrix)-> numpy.float:\n",
41 | " s = 0.0\n",
42 | " d0, d1 = m.shape\n",
43 | " for r in range(d0):\n",
44 | " for c in range(d1):\n",
45 | " s += m[r, c]\n",
46 | " return s\n",
47 | "\n",
48 | "\n",
49 | "@jit(nopython=True, parallel=True, fastmath=True)\n",
50 | "def sum_matrix_jit_par(m: numpy.matrix)-> numpy.float:\n",
51 | " s = 0.0\n",
52 | " d0, d1 = m.shape\n",
53 | " for r in prange(d0):\n",
54 | " sc = 0.0\n",
55 | " for c in range(d1):\n",
56 | " sc += m[r, c]\n",
57 | " s += sc\n",
58 | " return s"
59 | ]
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": 8,
64 | "metadata": {},
65 | "outputs": [
66 | {
67 | "name": "stdout",
68 | "output_type": "stream",
69 | "text": [
70 | "9.38 s ± 125 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
71 | "6.98 ms ± 94.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
72 | "6.83 ms ± 422 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
73 | ]
74 | }
75 | ],
76 | "source": [
77 | "%timeit sum_matrix_nojit(random_m)\n",
78 | "\n",
79 | "sum_matrix_jit(heat_m) # give it a heat\n",
80 | "%timeit sum_matrix_jit(random_m)\n",
81 | "\n",
82 | "sum_matrix_jit_par(heat_m) # give it a heat\n",
83 | "%timeit sum_matrix_jit_par(random_m)"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": null,
89 | "metadata": {},
90 | "outputs": [],
91 | "source": []
92 | }
93 | ],
94 | "metadata": {
95 | "kernelspec": {
96 | "display_name": "Python 3",
97 | "language": "python",
98 | "name": "python3"
99 | },
100 | "language_info": {
101 | "codemirror_mode": {
102 | "name": "ipython",
103 | "version": 3
104 | },
105 | "file_extension": ".py",
106 | "mimetype": "text/x-python",
107 | "name": "python",
108 | "nbconvert_exporter": "python",
109 | "pygments_lexer": "ipython3",
110 | "version": "3.9.1"
111 | }
112 | },
113 | "nbformat": 4,
114 | "nbformat_minor": 4
115 | }
116 |
--------------------------------------------------------------------------------
/docs/examples/01.quality/prime_check_with_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """
4 | Реализация проверки числа на простоту
5 | """
6 |
7 | import unittest
8 | from tqdm import tqdm
9 | from sympy.ntheory import primetest, factorint
10 |
11 | def is_prime(num):
12 | """
13 | Test if n is a prime number (True) or not (False).
14 | """
15 | # Менять отсюда =) ---- vvvvv ----
16 | # Реализовать проверку на простоту самим
17 | # А пока тут точная проверка на простоту, но из библиотеки
18 | if num <= 1:
19 | return False
20 | fi = factorint(num)
21 | return len(fi.keys()) == 1 and max(fi.values()) == 1
22 | # Менять до сюда =) ---- ^^^^^ ----
23 |
24 | class PrimeCheckTest(unittest.TestCase):
25 | """Тесты для функции проверки на простоту"""
26 |
27 | def setUp(self):
28 | """Инициализация"""
29 | pass
30 |
31 | @staticmethod
32 | def is_exatly_prime_64(num):
33 | """
34 | Точная проверка того, простое ли (True) или составное (False) число,
35 | работает только в диапазоне [0, 2*64)
36 | """
37 | if num >= 2 ** 64:
38 | raise ValueError("Эта функция работает только в диапазоне [0, 2*64)")
39 | return primetest.isprime(num)
40 |
41 | def test_return_type(self):
42 | """Проверка того, что функция возвращает bool"""
43 | for i in range(-5, 10):
44 | self.assertIsInstance(
45 | is_prime(i), bool,
46 | msg="Функция должна возвращать bool"
47 | )
48 |
49 | def test_nonned_1000(self):
50 | """Проверка [0..999)"""
51 | print("Проверяем на [0..999)")
52 | for i in tqdm(range(1000)):
53 | self.assertEqual(
54 | is_prime(i), PrimeCheckTest.is_exatly_prime_64(i),
55 | msg=f"Функция наврала на небольшом положительном {i}"
56 | )
57 |
58 | def test_negatives_10(self):
59 | """Проверка [-10, 0)"""
60 | for i in range(-10, 0):
61 | self.assertEqual(
62 | is_prime(i), PrimeCheckTest.is_exatly_prime_64(i),
63 | msg=f"Функция наврала на отрицательном {i}"
64 | )
65 |
66 | def test_carmichael(self):
67 | """Проверка на числах Кармайкла"""
68 | # можно было sympy.functions.combinatorial.numbers.carmichael,
69 | # но очень не быстро, лучше взять готовые https://oeis.org/A002997
70 | carmichaels = [
71 | 561, 1105, 1729, 2465, 2821, 6601, 8911, 10585, 15841, 29341,
72 | 41041, 46657, 52633, 62745, 63973, 75361, 101101, 115921, 126217,
73 | 162401, 172081, 188461, 252601, 278545, 294409, 314821, 334153,
74 | 340561, 399001, 410041, 449065, 488881, 512461
75 | ]
76 | print("Проверяем на числах Кармайкла")
77 | for c in tqdm(carmichaels):
78 | self.assertFalse(
79 | is_prime(c),
80 | msg=f"Функция наврала на числе Кармайкла {c}"
81 | )
82 |
83 | def test_trivial_pseudorandoms(self):
84 | """
85 | Проверка на тривиальных псевдослучайных
86 | Генерация линейным конгруэнтным генератором
87 | https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
88 | в память о ZX81
89 | """
90 | a = 75
91 | s = 1
92 | c = 74
93 | m = 1 << 16 + 1
94 | print("Проверяем на линейных конгруэнтных псевдослучайных")
95 | for _ in tqdm(range(50)):
96 | s = (s * a + c) % m
97 | r = s % ( 1 << 16 ) # 16 бит
98 | self.assertEqual(
99 | is_prime(r), PrimeCheckTest.is_exatly_prime_64(r),
100 | msg=f"Функция наврала на {r}"
101 | )
102 |
103 | # Должно выдать:
104 | # --------------
105 | # Ran ... tests in ...s
106 | # OK
107 |
108 | # Запуск тестов
109 | if __name__ == '__main__':
110 | unittest.main()
111 |
--------------------------------------------------------------------------------
/docs/examples/13.Evolutionary_Moon_Lander/moon_game.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from __future__ import annotations
3 |
4 | from numpy import array as vec
5 | import pygame
6 | import os
7 | import model
8 | import captain
9 |
10 | _scriptdir = os.path.dirname(os.path.realpath(__file__))
11 |
12 |
13 | class TextSprite(pygame.sprite.Sprite):
14 | def __init__(self, ship: model.Spaceship):
15 | super().__init__()
16 | self.font = pygame.font.Font(pygame.font.get_default_font(), 16)
17 | self.ship = ship
18 | self.update(0)
19 |
20 | def update(self, dt):
21 | super().update(dt)
22 | f = f"{self.ship.fuel_mass:03.0f}"
23 | vx = f"{self.ship.velocity[0]:0.2f}"
24 | vy = f"{self.ship.velocity[1]:0.2f}"
25 | self.image = self.font.render(f"{f} [{vx},{vy}]", True, (255,255,0))
26 | ir: pygame.Rect = self.image.get_rect()
27 | self.rect = ir.move(
28 | int(self.ship.position[0]) - ir.width // 2,
29 | 500 - int(self.ship.position[1]) - ir.height // 2
30 | )
31 |
32 |
33 | TIMEREVENT = pygame.USEREVENT + 1
34 |
35 | def model_and_repaint():
36 | global screen, renderups, ship_sprite, clear_screen
37 | global relief, game_model, cap, ship
38 | ms = pygame.time.get_ticks()
39 | t = ms / 1000.0
40 | game_model.run_to(t)
41 |
42 | renderups.clear(screen, clear_screen)
43 | ship_sprite.pos = ship.position
44 | renderups.update(0)
45 |
46 | rects = renderups.draw(screen) # auto + cleared rects
47 | pygame.display.update(rects)
48 |
49 |
50 | _key_to_thrust = {
51 | pygame.K_UP: model.Spaceship.Thrust.UP,
52 | pygame.K_w: model.Spaceship.Thrust.UP,
53 | pygame.K_LEFT: model.Spaceship.Thrust.LEFT,
54 | pygame.K_a: model.Spaceship.Thrust.LEFT,
55 | pygame.K_RIGHT: model.Spaceship.Thrust.RIGHT,
56 | pygame.K_d: model.Spaceship.Thrust.RIGHT
57 | }
58 |
59 | def main_cycle_body()-> bool:
60 | event = pygame.event.wait()
61 | if event.type == pygame.QUIT:
62 | return False
63 | elif event.type == TIMEREVENT:
64 | model_and_repaint()
65 | elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:
66 | print(f"Keyboard event: {event.type}, key: {event.key}")
67 | ktt = _key_to_thrust.get(event.key)
68 | if ktt:
69 | cap.instruct(ktt, event.type == pygame.KEYDOWN)
70 | else:
71 | print("Giving controls back to cap!")
72 | cap.free()
73 | else:
74 | pass
75 | #print(event)
76 | return True
77 |
78 |
79 | def start_game():
80 | global screen, clear_screen, relief
81 |
82 | # fps = clock.get_fps() # how to get system FPS?..
83 | fps = 30
84 | pygame.time.set_timer(TIMEREVENT, int(1000 / fps))
85 |
86 | bg = pygame.image.load(os.path.join(_scriptdir, 'images', "background.jpg"))
87 | screen.fill((0, 0, 0))
88 | screen.blit(bg, bg.get_rect())
89 |
90 | for x in range(0, 1500):
91 | pygame.draw.line(
92 | screen,
93 | (160, 160, 160),
94 | (round(x), 500),
95 | (round(x), 500 - round(relief.get_height(x)))
96 | )
97 |
98 | pygame.display.update(pygame.Rect(0, 0, 1500, 500))
99 | clear_screen = screen.copy()
100 |
101 | while main_cycle_body():
102 | pass
103 |
104 |
105 | def main():
106 | global window, screen, renderups, ship_sprite
107 |
108 | global relief, game_model, cap, ship
109 |
110 | relief = model.Relief("surface_heights.csv")
111 | ship = model.Spaceship(1000.0, vec([20.0, 0.0]), vec([0.0, 200.0]))
112 | # cap = captain.BraveCaptain()
113 | cap = captain.CarefulCaptain()
114 | game_model = model.Model(relief, ship, cap)
115 |
116 | pygame.init()
117 | # pygame.mixer.init()
118 | pygame.font.init()
119 |
120 | ship_sprite = TextSprite(ship)
121 | renderups = pygame.sprite.RenderUpdates(ship_sprite)
122 | window = pygame.display.set_mode((1500, 500), 0, 32)
123 | pygame.display.set_caption("Let's land")
124 | screen = pygame.display.get_surface()
125 | start_game()
126 |
127 | if __name__ == '__main__':
128 | main()
129 |
--------------------------------------------------------------------------------
/docs/slides/02.First_Practice/content.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Немного практики
4 |
5 | ### Луцив Дмитрий Вадимович1,2
6 | ### Кознов Дмитрий Владимирович2
7 |
8 | 1СПбАУ 2СПбГУ
9 |
10 |
11 | Введение в программирование на Python
12 |
13 |
14 |
15 |
16 | = = = = = = = = = = = = =
17 |
18 | ## Инсталляция окружения, первый запуск I
19 |
20 | 1. Регистрируемся:
21 | * на GitHub
22 | * на https://discord.com/
23 | * идём в чатик по ссылке на https://edu.dluciv.name/Home/python
24 | 2. Инсталлируем и запускаем ПО:
25 | * перечисленное на https://edu.dluciv.name/Home/python
26 |
27 | Запускаем REPL Python (`python`)
28 |
29 | - - - - - - - - - - - - -
30 | ## Инсталляция окружения, первый запуск II
31 |
32 | ### ... и второй
33 |
34 | Убеждаемся что каталог с `ipython` (`ipython.exe`) находится в пути. Запускаем IPython (`ipython`).
35 |
36 | Основные ценные возможности IPython в сравнении с «родной» оболочкой:
37 |
38 | * `_i`, `_ii`, `_iii`, ... — история ввода
39 | * `_`, `__`, `___`, ... — история вывода
40 | * `%ed #` — редактирование предыдущего ввода
41 | * `%cd ...`, `%ls ...`, `%pwd ...`, `%load ...` — операции с файловой системой
42 | * <Ctrl+R> — поиск по предыдущему вводу
43 | * Killer-features:
44 | * `obj?` — выдача справки по объекту
45 | * <Tab> — дополнение команды
46 |
47 | Больше «волшебных команд» [тут](https://ipython.readthedocs.io/en/stable/interactive/magics.html).
48 |
49 | - - - - - - - - - - - - -
50 | ## Третий запуск
51 |
52 | * Заходим в какой-нибудь свой любимый (а можно и временный) каталог. Запускаем `jupyter lab` или `jupiter notebook`.
53 | * Если всё в порядке, нам показывают браузер с красотой
54 |
55 | - - - - - - - - - - - - -
56 | ## Экспериментируем с типами данных в Python при помощи Jupyter
57 |
58 | **Прикладные** [встроенные типы и операции над ними](https://docs.python.org/3/library/stdtypes.html)
59 |
60 | 1. `bool`
61 | 2. `int`, `float`, `complex`
62 | 3. `tuple`, `list`, `range`, `slice`
63 | 4. `set`, `dict`
64 | 5. `str`, `bytes` (в Python 2 — `unicode`, `str`)
65 | 6. Функции
66 |
67 | Для этого воспользуемся [следующим блокнотом](../../jupiter-notebooks/01.operation_basics.ipynb)
68 |
69 | = = = = = = = = = = = = =
70 | # Задания
71 |
72 | - - - - - - - - - - - - -
73 | ## Задание 1. Первая программа I
74 |
75 | Исходный код:
76 |
77 | ```
78 | #!/usr/bin/env python3
79 | # -*- coding: utf-8 -*-
80 |
81 | import math
82 | import numpy
83 | import matplotlib.pyplot as mpp
84 |
85 | # Эта программа рисует график функции, заданной выражением ниже
86 |
87 | if __name__=='__main__':
88 | arguments = numpy.arange(0, 200, 0.1)
89 | mpp.plot(
90 | arguments,
91 | [math.sin(a) * math.sin(a/20.0) for a in arguments]
92 | )
93 | mpp.show()
94 |
95 | ```
96 |
97 | - - - - - - - - - - - - -
98 | ## Задание 1. Первая программа II
99 |
100 | ### Программа
101 |
102 | 0. По желанию можно дописать или изменить функцию
103 | 1. Придумать программе название
104 | 2. Прокомментировать каждую строку, котрую можно прокомментировать (комментарий — любой текст от `#` до конца строки)
105 |
106 | - - - - - - - - - - - - -
107 | ## Задание 2. GitHub
108 |
109 | 0. Установить Git и создать репозиторий — это мы уже сделали
110 |
111 | 1. Клонировать его локально (`git clone https://github.com/..my_nick../..my_repo..`)
112 | 2. Добавить первую программу в репозиторий (`git add ....py`, затем `git commit -m "Welcome to the Future!"`)
113 | 3. Опубликовать программу (`git push origin master`)
114 | 4. Проверить изменения при помощи Web-интерфейса GitHub
115 | 5. Сообщить преподавателю, что задание сделано, чтобы преподаватель понял, чьи программы можно проверять
116 |
117 | Если преподаватель не успел достаточно рассказать про Git — попробуйте, как получится, хотя не вредно почитать [и руководство](https://git-scm.com/book/ru/v2).
118 |
119 | - - - - - - - - - - - - -
120 | ## Задание 3. Элементарные функции
121 |
122 | 1. Выполнить задание из [блокнота 2](../../jupiter-notebooks/02.taylor.ipynb) на элементарную функцию
123 | 2. Получившийся блокнот также опубликовать в репозитории
124 |
125 | = = = = = = = = = = = = =
126 |
127 | # Спасибо!
128 |
129 | 
130 |
131 |
--------------------------------------------------------------------------------
/docs/jupiter-notebooks/02.taylor.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Чуть-чуть математики\n",
8 | "\n",
9 | "Теперь давайте попробуем написать функцию. Например, чтобы приближённо считала синус. Для небольших $x$:\n",
10 | "\n",
11 | "$$\\sin x = x - \\frac{x^3}{3!} + \\frac{x^5}{5!} - \\frac{x^7}{7!} + \\ldots + R(x) =\n",
12 | "\\sum_{n=0}^N (-1)^n \\frac{x^{2n+1}}{(2n+1)!} + R(x),$$\n",
13 | "\n",
14 | "причём $R(x) \\xrightarrow[N \\rightarrow \\infty]{} 0$.\n",
15 | "\n",
16 | "Это частичная сумма т.н. ряда Тейлора:\n",
17 | "\n",
18 | "$$ f(x) = f(a)+\\sum_{k=1}^\\infty {f^{(k)} (a) \\over k!} (x - a)^k. $$"
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": null,
24 | "metadata": {},
25 | "outputs": [],
26 | "source": [
27 | "import math\n",
28 | "\n",
29 | "ITERATIONS = 20\n",
30 | "\n",
31 | "def my_sin(x):\n",
32 | " \"\"\"\n",
33 | " Вычисление синуса при помощи частичного суммирования\n",
34 | " ряда Тейлора для окрестности 0\n",
35 | " \"\"\"\n",
36 | " x_pow = x\n",
37 | " multiplier = 1\n",
38 | " partial_sum = x\n",
39 | " for n in range(1, ITERATIONS):\n",
40 | " x_pow *= x**2 # В цикле постепенно считаем степень\n",
41 | " multiplier *= -1 / (2*n) / (2*n + 1) # (-1)^n и факториал\n",
42 | " partial_sum += x_pow * multiplier\n",
43 | " \n",
44 | " return partial_sum\n",
45 | "\n",
46 | "help(math.sin)\n",
47 | "help(my_sin)\n",
48 | "\n",
49 | "print(math.sin(0.4))\n",
50 | "print(my_sin(0.4))"
51 | ]
52 | },
53 | {
54 | "cell_type": "markdown",
55 | "metadata": {},
56 | "source": [
57 | "выходит у нас немного неточно, но зато...\n",
58 | "\n",
59 | "... Зато наша функция умеет считать синус комплексного аргумента, легко достигающий пяти и более в мирное время. Мнимая единица ($i$) в Питоне обозначется, как `j`.\n",
60 | "\n",
61 | "Стандартная библиотека тоже умеет, **и поточнее**."
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "metadata": {},
68 | "outputs": [],
69 | "source": [
70 | "import cmath\n",
71 | "\n",
72 | "complex_angle = cmath.asin(5)\n",
73 | "print('\"Угол\", на котором синус достигает пяти:', complex_angle)\n",
74 | "\n",
75 | "print(\"Достигает ли пяти наш синус?\", my_sin(complex_angle))\n",
76 | "print(\"А библиотечный?\", cmath.sin(complex_angle))"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "metadata": {},
82 | "source": [
83 | "А какое и где слабое место у нашего синуса?"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": null,
89 | "metadata": {},
90 | "outputs": [],
91 | "source": [
92 | "import matplotlib.pyplot as plt\n",
93 | "%config InlineBackend.figure_formats = ['svg', 'pdf']\n",
94 | "%matplotlib inline\n",
95 | "\n",
96 | "\n",
97 | "import matplotlib.pyplot as plt\n",
98 | "import numpy as np\n",
99 | "\n",
100 | "vs = np.vectorize(my_sin)\n",
101 | "print(my_sin, vs)\n",
102 | "\n",
103 | "angles = np.r_[-16.25:16.25:0.01]\n",
104 | "plt.plot(angles, np.sin(angles), linewidth=3.0, color='cyan')\n",
105 | "plt.plot(angles, vs(angles), linewidth=1.0, color='black')\n",
106 | "plt.show()"
107 | ]
108 | },
109 | {
110 | "cell_type": "markdown",
111 | "metadata": {},
112 | "source": [
113 | "# Задание\n",
114 | "\n",
115 | "Реализовать вычисление частичной суммы [ряда Тейлора для одной из элементарных функций](http://ru.math.wikia.com/wiki/%D0%A0%D1%8F%D0%B4_%D0%A2%D0%B5%D0%B9%D0%BB%D0%BE%D1%80%D0%B0) (кроме синуса 😸), аналогичным образом «испытать» получившиеся функции. "
116 | ]
117 | }
118 | ],
119 | "metadata": {
120 | "interpreter": {
121 | "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
122 | },
123 | "kernelspec": {
124 | "display_name": "Python 3 (ipykernel)",
125 | "language": "python",
126 | "name": "python3"
127 | },
128 | "language_info": {
129 | "codemirror_mode": {
130 | "name": "ipython",
131 | "version": 3
132 | },
133 | "file_extension": ".py",
134 | "mimetype": "text/x-python",
135 | "name": "python",
136 | "nbconvert_exporter": "python",
137 | "pygments_lexer": "ipython3",
138 | "version": "3.10.7"
139 | }
140 | },
141 | "nbformat": 4,
142 | "nbformat_minor": 4
143 | }
144 |
--------------------------------------------------------------------------------
/docs/slides/common/media/fonts/Alegreya/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2011 The Alegreya Project Authors (https://github.com/huertatipografica/Alegreya)
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/docs/slides/common/media/fonts/Merriweather/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2016 The Merriweather Project Authors (https://github.com/EbenSorkin/Merriweather), with Reserved Font Name "Merriweather".
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/docs/examples/13.Evolutionary_Moon_Lander/model.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from __future__ import annotations
3 | from typing import Optional
4 | from enum import IntFlag
5 | from abc import ABC, abstractmethod
6 |
7 | import numpy as np
8 | from numpy import array as vec
9 | from numpy.linalg import norm
10 | import csv
11 | from scipy import interpolate
12 |
13 |
14 | class Relief:
15 | def __init__(self, csv_filename: str):
16 | with open(csv_filename) as cfn:
17 | height_data = [(float(x), float(h)) for [x, h] in csv.reader(cfn)]
18 | xs, hs = tuple(zip(*height_data))
19 | self._relief_length = max(xs)
20 | self._height = interpolate.interp1d(xs, hs)
21 |
22 | def get_width(self):
23 | return self._relief_length
24 |
25 | def get_height(self, x):
26 | return float(self._height(x))
27 |
28 |
29 | class Spaceship:
30 | class Thrust(IntFlag):
31 | NOPE = 0
32 | LEFT = 1
33 | RIGHT = 2
34 | UP = 4
35 |
36 | _moon_g = 1.6
37 | _engine_thrust = 3000.0
38 | _engine_mass_per_sec = 3.0
39 |
40 | def __init__(self, mass: float, velocity: vec, position: vec):
41 | self.maxlandingvelocity: float = 1.0
42 | self.navigates = True
43 | self.ok: bool = True
44 | self.velocity: vec = velocity
45 | self.position: vec = position
46 | self.thrust: Spaceship.Thrust = Spaceship.Thrust.NOPE
47 |
48 | self.dry_mass: float = mass / 2.0
49 | self.fuel_mass: float = mass / 2.0
50 |
51 | def drain_fuel(self, amount: float):
52 | """
53 | Drain fuel to decrease weight
54 | """
55 | self.fuel_mass = max(self.fuel_mass - amount, 0.0)
56 |
57 | @property
58 | def mass(self):
59 | return self.dry_mass + self.fuel_mass
60 |
61 | def advance(self, delta_t: float):
62 | self.position += self.velocity * delta_t
63 | self.velocity += [0.0, -Spaceship._moon_g * delta_t]
64 |
65 | thrust_vec = vec([0.0, 0.0])
66 | dm = 0.0
67 |
68 | if self.fuel_mass > 0.0:
69 | if Spaceship.Thrust.RIGHT & self.thrust:
70 | thrust_vec += [0.25, 0.0]
71 | dm += 0.25
72 | if Spaceship.Thrust.LEFT & self.thrust:
73 | thrust_vec += [-0.25, 0.0]
74 | dm += 0.25
75 | if Spaceship.Thrust.UP & self.thrust:
76 | thrust_vec += [0.0, 1.0]
77 | dm += 1.0
78 |
79 | self.velocity += thrust_vec * delta_t * Spaceship._engine_thrust / self.mass
80 | self.fuel_mass -= Spaceship._engine_mass_per_sec * dm * delta_t
81 |
82 | def land(self):
83 | self.velocity = vec([0.0, 0.0])
84 | self.navigates = False
85 |
86 | def crash(self):
87 | self.land()
88 | self.ok = False
89 |
90 | class Captain(ABC):
91 | def __init__(self):
92 | self.model: Optional[Model] = None
93 | pass
94 |
95 | @abstractmethod
96 | def control(self):
97 | pass
98 |
99 |
100 | class ResponsiveCaptin(Captain):
101 | def __init__(self):
102 | super().__init__()
103 | self.controlled_from_outside = False
104 |
105 | def instruct(self, what_engine: Spaceship.Thrust, turn_on: bool):
106 | self.controlled_from_outside = True
107 |
108 | def free(self):
109 | self.controlled_from_outside = False
110 |
111 |
112 | class Model:
113 | def __init__(self, rl: Relief, ss: Spaceship, cap: Captain, default_delta_t = 0.01):
114 | self.default_delta_t: float = default_delta_t
115 | self.relief: Relief = rl
116 | self.spaceship: Spaceship = ss
117 | self.cap = cap
118 | cap.model = self
119 | self.time = 0.0
120 |
121 | def step_delta_t(self, delta_t):
122 | self.cap.control()
123 | self.spaceship.advance(delta_t)
124 | self.time += delta_t
125 | [sx, sy] = self.spaceship.position
126 | sv = norm(self.spaceship.velocity)
127 | if self.relief.get_height(sx) >= sy:
128 | self.spaceship.navigates = False
129 | if sv <= self.spaceship.maxlandingvelocity:
130 | print("Landing ok!")
131 | self.spaceship.land()
132 | else:
133 | print("Crash...")
134 | self.spaceship.crash()
135 |
136 | def step(self) -> bool:
137 | self.step_delta_t(self.default_delta_t)
138 | return self.spaceship.navigates
139 |
140 | def run_to(self, up_to_time: float = None) -> bool:
141 | if up_to_time is None:
142 | up_to_time = self.time + self.default_delta_t
143 |
144 | while self.time < up_to_time and self.spaceship.navigates:
145 | self.step_delta_t(self.default_delta_t)
146 |
147 | return self.spaceship.navigates
148 |
149 |
150 | if __name__ == '__main__':
151 | raise NotImplementedError("This file is not supposed to be launched as a program")
152 |
--------------------------------------------------------------------------------
/docs/jupiter-notebooks/13.Evolutionary_Moon_lander.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "## Задача\n",
8 | "\n",
9 | "Для вдохновения\n",
10 | "\n",
11 | "### Посадка на луну\n",
12 | "\n",
13 | "1. https://m.habr.com/ru/post/400013/\n",
14 | "2. https://m.habr.com/ru/post/400735/\n",
15 | "\n",
16 | "### Игровой автомат\n",
17 | "\n",
18 | "* [Сам автомат и исторя разработки](https://en.wikipedia.org/wiki/Lunar_Lander_(1979_video_game))\n",
19 | "* [Симулятор автомата](http://moonlander.seb.ly/)\n",
20 | "\n",
21 | "## Решение\n",
22 | "\n",
23 | "* Модель\n",
24 | " * Предлагается упростить лунный модуль до материальной точки\n",
25 | " * Три двигателя — вверх (сопло вниз), влево, вправо\n",
26 | " * Поверхность лугы предлагается сделать ступенчатой\n",
27 | "* Графика\n",
28 | " * Предлагается использвоать PyGame (ведь у нас уже есть опыт!)\n",
29 | " * Предлагается не встраивать модель, а научиться брать из неё данные и передавать ей управление\n",
30 | "* Переносимость и DevOps\n",
31 | " * Должно работать под Windows и Linux\n",
32 | " * Для мобильных... Вот тут надо подумать, м.б. и не PyGame\n",
33 | " * Ну и д.б. проект c кодом в Гите и всё такое\n",
34 | "\n",
35 | "## Как можно научить аппарат садиться самостоятельно?\n",
36 | "\n",
37 | "Отрастить автопилот. Именно для этого и следует отвязать модель от игры\n",
38 | "\n",
39 | "* Аналитическое решение [задачи оптимизации](https://ru.wikipedia.org/wiki/%D0%9E%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0))\n",
40 | " * В данном случае — [вариационного исчисления](https://ru.wikipedia.org/wiki/%D0%92%D0%B0%D1%80%D0%B8%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%B5_%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5)\n",
41 | "* Численное решение при помощи машинного обучения\n",
42 | " * [Градиентный спуск](https://ru.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D0%B4%D0%B8%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D1%83%D1%81%D0%BA), применительно к задачам, аналогичным поставленной — [нейронные сети](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%B9%D1%80%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D0%B5%D1%82%D1%8C)\n",
43 | " * Когда модель совсем непонятная — генетический алгоритм. Вообще это не наш случай, поэтому... давайте попробуем! =)\n",
44 | " * Обучающие алгоритмы будут быстро «гонять» модель, и задавать ей оптимальное управление\n",
45 | "\n",
46 | "## Генетические алгоритмы\n",
47 | "\n",
48 | "Введение\n",
49 | "\n",
50 | "* Запустим на фоне Box Car 2D https://rednuht.org/genetic_cars_2/\n",
51 | "* [Что это вообще такое?](https://dluciv.github.io/algs_and_data_structs-spbu-CB.5001/slides.html?md=11.evolution)\n",
52 | "* Пример всяких мелких генетических (на самом деле не только) проектов https://awesomeopensource.com/projects/genetic-algorithm\n",
53 | "\n",
54 | "Варианты исполнения\n",
55 | "\n",
56 | "* Проще — сделать самим\n",
57 | "* Правильнее — разобраться с\n",
58 | " * https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.differential_evolution.html, или \n",
59 | " * https://deap.readthedocs.io/, или\n",
60 | " * любым другим фреймворком\n",
61 | "\n",
62 | "Варианты генома автопилота\n",
63 | "\n",
64 | "* Дискретное время, битовая шкала — например, раз в 1/2 секунды включать/выключать разные двигатели\n",
65 | " * Лёгкие и понятные генетические операции\n",
66 | "* Набор пауз и интервалов\n",
67 | " * Геном компактнее, но эволюция сложнее"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "metadata": {},
73 | "source": [
74 | "## Распределение ролей\n",
75 | "\n",
76 | "1. Физическая модель (и классы/объекты) — Луцив Д.\n",
77 | "2. PyGame и графика — несколько человек\n",
78 | "3. Кросплатформенность — Гуняга А.\n",
79 | "4. Из чего состоит геном — Луцив Д.В.\n",
80 | "5. Генетический алгоритм — Харитонцева-Беглова Е. и ещё кто-нибудь\n",
81 | " * В любом случае для ф-ции качества имеет смысл numba\n",
82 | " * Фреймворки\n",
83 | " * Самопальный\n",
84 | " * И/или при помощий фреймворка"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "metadata": {},
91 | "outputs": [],
92 | "source": []
93 | }
94 | ],
95 | "metadata": {
96 | "kernelspec": {
97 | "display_name": "Python 3",
98 | "language": "python",
99 | "name": "python3"
100 | },
101 | "language_info": {
102 | "codemirror_mode": {
103 | "name": "ipython",
104 | "version": 3
105 | },
106 | "file_extension": ".py",
107 | "mimetype": "text/x-python",
108 | "name": "python",
109 | "nbconvert_exporter": "python",
110 | "pygments_lexer": "ipython3",
111 | "version": "3.8.2"
112 | }
113 | },
114 | "nbformat": 4,
115 | "nbformat_minor": 4
116 | }
--------------------------------------------------------------------------------
/docs/jupiter-notebooks/08.5.Lennard-Jones/real_gas.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # example simulation: https://youtu.be/pQOashWcvMs
4 |
5 | import numba
6 | import matplotlib.pyplot as plt
7 | import numpy
8 | from numpy import array as mkvec
9 | from numpy.linalg import norm
10 |
11 | from numba import njit, prange
12 | # prange = range
13 | # def njit(*a, **k):
14 | # def d(f):
15 | # return f
16 | # return d
17 |
18 | # ### Определим константы
19 | PARTICLE_EPSILON: float = 1.0e0
20 | PARTICLE_SIGMA : float = 5.0e0
21 | PARTICLE_MASS : float = 1.0
22 |
23 | MODEL_PRESS_C : float = 1.0e2
24 | MODEL_PRESS_FALL: float = 0.9
25 |
26 | PARTICLE_SAME : float = 0.01
27 | PARTICLE_FAR : float = 0.1
28 |
29 | PARTICLE_NUMBER : int = 300
30 |
31 | PARTICLE_I_VEL : float = 5e-3 / PARTICLE_NUMBER
32 |
33 | MODEL_DELTA_T : float = 1e-6
34 | MODEL_TIME_TO : float = 2e-3
35 |
36 | MODEL_STEPS_PER_FRAME : int = 5
37 |
38 | @njit(
39 | # numba.float64(numba.float64),
40 | fastmath=True, inline='always')
41 | def r_force_lennard_jones(r: float)->float:
42 | if PARTICLE_SAME <= r <= PARTICLE_FAR:
43 | return 24.0 * PARTICLE_EPSILON * (PARTICLE_SIGMA**6 / r**7 - 2 * PARTICLE_SIGMA**12 / r**13)
44 | else:
45 | return 0.0
46 |
47 | @njit(
48 | # numba.float64(numba.float64),
49 | fastmath=True, inline='always')
50 | def r_force(r: float)->float:
51 | if PARTICLE_SAME <= r <= PARTICLE_FAR:
52 | return PARTICLE_EPSILON * PARTICLE_SIGMA / r**3.5
53 | else:
54 | return 0.0
55 |
56 |
57 | zerovec = mkvec([0.0, 0.0], dtype=float)
58 | zeros = numpy.zeros((PARTICLE_NUMBER, 2), dtype=float)
59 |
60 | @njit(
61 | # numba.float64[:](numba.float64[:], numba.float64[:]),
62 | fastmath=True, inline='always'
63 | )
64 | def force_induced(to_p, by_p):
65 | r = norm(to_p - by_p)
66 | f = r_force(r)
67 | if f != 0.0:
68 | return f * (to_p - by_p) / r
69 | else:
70 | return zerovec
71 |
72 | def init_particles():
73 | global curr_coordinates, prev_coordinates
74 | curr_coordinates = numpy.random.uniform(low=0.0, high=1.0, size=(PARTICLE_NUMBER, 2))
75 | curr_coordinates *= mkvec([1, 1/3]) # сдвинем все в нижнюю треть стакана
76 | prev_coordinates = curr_coordinates +\
77 | numpy.random.uniform(low=-PARTICLE_I_VEL, high=PARTICLE_I_VEL, size=(PARTICLE_NUMBER, 2))
78 |
79 | init_particles()
80 |
81 | @njit(
82 | numba.float64[:](numba.int32),
83 | fastmath=True
84 | )
85 | def particle_velocity(idx: int):
86 | return (curr_coordinates[idx] - prev_coordinates[idx]) / MODEL_DELTA_T
87 |
88 | @njit(
89 | numba.float64(numba.float64[:,:],numba.float64[:,:]),
90 | fastmath=True, nogil=True
91 | )
92 | def model_step(cc, pc):
93 | nc = (2.0 * cc - pc)
94 | pressure = 0.0
95 |
96 | for i in prange(PARTICLE_NUMBER):
97 | force_i = zerovec.copy()
98 | # другие частицы
99 | for j in range(PARTICLE_NUMBER):
100 | if j != i:
101 | force_i += force_induced(cc[i], cc[j])
102 | nc[i] += force_i / PARTICLE_MASS * MODEL_DELTA_T ** 2 # $a \delta_t^2$
103 |
104 | # от стенок лучше просто отражаться
105 | for k in range(2):
106 | if not (0.0 <= nc[i,k] <= 1.0): # когда вылетели по k-й координате из кастрюли
107 | if k == 1 and nc[i,k] < 0.0: # отскок от дна
108 | pressure += MODEL_PRESS_C * PARTICLE_MASS * (cc[i,k] - nc[i,k])
109 | nc[i,k], cc[i,k] = cc[i,k], nc[i,k] # делаем, будто не вылетели, а влетели
110 |
111 | # и так numba не умеет
112 | # prev_coordinates = curr_coordinates
113 | # curr_coordinates = next_coordinates
114 | # и так тоже =)
115 | # numpy.copyto(prev_coordinates, curr_coordinates)
116 | # numpy.copyto(curr_coordinates, next_coordinates)
117 | pc[:] = cc[:]
118 | cc[:] = nc[:]
119 | return pressure
120 |
121 | import matplotlib.animation as animation
122 | # %matplotlib interactive
123 |
124 | fig, ax = plt.subplots()
125 |
126 | plt.xlim([0, 1])
127 | plt.ylim([0, 1])
128 |
129 |
130 | init_particles()
131 | particles, = plt.plot(curr_coordinates[1:,0], curr_coordinates[1:,1], 'b.')
132 | particle0, = plt.plot(curr_coordinates[0, 0], curr_coordinates[0, 1], 'r.')
133 | label = ax.text(0, 0, "Frame: 0")
134 | pressure = 0.0
135 |
136 | def animate(frame_no):
137 | global pressure
138 | if 0 == frame_no:
139 | init_particles()
140 | for _ in range(MODEL_STEPS_PER_FRAME):
141 | pressure += model_step(curr_coordinates, prev_coordinates)
142 | pressure *= MODEL_PRESS_FALL
143 | label.set_text(f"Frame: {frame_no}, P: {pressure}")
144 | particles.set_data(curr_coordinates[1:,0], curr_coordinates[1:,1])
145 | particle0.set_data(curr_coordinates[0, 0], curr_coordinates[0, 1])
146 |
147 | ma = animation.FuncAnimation(
148 | fig, animate,
149 | frames=round(MODEL_TIME_TO / MODEL_DELTA_T),
150 | interval=50, blit=False, repeat=False
151 | )
152 |
153 | plt.show()
154 | ma.save("rg.mp4")
155 |
--------------------------------------------------------------------------------
/docs/slides/02.First_Practice/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Немного практики
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
80 |
81 |
82 |
89 |
90 |
91 |
92 |
93 |