├── docs ├── _config.yml ├── examples │ ├── 03.parallel │ │ ├── mpi4py │ │ │ ├── hostfile │ │ │ ├── mpitest.py │ │ │ └── mpitest_arbitrary_data_size.py │ │ ├── threading_no_effect.py │ │ ├── multiprocessing_effect.py │ │ ├── coroutines_sync.py │ │ ├── coroutines_async_311.py │ │ ├── coroutines_async_310.py │ │ ├── pyqt6_sync.py │ │ ├── pyside6_sync.py │ │ ├── pyside6_async.py │ │ └── pyqt6_async.py │ ├── 03.5.modules │ │ ├── package1 │ │ │ ├── __init__.py │ │ │ ├── subpack2 │ │ │ │ └── module3.py │ │ │ ├── module1.py │ │ │ └── module2.py │ │ ├── module1.py │ │ ├── module2.py │ │ ├── use2.py │ │ └── use1.py │ ├── 13.Evolutionary_Moon_Lander │ │ ├── requirements.txt │ │ ├── images │ │ │ └── background.jpg │ │ ├── surface_heights.csv │ │ ├── try_something.py │ │ ├── captain.py │ │ ├── moon_game.py │ │ └── model.py │ ├── 02.75.NedoForth │ │ ├── test_01.nf │ │ ├── test_02.nf │ │ └── nedoforth.py │ ├── 01.quality │ │ ├── impl_gauss.py │ │ ├── test_gauss.py │ │ ├── rabin_karp_with_tests.py │ │ └── prime_check_with_tests.py │ ├── 04.web │ │ ├── static │ │ │ └── static.html │ │ ├── templates │ │ │ ├── index.html │ │ │ ├── hello_anon.html │ │ │ └── base.html │ │ ├── sample_app.py │ │ └── README.md │ ├── 02.5.typing │ │ ├── typeguard_test.py │ │ ├── mypy_bug.py │ │ ├── mypy_test.py │ │ ├── typeguard_bug.py │ │ └── README.md │ ├── 01_5.Turtle │ │ ├── fractal1.py │ │ ├── fractal2.py │ │ └── two_turtles.py │ ├── 06.GUI │ │ ├── main_dialog_qt6.py │ │ ├── main_dialog_pyside6.py │ │ ├── main_dialog_qt6_autobind_task.py │ │ └── ui │ │ │ └── main_dialog.ui │ ├── 05.multiple_inheritance │ │ └── multiple_unheritance_demo.py │ └── 02.Kepler │ │ └── typed_kepler.py ├── jupiter-notebooks │ ├── img │ │ └── oop.jpg │ ├── 08.just-in-time.ipynb │ ├── 02.taylor.ipynb │ ├── 13.Evolutionary_Moon_lander.ipynb │ ├── 08.5.Lennard-Jones │ │ └── real_gas.py │ ├── 06.relax_coding.ipynb │ ├── 03.5.Artillery.ipynb │ └── 01.operation_basics.ipynb └── slides │ ├── 01.Python_and_slightly_GitHub │ ├── images │ │ ├── 1791.gif │ │ ├── Octocat.jpg │ │ ├── tired-goat.jpg │ │ ├── git-workflow.png │ │ ├── nosql-expert.gif │ │ ├── GoldenHammer-ru.gif │ │ ├── dilbert-meeting.jpg │ │ ├── github-workflow.png │ │ ├── octoverse-2020.png │ │ └── dilbert-training.gif │ ├── index.html │ └── content.md │ ├── common │ ├── images │ │ ├── qr-edu.dluciv.name-address.png │ │ ├── vignette-e.svg │ │ └── vignette-1.svg │ └── media │ │ └── fonts │ │ ├── Alegreya │ │ ├── Alegreya-Black.ttf │ │ ├── Alegreya-Bold.ttf │ │ ├── Alegreya-Italic.ttf │ │ ├── Alegreya-Medium.ttf │ │ ├── Alegreya-Regular.ttf │ │ ├── Alegreya-BoldItalic.ttf │ │ ├── Alegreya-ExtraBold.ttf │ │ ├── Alegreya-BlackItalic.ttf │ │ ├── Alegreya-MediumItalic.ttf │ │ ├── Alegreya-ExtraBoldItalic.ttf │ │ ├── alegreya.css │ │ └── OFL.txt │ │ ├── OpenSans │ │ ├── OpenSans-Bold.ttf │ │ ├── OpenSans-Italic.ttf │ │ ├── OpenSans-Regular.ttf │ │ ├── OpenSans-BoldItalic.ttf │ │ └── opensans.css │ │ ├── Terminus │ │ ├── terminus-bold.ttf │ │ └── terminus.css │ │ ├── Pacifico │ │ ├── Pacifico-Regular.ttf │ │ └── pacifico.css │ │ └── Merriweather │ │ ├── Merriweather-Bold.ttf │ │ ├── Merriweather-Black.ttf │ │ ├── Merriweather-Italic.ttf │ │ ├── Merriweather-Regular.ttf │ │ ├── Merriweather-BlackItalic.ttf │ │ ├── Merriweather-BoldItalic.ttf │ │ ├── Merriweather.css │ │ └── OFL.txt │ ├── 02.5.quality.md │ ├── 02.First_Practice │ ├── content.md │ └── index.html │ ├── 03.parallel.md │ └── slides.html ├── tools ├── plagiarism-reporter │ ├── requirements.txt │ ├── report_export.py │ ├── sourcedrop.py │ └── inclusion_report.py └── studgit │ └── uhw.rb ├── .gitmodules └── .gitignore /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/examples/03.parallel/mpi4py/hostfile: -------------------------------------------------------------------------------- 1 | 127.0.0.1 slots=8 2 | -------------------------------------------------------------------------------- /docs/examples/03.5.modules/package1/__init__.py: -------------------------------------------------------------------------------- 1 | def p1f1(): 2 | return 'p1f1' -------------------------------------------------------------------------------- /tools/plagiarism-reporter/requirements.txt: -------------------------------------------------------------------------------- 1 | chardet 2 | odfpy 3 | tqdm 4 | tzlocal 5 | -------------------------------------------------------------------------------- /docs/examples/03.5.modules/package1/subpack2/module3.py: -------------------------------------------------------------------------------- 1 | def p1s2m3f1(): 2 | return 'p1s2m3f1' 3 | -------------------------------------------------------------------------------- /docs/examples/13.Evolutionary_Moon_Lander/requirements.txt: -------------------------------------------------------------------------------- 1 | pygame 2 | numpy 3 | numba 4 | scipy 5 | matplotlib 6 | 7 | -------------------------------------------------------------------------------- /docs/examples/02.75.NedoForth/test_01.nf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ./nedoforth.py 2 | ввод 3 | input 4 | 5 5 | стек 6 | + 7 | - 8 | top 9 | -------------------------------------------------------------------------------- /docs/jupiter-notebooks/img/oop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/jupiter-notebooks/img/oop.jpg -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/slides/reveal.js"] 2 | path = docs/slides/reveal.js 3 | url = https://github.com/hakimel/reveal.js.git 4 | -------------------------------------------------------------------------------- /docs/examples/01.quality/impl_gauss.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def gauss(a, b): 4 | """Конечно же такая функция не пройдёт тест, допишите её""" 5 | return b 6 | -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/1791.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/1791.gif -------------------------------------------------------------------------------- /docs/slides/common/images/qr-edu.dluciv.name-address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/images/qr-edu.dluciv.name-address.png -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-Black.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-Bold.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/OpenSans/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/OpenSans/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Terminus/terminus-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Terminus/terminus-bold.ttf -------------------------------------------------------------------------------- /docs/examples/02.75.NedoForth/test_02.nf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ./nedoforth.py 2 | ввод 3 | 2 4 | ** 5 | input 6 | 2 7 | ** 8 | + 9 | 0.5 10 | ** 11 | top 12 | -11 13 | отпрыг 14 | -------------------------------------------------------------------------------- /docs/examples/03.5.modules/module1.py: -------------------------------------------------------------------------------- 1 | import module2 2 | 3 | def m1f1(): 4 | return 'm1f1' 5 | 6 | def _m1f2(): 7 | return '_m1f2' 8 | 9 | print(__name__, "was imported") -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/Octocat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/Octocat.jpg -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-Italic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-Medium.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-Regular.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/OpenSans/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/OpenSans/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/OpenSans/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/OpenSans/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Pacifico/Pacifico-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Pacifico/Pacifico-Regular.ttf -------------------------------------------------------------------------------- /docs/examples/03.5.modules/package1/module1.py: -------------------------------------------------------------------------------- 1 | import module1 # !!! очень подозрительно — import чего-то «сверху» 2 | 3 | def p1m1f1(): 4 | return 'p1m1f1 -> ' + module1.m1f1() 5 | -------------------------------------------------------------------------------- /docs/examples/13.Evolutionary_Moon_Lander/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/examples/13.Evolutionary_Moon_Lander/images/background.jpg -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/tired-goat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/tired-goat.jpg -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-BoldItalic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-ExtraBold.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/OpenSans/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/OpenSans/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/git-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/git-workflow.png -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/nosql-expert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/nosql-expert.gif -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-BlackItalic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-MediumItalic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Merriweather/Merriweather-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Merriweather/Merriweather-Bold.ttf -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/GoldenHammer-ru.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/GoldenHammer-ru.gif -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/dilbert-meeting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/dilbert-meeting.jpg -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/github-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/github-workflow.png -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/octoverse-2020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/octoverse-2020.png -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Alegreya/Alegreya-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Alegreya/Alegreya-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Merriweather/Merriweather-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Merriweather/Merriweather-Black.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Merriweather/Merriweather-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Merriweather/Merriweather-Italic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Merriweather/Merriweather-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Merriweather/Merriweather-Regular.ttf -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/images/dilbert-training.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/01.Python_and_slightly_GitHub/images/dilbert-training.gif -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Merriweather/Merriweather-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Merriweather/Merriweather-BlackItalic.ttf -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Merriweather/Merriweather-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dluciv/python-intro-course/HEAD/docs/slides/common/media/fonts/Merriweather/Merriweather-BoldItalic.ttf -------------------------------------------------------------------------------- /docs/examples/03.5.modules/module2.py: -------------------------------------------------------------------------------- 1 | import module1 2 | 3 | __all__ = ['m2f1'] 4 | 5 | def m2f1(): 6 | return 'm2f1' 7 | 8 | def m2f2(): 9 | return 'm2f2' 10 | 11 | print(__name__, "was imported") -------------------------------------------------------------------------------- /docs/examples/03.5.modules/use2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import package1 4 | from package1 import module1, module2 5 | 6 | print(package1.p1f1()) 7 | print(module1.p1m1f1()) 8 | print(module2.p1m2f1()) 9 | -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Pacifico/pacifico.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Pacifico'; 3 | src: url(Pacifico-Regular.ttf) format('truetype'); 4 | font-weight: bold; 5 | font-style: normal; 6 | } 7 | -------------------------------------------------------------------------------- /docs/slides/common/media/fonts/Terminus/terminus.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Terminus'; 3 | src: url(terminus-bold.ttf) format('truetype'); 4 | font-weight: bold; 5 | font-style: normal; 6 | } 7 | -------------------------------------------------------------------------------- /docs/examples/13.Evolutionary_Moon_Lander/surface_heights.csv: -------------------------------------------------------------------------------- 1 | 0,100 2 | 68,110 3 | 140,130 4 | 168,130 5 | 300,93 6 | 365,40 7 | 533,40 8 | 677,100 9 | 820,100 10 | 963,140 11 | 991,140 12 | 1242,70 13 | 1357,70 14 | 1500,90 15 | -------------------------------------------------------------------------------- /docs/examples/04.web/static/static.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Статическая страница 5 | 6 | 7 | Статическая страница 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/examples/02.5.typing/typeguard_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from numbers import Number 4 | from typeguard import typechecked 5 | 6 | @typechecked 7 | def handle_simple(i: Number) -> None: 8 | print(str(i) + "abc") 9 | 10 | handle_simple('123') 11 | -------------------------------------------------------------------------------- /docs/examples/03.5.modules/package1/module2.py: -------------------------------------------------------------------------------- 1 | from . import module1 # а вот тут хорошо — import из своего пакета 2 | from .subpack2 import module3 # или из пакета/модуля на одном уровне с собой 3 | 4 | def p1m2f1(): 5 | return 'p1m2f1 -> ' + module3.p1s2m3f1() + ' -> ' + module1.p1m1f1() 6 | -------------------------------------------------------------------------------- /docs/examples/02.5.typing/mypy_bug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from numbers import Number 4 | 5 | # MyPy считает, что int — не Number https://github.com/python/mypy/issues/3186 6 | 7 | def handle_simple(i: Number) -> None: 8 | print(str(i) + "abc") 9 | 10 | handle_simple(123) 11 | -------------------------------------------------------------------------------- /docs/examples/02.5.typing/mypy_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | def handle_simple(i: int) -> None: 4 | print(str(i) + "abc") 5 | 6 | handle_simple('123') 7 | 8 | def handle_strings_or_ints(values: list[str | int]) -> str: 9 | return " + ".join(map(str, values)) 10 | 11 | print(handle_strings_or_ints([1, 2, "abc", 3.5])) 12 | -------------------------------------------------------------------------------- /docs/examples/04.web/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |

Привет! Можно:

5 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /docs/examples/03.5.modules/use1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import module1 4 | import module2 5 | 6 | from module1 import * 7 | from module2 import * 8 | 9 | print(m1f1()) 10 | 11 | # print(_m1f2()) вызовет ошибку, так как с _ начинаются внутренние имена 12 | print(module1._m1f2()) # а так можно 13 | 14 | print(m2f1()) 15 | # print(m2f2()) вызовет ошибку, так как с m2f2 явно не указано в __all__ в module2 16 | print(module2.m2f2()) # а так можно 17 | -------------------------------------------------------------------------------- /docs/examples/04.web/templates/hello_anon.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |

Hello, {{name}}!

5 | 6 |

Method used: {{method}}

7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /docs/examples/01_5.Turtle/fractal1.py: -------------------------------------------------------------------------------- 1 | import turtle as tl 2 | 3 | def draw_fractal(scale): 4 | if scale >= 5: 5 | draw_fractal(scale / 3.0) 6 | tl.left(45) 7 | draw_fractal(scale / 3.0) 8 | tl.right(90) 9 | draw_fractal(scale / 3.0) 10 | tl.left(45) 11 | draw_fractal(scale / 3.0) 12 | else: 13 | tl.forward(scale) 14 | 15 | scale = 400 16 | tl.pensize(2) 17 | tl.penup() 18 | tl.goto(-scale, -scale/4) 19 | tl.pendown() 20 | 21 | draw_fractal(scale) 22 | tl.done() 23 | -------------------------------------------------------------------------------- /docs/examples/02.5.typing/typeguard_bug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typeguard import typechecked 4 | import typing 5 | 6 | @typechecked 7 | def handle_strings_or_ints_py_3_10(values: list[str | int]) -> str: 8 | return "OK: " + " + ".join(map(str, values)) 9 | 10 | @typechecked 11 | def handle_strings_or_ints_py_3_9(values: list[typing.Union[str | int]]) -> str: 12 | return "OK: " + " + ".join(map(str, values)) 13 | 14 | print(handle_strings_or_ints_py_3_10([1, 2, "abc", 3.5])) 15 | 16 | print(handle_strings_or_ints_py_3_9([1, 2, "abc", 3.5])) 17 | -------------------------------------------------------------------------------- /docs/examples/03.parallel/threading_no_effect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import timeit 5 | import threading 6 | 7 | 8 | def n_randoms(n): 9 | s = 123 10 | m = 2**16+1 11 | a = 75 12 | c = 74 13 | for i in range(n): 14 | s = (a*s + c) % m 15 | 16 | return s 17 | 18 | def test_all(): 19 | t = threading.Thread(target=lambda : n_randoms(10000000)) 20 | t.start() 21 | # n_randoms(10000000) 22 | n_randoms(10000000) 23 | t.join() 24 | 25 | if __name__ == '__main__': 26 | print(timeit.timeit(test_all, number=1)) 27 | -------------------------------------------------------------------------------- /docs/examples/04.web/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Мой первый сайт — глупый, но такой милый! 5 | 6 | 7 |

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 | ![](../common/images/qr-edu.dluciv.name-address.png) 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 |
94 | 95 | 96 |
97 | 98 | 99 |
104 |
105 | 106 | 107 |
108 | 109 |
110 | 111 | 112 | 113 | 114 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Python: что это, откуда и зачем 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 |
94 | 95 | 96 |
97 | 98 | 99 |
104 |
105 | 106 | 107 |
108 | 109 |
110 | 111 | 112 | 113 | 114 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/jupiter-notebooks/06.relax_coding.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Немного простого кодирования\n", 8 | "\n", 9 | "Для того чтобы размяться, расслабиться и получить ещё немножко удовольствия и очков.\n", 10 | "\n", 11 | "## A. Алгоритм Евклида\n", 12 | "\n", 13 | "[Тот самый](http://e-maxx.ru/algo/export_euclid_algorithm).\n", 14 | "\n", 15 | "Даны $a$ и $b$.\n", 16 | "\n", 17 | "$$\n", 18 | "\\text{gcd}(a,b) = \\begin{cases}\n", 19 | "a, & b = 0\\\\\n", 20 | "\\text{gcd}(b, a\\mod{}b), & b \\ne 0\n", 21 | "\\end{cases}\n", 22 | "$$\n", 23 | "\n", 24 | "Реализовать $\\text{gcd}$ на Python." 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "## B. Расширенный Алгоритм Евклида\n", 32 | "\n", 33 | "Тоже [тот самый](http://e-maxx.ru/algo/export_extended_euclid_algorithm).\n", 34 | "\n", 35 | "Даны $a$ и $b$. Обратиться к своим знаниям алгебры (если они не помогли, то можно сходить по ссылке выше) и реализовать $\\text{egcd}$, такую что $\\text{egcd}(a, b) = \\left$, такие что $\\text{gcd}(a, b) = ax + by$." 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "## C. Реализовать функцию, которая отвечает, простое число, или нет\n", 43 | "\n", 44 | "Т.е. возвращает `True`, если оно простое, и `False` в противном случае.\n", 45 | "Можно совершенно «в лоб», с проверкой на делимость, но чтобы проверок было не больше, чем необходимо.\n", 46 | "\n", 47 | "Дальше возможны два варианта.\n", 48 | "\n", 49 | "### Кеширование\n", 50 | "\n", 51 | "Но поскольку это решение неоптимально, следует кешировать результаты функций, чтобы не считать многократно то, что уже посчитано. Например, функция может проверять делимость только на простые числа, если простые до какого-то числа известны. Можно пользоваться глобальной переменной (словарём или списком) для того, чтобы накапливать знания При этом при помощи директивы `%timeit` можно посмотреть, насколько мемоизация ускорит работу функции." 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "def is_simple(value):\n", 61 | " raise NotImplementedError(\"Ой, меня не запрограммировали...\")" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": { 67 | "tags": [] 68 | }, 69 | "source": [ 70 | "### Больше про всякие хитрые проверки на простоту\n", 71 | "\n", 72 | "[Можно прочитать тут](https://dluciv.github.io/algs_and_data_structs-spbu-CB.5001/slides.html?md=b5.02.randomized#/9). Кратко подытожим.\n", 73 | "\n", 74 | "#### Хранение списка простых, не превосходящих какого-то числа\n", 75 | "\n", 76 | "Проверять $n$ на простоту можно, пытаясь делить на простые числа, не превосходящие $\\left\\lfloor\\sqrt{n}\\right\\rfloor$. Как показывают теория и практика (см. слайды по ссылке выше), если $n$ небольшое, их не так и много. Если использовать `@lru_cache`, постепенно они в кэш и набьются, но это будет слишком недетерминированно. Так что кэшировать стоит как-то иначе, возможно сколько-то (немного) первых простых стоит хранить явно?\n", 77 | "\n", 78 | "#### Вероятностная проверка на простоту\n", 79 | "\n", 80 | "Также можно поступить, как в криптографии — проверить на простоту вероятностным способом, а если выяснится, что число простое — проверить уже точно." 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 5, 86 | "metadata": {}, 87 | "outputs": [ 88 | { 89 | "name": "stdout", 90 | "output_type": "stream", 91 | "text": [ 92 | "Проверяем тестом Ферма число Кармайкла 6601 = 7*23*41 по 1000 раз с разным количеством итераций, и смотрим, в каком промилле случаев тест ошибся\n", 93 | "1 итераций, 0‰ ошибок\n", 94 | "3 итераций, 0‰ ошибок\n", 95 | "10 итераций, 0‰ ошибок\n", 96 | "15 итераций, 0‰ ошибок\n", 97 | "20 итераций, 0‰ ошибок\n", 98 | "25 итераций, 0‰ ошибок\n", 99 | "30 итераций, 0‰ ошибок\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "import random\n", 105 | "\n", 106 | "rg = random.Random()\n", 107 | "\n", 108 | "def fermat_prime_or_pseudoprime(n, iter=25):\n", 109 | " for _ in range(iter):\n", 110 | " a = rg.randint(2, n-1)\n", 111 | " if pow(a, n-1, n) != 1:\n", 112 | " return False\n", 113 | " return True\n", 114 | "\n", 115 | "checks = 2000\n", 116 | "print(\"Проверяем тестом Ферма число Кармайкла 6601 = 7*23*41 по 1000 раз с разным количеством итераций, и смотрим, в каком промилле случаев тест ошибся\")\n", 117 | "for iters in [1, 3, 10, 15, 20, 25, 30]:\n", 118 | " print(f\"{iters} итераций, {sum(1 for _ in range(checks) if fermat_prime_or_pseudoprime(6606, iters)) * 1000 // checks}‰ ошибок\")" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [] 127 | } 128 | ], 129 | "metadata": { 130 | "kernelspec": { 131 | "display_name": "Python 3 (ipykernel)", 132 | "language": "python", 133 | "name": "python3" 134 | }, 135 | "language_info": { 136 | "codemirror_mode": { 137 | "name": "ipython", 138 | "version": 3 139 | }, 140 | "file_extension": ".py", 141 | "mimetype": "text/x-python", 142 | "name": "python", 143 | "nbconvert_exporter": "python", 144 | "pygments_lexer": "ipython3", 145 | "version": "3.9.1" 146 | } 147 | }, 148 | "nbformat": 4, 149 | "nbformat_minor": 4 150 | } 151 | -------------------------------------------------------------------------------- /docs/examples/02.Kepler/typed_kepler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Численное решение задачи скольких-то тел 5 | """ 6 | 7 | from __future__ import annotations # !! typing 8 | from abc import abstractmethod, ABC 9 | from typing import List 10 | from numpy import array as vec 11 | import numpy.linalg 12 | import matplotlib.pyplot as plt 13 | import matplotlib.axes 14 | 15 | 16 | class Body: 17 | """Тело, движущаееся по двумерной плоскости""" 18 | 19 | def __init__(self, universe: Universe, mass: float, position: vec, velocity: vec): 20 | # Аннотации типов по желанию, но могут помочь IDE и компилятору, когда таковые имеются 21 | self.universe: Universe = universe 22 | self.mass: float = mass 23 | self.position: vec = position 24 | self.velocity: vec = velocity 25 | 26 | def force_induced_by_other(self, other: Body) -> vec: 27 | """Сила, с которой другое тело действует на данное""" 28 | # Body is forward reference here 29 | delta_p = other.position - self.position 30 | distance = numpy.linalg.norm(delta_p) # Евклидова норма (по теореме Пифагора) 31 | force_direction = delta_p / distance 32 | force = force_direction * self.mass * other.mass *\ 33 | self.universe.gravity_flow_dencity_per_1_1(distance) 34 | return force 35 | 36 | def advance(self): 37 | """Перемещаем тело, исходя из его скорости""" 38 | self.position += self.velocity * MODEL_DELTA_T 39 | 40 | def apply_force(self, force: vec): 41 | """Изменяем скорость, исходя из силы, действующей на тело""" 42 | self.velocity += force * MODEL_DELTA_T / self.mass 43 | 44 | 45 | # ABC это не алфавит, а AbstractBaseClass. Не даст создать экземпляр, пока не переопределишь все методы-заглушки 46 | class Universe(ABC): 47 | """Невнятная вселенная, основа всех миров""" 48 | 49 | def __init__(self, 50 | G: float, # гравитационная постоянная 51 | collision_distance: float # всё-таки это не точки 52 | ): 53 | self.G: float = G 54 | self.collision_distance: float = collision_distance 55 | 56 | 57 | @abstractmethod 58 | def gravity_flow_dencity_per_1_1(self, dist: float) -> float: 59 | """ 60 | Плотность потока гравитационного поля между двумя 61 | единичными массами на заданном расстоянии 62 | """ 63 | ... 64 | 65 | @abstractmethod 66 | def model_step(self): 67 | """Итерация решения задачи Коши. Конечно не присуща вселенной, но тут на своём месте""" 68 | ... 69 | 70 | class UniverseWith3Bodies(Universe): 71 | """ 72 | Демо-вселенная. 73 | Кому угодно понятно, что она ненастоящая. 74 | Зато уже есть. 75 | """ 76 | 77 | def __init__(self, 78 | G: float, # гравитационная постоянная 79 | collision_distance: float # всё-таки это не точки 80 | ): 81 | """В начале было... да, а потом тестовая вселенная с пупом мира и двумя камнями""" 82 | super().__init__(G, collision_distance) 83 | 84 | self.centrum = Body(self, 500.0, vec([0.0, 0.0]), vec([0.0, 0.0])) 85 | self.p_1 = Body(self, 10.0, vec([50.0, 0.0]), vec([0.0, 15.0])) 86 | self.p_2 = Body(self, 10.0, vec([50.0, 40.0]), vec([-7.0, 7.0])) 87 | 88 | def gravity_flow_dencity_per_1_1(self, dist: float) -> float: 89 | # будем считать, что отскакивают точки друг от друга резко, 90 | # но стараться не допускать этого 91 | return self.G / ( 92 | dist ** 2 if dist > self.collision_distance 93 | else -self.G / dist ** 3 94 | ) 95 | 96 | def model_step(self): 97 | self.p_1.apply_force(self.p_1.force_induced_by_other(self.centrum)) 98 | self.p_2.apply_force(self.p_2.force_induced_by_other(self.centrum)) 99 | self.p_1.advance() 100 | self.p_2.advance() 101 | 102 | class UniverseWithBodies(Universe): 103 | """ 104 | Будем считать, что это наша вселенная. Кстати, в ней тела действуют и 105 | друг на друга. 106 | """ 107 | 108 | def __init__(self, 109 | G: float, # гравитационная постоянная 110 | collision_distance: float # всё-таки это не точки 111 | ): 112 | super().__init__(G, collision_distance) 113 | self.bodies: List[Body] = [] 114 | 115 | def add_body(self, b: Body): 116 | raise NotImplementedError("Запрограммируй меня!") 117 | 118 | @abstractmethod 119 | def gravity_flow_dencity_per_1_1(self, dist: float) -> float: 120 | pass 121 | 122 | @abstractmethod 123 | def model_step(self): 124 | pass 125 | 126 | 127 | class UniverseWithDimensionsAndBodies(UniverseWithBodies): 128 | """ 129 | А это уже вселенная, у которой пространственных измерений, сколько скажут 130 | """ 131 | 132 | def __init__(self, 133 | dimensions: int, # сколько пространственных измерений 134 | G: float, # гравитационная постоянная 135 | collision_distance: float # всё-таки это не точки 136 | ): 137 | super().__init__(G, collision_distance) 138 | self.dimensions = dimensions 139 | 140 | def gravity_flow_dencity_per_1_1(self, dist: float) -> float: 141 | # Должна использовать self.dimensions 142 | raise NotImplementedError("Запрограммируй меня!") 143 | 144 | 145 | if __name__ == '__main__': 146 | 147 | un = UniverseWith3Bodies(50, 3.0) 148 | 149 | MODEL_DELTA_T = 0.01 150 | TIME_TO_MODEL = 10 151 | 152 | xs_1 = [] 153 | ys_1 = [] 154 | xs_2 = [] 155 | ys_2 = [] 156 | for stepn in range(int(TIME_TO_MODEL / MODEL_DELTA_T)): 157 | xs_1.append(un.p_1.position[0]) 158 | ys_1.append(un.p_1.position[1]) 159 | xs_2.append(un.p_2.position[0]) 160 | ys_2.append(un.p_2.position[1]) 161 | un.model_step() 162 | 163 | c = plt.Circle((0, 0), 2, color='b') 164 | ax: matplotlib.axes.Axes = plt.gca() # !! typing 165 | ax.set_aspect('equal') 166 | ax.add_patch(c) 167 | 168 | plt.plot(xs_1, ys_1) 169 | plt.plot(xs_2, ys_2) 170 | 171 | plt.show() 172 | -------------------------------------------------------------------------------- /docs/slides/03.parallel.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Нам потребуются 5 | 6 | ``` 7 | pip install pyqt6 aiohttp aiofiles asyncqt pyqt6_tools mpi4py 8 | ``` 9 | 10 | = = = = = = 11 | 12 | # Потоки 13 | 14 | - - - - - - 15 | 16 | ## Модуль `threading` 17 | 18 | См. [пример](../examples/03.parallel/threading_no_effect.py) 19 | 20 | * Используем модуль `threading` 21 | * Создаём экземпляр `threading.Thread(target=функция, которую надо выполнить)` 22 | * Запускаем параллельно 23 | 24 | Функции работают попеременно, т.к. потоки Python (реализация CPython) не используют несколько ядер машины, т.е. ускорения нет. 25 | 26 | Тем не менее, функции никто не спрашивает, хотят они, чтобы их прерывали, или нет. Это **вытесняющая** многозадачность. 27 | 28 | = = = = = = 29 | 30 | # Python Multiprocessing 31 | 32 | - - - - - - 33 | 34 | ## Модуль `multiprocessing` 35 | 36 | См. [пример](../examples/03.parallel/multiprocessing_effect.py) 37 | 38 | * Используем модуль `multiprocessing` 39 | * Создаём экземпляр `multiprocessing.Pool()` 40 | * При помощи `pool.map` просим его параллельно выполнить ряд задач 41 | 42 | Python запустит несколько процессов, они будут выполняться операционной системой параллельно (если есть, на чём). 43 | 44 | Проверка `__name__` важна, как никогда: 45 | 46 | ``` 47 | if __name__ == '__main__': 48 | ... 49 | ``` 50 | 51 | И это тоже **вытесняющая** многозадачность. Можно запустить процессов больше, чем ядер, и операционная система будет между ними переключаться. 52 | 53 | = = = = = = 54 | 55 | # MPI 56 | 57 | - - - - - - 58 | 59 | ## Модуль `mpi4py` 60 | 61 | См. [пример](https://github.com/dluciv/python-intro-course/tree/master/docs/examples/03.parallel/mpi4py) 62 | 63 | Это уже совсем «взрослый» способ. Как им пользоваться? 64 | 65 | * У кого Linux, тот всё прекрасно настроит сам. Можно инсталлировать MPICH, OpenMPI или любую другую реализацию 66 | * У кого Windows, скачиваем и инсталлируем [MS MPI](https://docs.microsoft.com/en-us/message-passing-interface/microsoft-mpi#ms-mpi-downloads) 67 | (SDK не надо, достаточно `msmpisetup.exe`). Перезагружаемся или перезаходим в систему, после чего можем использовать `mpiexec` 68 | * Если компьютеров с Windows несколько, на вссех надо запустить `smpd` из MS MPI, а когда брандмауер спросит в первый раз, 69 | разрешать ли этой программе работать с сетью, то разрешить. 70 | 71 | Дальше пробуем один из двух или оба варианта: 72 | 73 | * [Уже упомянутый пример](https://github.com/dluciv/python-intro-course/tree/master/docs/examples/03.parallel/mpi4py) — запускаем, например, `mpiexec -n 4 python mpitest.py` 74 | * Как [написано в докумментации](https://mpi4py.readthedocs.io/en/stable/install.html#testing). 75 | 76 | = = = = = = 77 | 78 | # Сопрограммы 79 | 80 | - - - - - - 81 | 82 | ## Сопрограммы, сопроцедуры 83 | 84 | См. [пример](../examples/03.parallel/coroutines_sync.py) 85 | 86 | * Сопроцедура возвращает не значение, а, «ленивую» коллекцию, по которой можно итерироваться, при помощи, например, `for` 87 | * А если по ней не итерироваться, а брать по одному элементу? 88 | * А если запустить сразу две, и брать то из одной, то из другой? 89 | 90 |
91 | 92 | * Да пожалуйста! 93 | 94 |
95 | 96 | Получается, мы можем выполнить кусочек кода, в нём сказать, что мы возвращаем управление (возможно вместе с каким-нибудь значением), 97 | а потом, когда надо, выполнить ещё и ещё. 98 | 99 | Это тоже многозадачность, **невытесняющая**. Функции сами решают, когда их можно прервать. 100 | 101 | = = = = = = 102 | 103 | # Циклы обработки сообщений, GUI 104 | 105 | - - - - - - 106 | 107 | ## Циклы обработки сообщений 108 | 109 | * Пусть есть система, в которой происходят *события* 110 | * У каждого вида событий (например, пользователь нажал на кнопку) есть *обработчик* — функция 111 | 112 | Система в цикле ожидает событий, и по событиям запускает обработчики 113 | 114 | - - - - - - 115 | 116 | ## GUI 117 | 118 | В большинстве графических сред GUI работает в одном потоке. Это значит, что, если, например, 119 | по нажатию на кнопку обработчик «задумается», приложение «повиснет», т.е. перестанет обрабатывать 120 | новые события. 121 | 122 | Некоторые системы, например WinMobile или iOS, «убивают» задумавшиеся обработчики. Некоторые (Windows, Android, 123 | Web-браузеры применительно к запущенному JavaScript) иногда выдают предупреждения и предлагают пользователю решить судьбу программы. 124 | 125 | См. [пример](../examples/03.parallel/pyqt6_sync.py) зависающей GUI-программы. Оказывается прочитать файл — это может быть долго 126 | (если вы найдёте дисковод, дискеты, и научитесь ими пользоваться)! 127 | 128 |
129 | 130 | Выход — например, запустить долгую операцию в отдельном потоке. Если это операция ввода-вывода, то даже потоки Python для этого подойдут, 131 | т.к. системные вызовы ввода-вывода Python всё-таки может запускать параллельно. 132 | 133 | = = = = = = 134 | 135 | # AsyncIO — неблокирующий ввод-вывод 136 | 137 | - - - - - - 138 | 139 | ## AsyncIO 140 | 141 | См. [пример](../examples/03.parallel/coroutines_async.py) 142 | 143 | * `c1` и `c2` — как из предыдущего примера с сопроцедурами 144 | * `c3` — с использованием нового синтаксиса Python 3.5+ 145 | 146 | В конце файла мы видим запуск цикла обработки сообщений, в котором происходит одновременная *асинхронная* работа сопроцедур. 147 | Данные сопроцедуры являются *асинхронными функциями*. 148 | 149 | Хорошее подробное описание: https://tproger.ru/translations/asynchronous-programming-in-python/ 150 | 151 | - - - - - - 152 | 153 | ## А причём тут ввод-вывод? 154 | 155 | Ведь был пример со `sleep`... 156 | 157 |
158 | 159 | ... но только потому, что `sleep` хорошо понятен. Можно и «по делу». 160 | 161 | Давайте сделаем наше GUI-приложение более живучим. Оно-то «подвисало» в том числе и на вводе-выводе. 162 | Для этого используем в нём библиотеки `aiohttp` для работы с сетью и `aiofile` для работы с файловой системой. 163 | 164 | - - - - - - 165 | 166 | ## GUI-приложение, которое не завесить 167 | 168 | ... ну или очень тяжело. [Вот оно](../examples/03.parallel/pyqt6_async.py). 169 | 170 | Обратите внимание на код: 171 | 172 | ``` 173 | app = qw.QApplication(sys.argv) 174 | loop = asyncqt.QEventLoop(app) 175 | asyncio.set_event_loop(loop) 176 | ``` 177 | 178 | Поскольку у GUI есть свой цикл обрадотки событий, мы «перевешиваем» AsyncIO на него. Т.е. говорим ему не запускать свой, а 179 | испольовать тот, который уже есть в библиотеке Qt. 180 | -------------------------------------------------------------------------------- /docs/jupiter-notebooks/03.5.Artillery.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Классы\n", 8 | "\n", 9 | "Класс — тип, опрределённый программистом. Может предоставлять наружу поля (переменные специального вида) и методы (функции специального вида). Попробуем определить класс — тело, у которого есть две координаты и скорость по двум направлениям.\n", 10 | "\n", 11 | "Для того, чтобы создать поле, достаточно ему что-то присовить. В питоне это можно сделать в любой момент, в том числе и «снаружи» от объекта. Но лучше инициализировать поля специальным методом — конструктором. В Питоне конструктор имеет имя `__init__`. У методов, за исключением статических, есть параметр `self`, находящийся в списке параметров первым. Если мы вызываем метод объекта, как `obj.meth(1,2,3)`, то, включая `self`, метод `meth` получит параметры `obj, 1, 2, 3`. Посмотрите, как `__init__` присваивает полям объекта `self` значения своих аргументов.\n", 12 | "\n", 13 | "Также поля можно использовать и изменять, как в методе `advance`." 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "import math\n", 23 | "\n", 24 | "MODEL_G = 9.81\n", 25 | "MODEL_DT = 0.001\n", 26 | "\n", 27 | "class Body:\n", 28 | " def __init__(self, x, y, vx, vy):\n", 29 | " \"\"\"\n", 30 | " Создать тело.\n", 31 | " \n", 32 | " Пареметры:\n", 33 | " ----------\n", 34 | " x: float\n", 35 | " горизонтальная координата\n", 36 | " y: float\n", 37 | " вертикальная координата\n", 38 | " vx: float\n", 39 | " горизонтальная скорость\n", 40 | " vy: float\n", 41 | " вертикальная скорость\n", 42 | " \"\"\"\n", 43 | "\n", 44 | " self.x = x\n", 45 | " self.y = y\n", 46 | " self.vx = vx\n", 47 | " self.vy = vy\n", 48 | " \n", 49 | " self.trajectory_x = []\n", 50 | " self.trajectory_y = []\n", 51 | " \n", 52 | "\n", 53 | " def advance(self):\n", 54 | " \"\"\"\n", 55 | " Выполнить шаг мат. модели применительно к телу, предварительно записав его координаты\n", 56 | " \"\"\"\n", 57 | " self.trajectory_x.append(self.x)\n", 58 | " self.trajectory_y.append(self.y)\n", 59 | " \n", 60 | " self.x += self.vx * MODEL_DT\n", 61 | " self.y += self.vy * MODEL_DT\n", 62 | " self.vy -= MODEL_G * MODEL_DT" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "# Наследование\n", 70 | "\n", 71 | "![](img/oop.jpg)\n", 72 | "\n", 73 | "Основные принципы \\[основанного на классах\\] объектно-ориентированного программирования:\n", 74 | "\n", 75 | "* **Инкапсуляция** — сокрытие деталей реализации за простым интерфейсом. Мы уже сделали это в конструкторе и методе `advance`.\n", 76 | "* **Наследование** — создание иерархии от общего к частному. Например, физическое тело — это очень много что, но мы мало знаем о свойствах тела в принципе. Зато если у нас есть, скажем, ёжик, ракета и котлета, то их гораздо меньше, чем просто тел, но зато, опираясь на знания об их сущности, с ними миожно уже делать более интересные вещи.\n", 77 | "* **Полиморфизм** — можно сказать, что это инкапсуляция применительно к наследованию. Например, у ёжика и у ракеты могут быть методы наподобие `отправиться_в_полёт`. При этом ёжик и ракета делают это по-разному, но если мы хотим отправлть в полёт физическое тело вообще, то хорошо бы, чтобы можно было так и написать `объект.отправиться_в_полёт()`. При этом *реализации* этого метода будут у всех разные, и они сами будут разбираться в том, как им следует летать.\n", 78 | "\n", 79 | "В общем, давайте сделаем ракету, например, «Катюшу», которая будет на первых этапах полёта разгоняться и терять в массе. А метод `advance` будет это учитывать." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "class Rocket(Body):\n", 89 | " def __init__(self, x, y):\n", 90 | " \"\"\"\n", 91 | " Создать ракету.\n", 92 | " \n", 93 | " Пареметры:\n", 94 | " ----------\n", 95 | " x: float\n", 96 | " горизонтальная координата\n", 97 | " y: float\n", 98 | " вертикальная координата\n", 99 | " \"\"\"\n", 100 | " super().__init__(x, y, 10, 10) # Вызовем конструктор предка — тела, т.к. он для ракеты актуален\n", 101 | " ... # Дописать\n", 102 | "\n", 103 | " def advance(self):\n", 104 | " super().advance() # вызовем метод предка — тела, т.к. и он для ракеты актуален.\n", 105 | " ... # Дописать" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "# Домашнее задание\n", 113 | "\n", 114 | "Наша ракета здесь — не очень-то и ракета. Требуется дописать её `__init__` и `__advance__` так, чтобы:\n", 115 | "\n", 116 | "* Она летела, ускоряясь и теряя в массе, пока не прогорит порох, а потом летела, как тело\n", 117 | "* М.б. ещё что-нибудь нафантазировать про неё =)" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "import numpy as np\n", 127 | "\n", 128 | "b = Body(0, 0, 9, 9)\n", 129 | "r = Rocket(0, 0)\n", 130 | "\n", 131 | "bodies = [b, r]\n", 132 | "# Дальше мы уже не будем думать, кто тут ёжик, кто ракета, а кто котлета —\n", 133 | "# благодаря возможностям ООП будем просто работать со списком тел\n", 134 | "\n", 135 | "for t in np.arange(0, 2, MODEL_DT): # для всех временных отрезков\n", 136 | " for b in bodies: # для всех тел\n", 137 | " b.advance() # выполним шаг" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "%matplotlib inline\n", 147 | "from matplotlib import pyplot as pp\n", 148 | "\n", 149 | "for b in bodies: # для всех тел\n", 150 | " pp.plot(b.trajectory_x, b.trajectory_y) # нарисуем их траектории" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [] 159 | } 160 | ], 161 | "metadata": { 162 | "interpreter": { 163 | "hash": "a60c4638ffa677002f422f6be40ff22d44f12e64592a9c06483ed9384b74d57d" 164 | }, 165 | "kernelspec": { 166 | "display_name": "Python 3.9.1 64-bit", 167 | "name": "python3" 168 | }, 169 | "language_info": { 170 | "codemirror_mode": { 171 | "name": "ipython", 172 | "version": 3 173 | }, 174 | "file_extension": ".py", 175 | "mimetype": "text/x-python", 176 | "name": "python", 177 | "nbconvert_exporter": "python", 178 | "pygments_lexer": "ipython3", 179 | "version": "3.9.1" 180 | } 181 | }, 182 | "nbformat": 4, 183 | "nbformat_minor": 4 184 | } 185 | -------------------------------------------------------------------------------- /docs/slides/slides.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 | 26 | 27 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 |
99 | 100 | 101 |
102 | 103 | 104 | 105 | 106 |
107 |

Основы программирования

108 |

Python, направление 03.03.01 «Прикладные математика и физика»,
109 | программы «теоретическая физика» и «математика и информационные технологии»

110 |

***

111 |

112 |
Луцив Дмитрий Вадимович
113 |
Кафедра биоинформатики и математической биологии СПбАУ
114 | 115 |
116 | 117 | 118 | 119 | 120 | 121 |
127 |
128 | 129 | 134 | 135 | 136 | 137 | 138 | 139 |
140 |

СПАСИБО!

141 |

142 |
143 | 144 | 145 | 146 |
147 | 148 |
149 | 150 | 151 | 152 | 153 | 184 | 185 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /tools/plagiarism-reporter/sourcedrop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import dataclasses 4 | from enum import Enum 5 | import itertools 6 | import json 7 | import sys 8 | import difflib 9 | from io import BytesIO 10 | from typing import Optional, List, Tuple, Iterable 11 | import tokenize 12 | import keyword 13 | 14 | import chardet 15 | 16 | 17 | class LexMode(Enum): 18 | EXACT_MODE = 'exact' 19 | TOKENIZE_MODE = 'tokenize' 20 | IDENTIFIER_STRIP_MODE = 'id-strip' 21 | 22 | def __str__(self): 23 | return self.value 24 | 25 | 26 | @dataclasses.dataclass() 27 | class PythonSource: 28 | """ 29 | To keep a part of Python code 30 | 31 | Attributes: 32 | file_name Source file name 33 | file_index Index within filename 34 | raw_lexemes Lexemes 35 | fingerprint_lexemes Lexemes with depersonated strings and IDs 36 | """ 37 | file_name: str 38 | file_index: Optional[int] 39 | total_lexemes: int 40 | fingerprint_lexemes: List[str] 41 | 42 | @property 43 | def id_repr(self)-> str: 44 | return\ 45 | "%s[%02d]" % ( 46 | self.file_name, 47 | self.file_index) if self.file_index is not None else self.file_name 48 | 49 | def borrowed_fraction_from( 50 | self, 51 | other: 'PythonSource', 52 | minimal_match_length: int, 53 | consider_reordered: bool = True 54 | )-> Optional[float]: 55 | """Tells, what fraction of current source was (if it was) 56 | likely borrowed from another one""" 57 | 58 | # Trivial cases 59 | 60 | if self is other or self.id_repr == other.id_repr: 61 | return None 62 | elif self.fingerprint_lexemes == other.fingerprint_lexemes: 63 | return 1.0 64 | 65 | # Invoke LCS until nothing is borrowed 66 | 67 | # Markers from unicode provate use area, 68 | # will never occur in source code 69 | self_marker = '\uE001' 70 | other_marker = '\uE002' 71 | 72 | self_lexemes = self.fingerprint_lexemes.copy() 73 | other_lexemes = other.fingerprint_lexemes.copy() 74 | 75 | common_size = 0 76 | 77 | resultative = True 78 | while resultative: 79 | sm = difflib.SequenceMatcher( 80 | None, 81 | self_lexemes, 82 | other_lexemes, 83 | False 84 | ) # type: ignore 85 | 86 | resultative = False 87 | for b in sm.get_matching_blocks(): 88 | self_index, other_index, match_size = tuple(b) 89 | if match_size >= minimal_match_length: 90 | 91 | # Found something, will try next time 92 | # if we consider reordered plagiarism 93 | resultative = consider_reordered 94 | 95 | # Take the match into account 96 | common_size += match_size 97 | 98 | # Make the match different 99 | self_lexemes[self_index: self_index + match_size] = [self_marker] * match_size 100 | other_lexemes[other_index: other_index + match_size] = [other_marker] * match_size 101 | 102 | """ 103 | sm = difflib.SequenceMatcher( 104 | None, 105 | self.fingerprint_lexemes, 106 | other.fingerprint_lexemes, 107 | False 108 | ) # type: ignore 109 | 110 | common_size = sum(b.size for b in sm.get_matching_blocks() 111 | if b.size >= minimal_match_length) 112 | """ 113 | 114 | return float(common_size / len(self.fingerprint_lexemes)) 115 | 116 | @staticmethod 117 | def _lex_python_source( 118 | source_code: str, lex_mode: LexMode) -> Tuple[List[str], int]: 119 | """Get sequence of lexemes (depersonated or raw) from python source""" 120 | 121 | fingerprint_lexemes = [] 122 | 123 | tokens = list(tokenize.tokenize( 124 | BytesIO(source_code.encode('utf-8')).readline)) 125 | 126 | for ttype, tvalue, tstart, tend, tline in tokens: 127 | if ttype in ( 128 | tokenize.INDENT, tokenize.DEDENT, tokenize.NEWLINE, 129 | tokenize.NL, tokenize.ENDMARKER, tokenize.ERRORTOKEN 130 | ): 131 | continue 132 | 133 | ts = tvalue.strip() 134 | 135 | if lex_mode == LexMode.TOKENIZE_MODE: 136 | fingerprint_lexemes.append(ts) 137 | elif lex_mode == LexMode.IDENTIFIER_STRIP_MODE: 138 | if ttype == tokenize.NAME: 139 | if keyword.iskeyword(ts): 140 | fingerprint_lexemes.append(ts) 141 | else: 142 | fingerprint_lexemes.append('&id') 143 | elif ttype == tokenize.STRING: 144 | fingerprint_lexemes.append('&""') 145 | elif ttype == tokenize.NUMBER: 146 | fingerprint_lexemes.append('&num') 147 | elif ttype == tokenize.COMMENT: 148 | fingerprint_lexemes.append('&#') 149 | else: 150 | fingerprint_lexemes.append(ts) 151 | else: 152 | raise ValueError( 153 | "Lex mode %s not implemented for Python sources" % 154 | (lex_mode.value)) 155 | 156 | return fingerprint_lexemes, len(tokens) 157 | 158 | @staticmethod 159 | def read_pythons_from_file( 160 | filename: str, lex_mode: LexMode)-> Iterable['PythonSource']: 161 | def read_nasty_file()-> str: 162 | try: 163 | with open(filename, 'r', encoding='utf-8') as tf: 164 | return tf.read() 165 | except UnicodeDecodeError as ue: 166 | print( 167 | "Author did not master UTF-8: %s" % 168 | (filename), file=sys.stderr) 169 | with open(filename, 'rb') as bf: 170 | bts = bf.read() 171 | ec = chardet.detect(bts) 172 | print(" - and with confidence of %f used %s" % 173 | (ec['confidence'], ec['encoding']), file=sys.stderr) 174 | return bts.decode(ec['encoding']) 175 | 176 | def read_pythons_from_notebook() -> Iterable['PythonSource']: 177 | """Too lazy to look for Jupyter API""" 178 | try: 179 | with open(filename, 'r', encoding='utf-8') as ipynb: 180 | nbc = json.load(ipynb) 181 | cells: Iterable = nbc['cells'] 182 | for c, n in zip(cells, itertools.count()): 183 | if c['cell_type'] == 'code': 184 | src = '\n'.join( 185 | l if not l.startswith('%') else '#<%> ' + l 186 | for l in c['source'] 187 | ) 188 | fl, tt = PythonSource._lex_python_source( 189 | src, lex_mode) 190 | yield PythonSource(filename, n, tt, fl) 191 | except Exception as e: 192 | print( 193 | "Error reading %s" % 194 | (filename), 195 | repr(e), 196 | file=sys.stderr) 197 | 198 | if filename.endswith('.ipynb'): 199 | yield from read_pythons_from_notebook() 200 | 201 | else: 202 | try: 203 | src = read_nasty_file() 204 | fl, tt = PythonSource._lex_python_source(src, lex_mode) 205 | yield PythonSource(filename, None, tt, fl) 206 | except Exception as e: 207 | print( 208 | "Error reading %s" % 209 | (filename), 210 | repr(e), 211 | file=sys.stderr) 212 | 213 | 214 | if __name__ == '__main__': 215 | print("This is not a script, just a module", file=sys.stderr) 216 | exit(-1) 217 | -------------------------------------------------------------------------------- /docs/slides/common/images/vignette-e.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/jupiter-notebooks/01.operation_basics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Лекция 2\n", 8 | "\n", 9 | "## Что мы уже сделали?\n", 10 | "\n", 11 | "Довольно много:\n", 12 | "\n", 13 | "1. Установить Python, запустить его.\n", 14 | "2. Установить пакеты `ipython`, `scipy`, `numpy`, `jupyter`, `matplotlib`, `pandas`.\n", 15 | "3. Запустить IPython, почувствовать разницу.\n", 16 | "4. Судя по тому, что мы видим, запустить Jupyter\n", 17 | "\n", 18 | "## Что нам предстоит?\n", 19 | "\n", 20 | "1. Впечатлиться тому, какой Jupyter крутой (правда же?)\n", 21 | "2. Убеждённым *радикальным* математикам и физикам — порадоваться тому, насколько он похож на математические пакеты наподобие Mathematica™ или Maple™.\n", 22 | "3. Осознать, что всё-таки Standalone-программа — может быть не так радостно, но более понятно и более зрело.\n", 23 | "\n", 24 | "![Гагарин](https://upload.wikimedia.org/wikipedia/commons/5/54/Yuri_Gagarin_in_Sweden%2C_1964_%28cropped%29_%282%29.jpg)\n", 25 | "\n", 26 | "# Поехали!" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "## Ой, как красиво!\n", 34 | "\n", 35 | "А как это?..\n", 36 | "\n", 37 | "* Markdown — https://www.markdownguide.org/\n", 38 | "* Формулы наподобие $ f(x) = f(a)+\\sum_{k=1}^\\infty {f^{(k)} (a) \\over k!} (x - a)^k $ — https://en.wikibooks.org/wiki/LaTeX/Mathematics\n", 39 | "* Literate Programming — http://www.literateprogramming.com/" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "## Основные операторы\n", 47 | "\n", 48 | "... и чуть-чуть самого Питона. Да, наконец не абстрактные рассказы." 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "print(\"Saluton, la universo!\")\n", 58 | "name = input(\"Как звать тебя? \")\n", 59 | "print(\"И ты, \" + name + \", здравствуй!\")" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "Здесь мы:\n", 67 | "1. Трижды вызвали две **функции**. *Какие?*\n", 68 | "2. Произвели **ввод** и **вывод**, воспользовавшись предопределёнными **потоками консоли** `STDIN` и `STDOUT`. *Где?*\n", 69 | "3. Создали **переменную**, поместили в неё **строку**, **конкатенировали** несколько строк.\n", 70 | "\n", 71 | "Вообще настоящая текстовая консоль — примерно такая штука:\n", 72 | "\n", 73 | "![Консоль DEC VT05](https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/VT05.jpg/640px-VT05.jpg)\n", 74 | "\n", 75 | "Но если меньше повезёт, то с принтером и бумагой, а не с дисплеем.\n", 76 | "\n", 77 | "У консоли ещё есть поток `STDERR`, предназначенный для сообщений об ошибках:" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "import sys # \"Подключение\" библиотечного модуля\n", 87 | "print('Это пойдёт в поток \"STDERR\", возможно консоль выделит его цветом.', file=sys.stderr) # Заметили? Кавычки могут быть разными." 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "# Чуть-чуть встроенных типов и операций над ними\n", 95 | "\n", 96 | "Самое основное — булев тип" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "b = True\n", 106 | "print(\"1.\", b, not b, b or not b, b and not b)\n", 107 | "print(\"2.\", 1 < 2, 1 <= 2 < 4, 1 > 2)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "Теперь числа, иногда с неожиданностями" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "i = 2\n", 124 | "print(\"1.\", i, 2**i, i**128)\n", 125 | "print(\"2.\", 1//i, 1/i)\n", 126 | "print(\"3.\", 0.1, 0.1 + 0.1, 0.1 + 0.1 + 0.1)\n", 127 | "print(\"4.\", max(1,2))\n", 128 | "\n", 129 | "print(max(1+0j,2+0j)) # выдаст ошибку — язык сильно типизированный" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "Теперь кортежи, списки, диапазоны и вырезки" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "x, y = 1, 2\n", 146 | "\n", 147 | "y, x = x, y\n", 148 | "\n", 149 | "t = x, y\n", 150 | "\n", 151 | "print(\"1.\", t, t[0], t[1])\n", 152 | "print(\"2.\", t[-1])\n", 153 | "\n", 154 | "l = [30, 10, 20, 35, 5, 12]\n", 155 | "print(\"3.\", l[1:3], l[1:5:2], sorted(l))\n", 156 | "\n", 157 | "r1, r2, r3 = range(3), range(1,3), range(1,5,2)\n", 158 | "print(\"4.\", r1, r2, r3)\n", 159 | "print(\"5.\", list(r1), list(r2), list(r3))\n", 160 | "\n", 161 | "for i in range(3):\n", 162 | " print(\"6.\", i)\n", 163 | "\n", 164 | "print([2**n for n in range(17) if n != 13]) # для суеверных" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "set, dict" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [ 180 | "s = {1, 3, 5, \"q\"} # можно, но не стоит хранить объекты разной природы\n", 181 | "d = {1: \"x\", 4: \"y\", \"5\": 10} # аналогично\n", 182 | "what_is_it = {}\n", 183 | "\n", 184 | "print(\"1.\", s, d, type(what_is_it))\n", 185 | "\n", 186 | "print(\"2.\", 3 in s, \"x\" in d.keys())\n", 187 | "\n", 188 | "print(\"3.\", s.difference([3, \"q\"]))\n", 189 | "\n", 190 | "for k in d:\n", 191 | " print(\"4.\", k, d[k])\n", 192 | "\n", 193 | "print(\"5.\", [e * 2 for e in s])" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "str, bytes" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "юникодная_строка = \"Ѣ,€,௵,𦈘\"\n", 210 | "print(\"1.\", юникодная_строка, len(юникодная_строка))\n", 211 | "\n", 212 | "байтики = юникодная_строка.encode('utf-8')\n", 213 | "print(\"2.\", байтики, len(байтики))\n", 214 | "\n", 215 | "print(\"3.\", байтики.decode('utf-8'))\n", 216 | "\n", 217 | "import sys\n", 218 | "\n", 219 | "print(\"5.\", \"R\" * 3)\n", 220 | "print(\"6.\", sys.getsizeof(\"f\"*1000), sys.getsizeof(\"б\"*1000), sys.getsizeof(\"𦈘\"*1000))" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "функции" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "def fact1(n):\n", 237 | " r = 1\n", 238 | " for i in range(1, n + 1):\n", 239 | " r *= i\n", 240 | " return r\n", 241 | "\n", 242 | "\n", 243 | "def fact2(n):\n", 244 | " if n <= 1:\n", 245 | " return 1\n", 246 | " else:\n", 247 | " return n * fact2(n - 1)\n", 248 | " \n", 249 | "\n", 250 | "def fact3(n):\n", 251 | " return 1 if n <= 1 else n * fact3(n - 1)\n", 252 | "\n", 253 | "\n", 254 | "fact4 = lambda n: 1 if n <= 1 else n * fact4(n - 1)\n", 255 | "\n", 256 | "\n", 257 | "print(fact1(6), fact2(6), fact3(6), fact4(6))" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "Как ни странно, мы на самом деле уже можем писать программы. Когда знаем, что хотим в них написать =).\n", 265 | "\n", 266 | "P.S. Ещё немного знаний — про встроенные функции: https://docs.python.org/3.7/library/functions.html" 267 | ] 268 | } 269 | ], 270 | "metadata": { 271 | "interpreter": { 272 | "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" 273 | }, 274 | "kernelspec": { 275 | "display_name": "Python 3.10.0 64-bit", 276 | "name": "python3" 277 | }, 278 | "language_info": { 279 | "codemirror_mode": { 280 | "name": "ipython", 281 | "version": 3 282 | }, 283 | "file_extension": ".py", 284 | "mimetype": "text/x-python", 285 | "name": "python", 286 | "nbconvert_exporter": "python", 287 | "pygments_lexer": "ipython3", 288 | "version": "3.10.0" 289 | } 290 | }, 291 | "nbformat": 4, 292 | "nbformat_minor": 2 293 | } 294 | -------------------------------------------------------------------------------- /tools/plagiarism-reporter/inclusion_report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | import glob 5 | import locale 6 | import os 7 | import sys 8 | import argparse 9 | import multiprocessing 10 | import multiprocessing.dummy 11 | import subprocess 12 | from typing import List, Iterable, Tuple, Optional 13 | 14 | import tzlocal 15 | import tqdm 16 | 17 | from sourcedrop import LexMode, PythonSource 18 | import report_export 19 | 20 | 21 | def get_file_mdate(file_name: str)-> datetime.datetime: 22 | try: 23 | abs_file_name = os.path.abspath(file_name) 24 | output: subprocess.Popen = subprocess.Popen( 25 | ['git', 'log', '--date=iso', '--format="%ad"', '--', abs_file_name], 26 | stdout=subprocess.PIPE, 27 | cwd=os.path.split(abs_file_name)[0] 28 | ) 29 | output.wait(5.0) 30 | 31 | if output.returncode != 0: 32 | raise UserWarning("Git return code was %d" % (output.returncode)) 33 | 34 | bstdout: bytes = output.communicate()[0] 35 | stdout: Iterable[str] = bstdout.decode( 36 | locale.getpreferredencoding(False)).split('\n') 37 | stdout = [l.replace('"', '').replace("'", '').strip() for l in stdout] 38 | stdout = [l for l in stdout if len(l)] 39 | 40 | last_date: str = stdout[0] 41 | if last_date.endswith('00'): 42 | last_date = last_date[:-2] + ':00' 43 | 44 | return datetime.datetime.fromisoformat(last_date) 45 | except Exception as e: 46 | print("Error <<%s>> when getting git times for %s" % 47 | (str(e), file_name), file=sys.stderr) 48 | return datetime.datetime.utcfromtimestamp(os.path.getmtime(file_name)) 49 | 50 | 51 | def globs(path: str, min_date: Optional[datetime.datetime])-> List[str]: 52 | filenames = [] 53 | filenames.extend( 54 | glob.glob( 55 | os.path.join( 56 | path, 57 | '**', 58 | '*.py'), 59 | recursive=True)) 60 | filenames.extend( 61 | glob.glob( 62 | os.path.join( 63 | path, 64 | '**', 65 | '*.ipynb'), 66 | recursive=True)) 67 | filenames.sort() 68 | 69 | if min_date: 70 | filenames = [fn for fn in filenames if get_file_mdate(fn) >= min_date] 71 | 72 | return filenames 73 | 74 | 75 | def get_python_sources( 76 | filenames: Iterable[str], min_lexemes: int, check_method: LexMode 77 | )-> Iterable[PythonSource]: 78 | for fn in filenames: 79 | for ps in PythonSource.read_pythons_from_file(fn, check_method): 80 | if ps.total_lexemes >= min_lexemes: 81 | yield ps 82 | 83 | 84 | def get_args()-> argparse.Namespace: 85 | 86 | # Thanks to https://gist.github.com/monkut/e60eea811ef085a6540f 87 | def valid_date_type(arg_date_str): 88 | """custom argparse *date* type for user dates values given from the command line""" 89 | try: 90 | given_time = datetime.datetime.strptime(arg_date_str, "%Y-%m-%d") 91 | tz_time = tzlocal.get_localzone().localize(given_time) 92 | return tz_time 93 | except ValueError: 94 | msg = "Given Date ({0}) not valid! Expected format, YYYY-MM-DD!".format(arg_date_str) 95 | raise argparse.ArgumentTypeError(msg) 96 | 97 | apr = argparse.ArgumentParser() 98 | apr.add_argument( 99 | "-gg", 100 | "--good-guys", 101 | help="Sources of good guys, who code", 102 | required=True 103 | ) 104 | apr.add_argument( 105 | "-bg", 106 | "--bad-guys", 107 | help="Sources of presumably bad guys, who can steal", 108 | required=True 109 | ) 110 | apr.add_argument( 111 | "-bt", 112 | "--borrow-threshold", 113 | help="Max amount of borrowed code to remain good", 114 | type=float, 115 | default=0.25 116 | ) 117 | apr.add_argument( 118 | "-cm", 119 | "--check-method", 120 | type=LexMode, 121 | help="Check all lexemes or only structure (keywords, etc.) ones", 122 | default=LexMode.TOKENIZE_MODE.value, 123 | choices=list(LexMode) 124 | ) 125 | apr.add_argument( 126 | "-ml", 127 | "--min-length", 128 | help="Minimal number of tokens in source to take it in account", 129 | type=int, 130 | default=20 131 | ) 132 | apr.add_argument( 133 | "-mml", 134 | "--min-match-length", 135 | help="Minimal length of text fragment to take in account", 136 | type=int, 137 | default=5 138 | ) 139 | apr.add_argument( 140 | "-mbfd", 141 | "--min-bad-file-date", 142 | help="Oldest source file of bad guys to consider, older ones will be ignored; format: YYYY-MM-DD", 143 | type=valid_date_type 144 | ) 145 | apr.add_argument( 146 | "-rf", 147 | "--report-file", 148 | help="OpenDocument spreadsheet to save the report", 149 | type=str, 150 | required=False 151 | ) 152 | apr.add_argument( 153 | "-nm", 154 | "--no-multiprocessing", 155 | help="No multiprocessing to debug it easily", 156 | action='store_true', 157 | required=False 158 | ) 159 | return apr.parse_args() 160 | 161 | 162 | def _is_same_guy(bad_filename: str, good_filename: str, 163 | bad_root: str, good_root: str)-> bool: 164 | """If files belong to the same guy to skip the check""" 165 | 166 | bad_root = os.path.normpath(bad_root).\ 167 | lstrip(os.path.sep).rstrip(os.path.sep) 168 | good_root = os.path.normpath(good_root).\ 169 | lstrip(os.path.sep).rstrip(os.path.sep) 170 | 171 | bad_filename = os.path.normpath(bad_filename).\ 172 | lstrip(os.path.sep).rstrip(os.path.sep) 173 | good_filename = os.path.normpath(good_filename).\ 174 | lstrip(os.path.sep).rstrip(os.path.sep) 175 | 176 | bad_filename = bad_filename.replace(bad_root + os.path.sep, '') 177 | good_filename = good_filename.replace(good_root + os.path.sep, '') 178 | 179 | return bad_filename.split(os.path.sep)[0] == \ 180 | good_filename.split(os.path.sep)[0] 181 | 182 | 183 | _minimal_match_length: int = 0 184 | 185 | 186 | def compare_srcs( 187 | settings_bad_good: Tuple[PythonSource, PythonSource] 188 | )-> Tuple[str, str, Optional[float]]: 189 | global _minimal_match_length 190 | bad, good = settings_bad_good 191 | borrowed_fraction = bad.borrowed_fraction_from( 192 | good, _minimal_match_length) 193 | return bad.id_repr, good.id_repr, borrowed_fraction 194 | 195 | 196 | def compare_srcs_initializer( 197 | minimal_match_length: int): 198 | global _minimal_match_length 199 | _minimal_match_length = minimal_match_length 200 | 201 | 202 | def workflow(): 203 | args = get_args() 204 | 205 | borrow_threshold: float = args.borrow_threshold # type: ignore 206 | check_method: LexMode = args.check_method # type: ignore 207 | minimal_match_length: int = args.min_match_length # type: ignore 208 | 209 | gs = globs(args.good_guys, None) # type: ignore 210 | bs = globs(args.bad_guys, args.min_bad_file_date) # type: ignore 211 | ml = args.min_length 212 | 213 | print("Looking for them...") 214 | good_sources = list(get_python_sources(gs, ml, check_method)) 215 | bad_sources = list(get_python_sources(bs, ml, check_method)) 216 | 217 | tasks: List[Tuple[PythonSource, PythonSource]] = [] 218 | 219 | total_comparisons: int = 0 220 | done_comparisons: int = 0 221 | 222 | print("Capturing them...") 223 | for b in bad_sources: 224 | for g in good_sources: 225 | if not _is_same_guy(b.file_name, g.file_name, 226 | args.bad_guys, args.good_guys): 227 | tasks.append((b, g)) 228 | total_comparisons += 1 229 | 230 | print("Inquiring them...") 231 | 232 | if args.no_multiprocessing: # type: ignore 233 | pool = multiprocessing.dummy.Pool( 234 | 1, 235 | initializer=compare_srcs_initializer, 236 | initargs=[minimal_match_length] 237 | ) 238 | else: 239 | pool = multiprocessing.Pool( 240 | initializer=compare_srcs_initializer, 241 | initargs=[minimal_match_length] 242 | ) 243 | 244 | results = pool.imap_unordered(compare_srcs, tasks) 245 | 246 | borrowing_facts: List[Tuple[str, str, float]] = [] 247 | 248 | tty = sys.stdout.isatty() 249 | if tty: 250 | results = tqdm.tqdm(results, total=total_comparisons) 251 | 252 | for bfn, gfn, bo in results: 253 | done_comparisons += 1 254 | if bo is not None and bo >= borrow_threshold: 255 | borrowing_facts.append((bfn, gfn, bo)) 256 | (results.write if tty else print)( 257 | "%02d%% of %s borrowed from %s" % ( 258 | int(100.0 * bo), 259 | bfn, 260 | gfn 261 | ) 262 | ) 263 | if args.report_file: 264 | report_export.export_odf_report(args.report_file, borrowing_facts) 265 | 266 | 267 | if __name__ == '__main__': 268 | workflow() 269 | -------------------------------------------------------------------------------- /docs/slides/01.Python_and_slightly_GitHub/content.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Python: что это, откуда и зачем 4 | # И немножко GitHub 5 | 6 | ### Луцив Дмитрий Вадимович1,2 7 | ### Кознов Дмитрий Владимирович2 8 | 9 | 1СПбАУ    2СПбГУ 10 | 11 | 12 | Введение в программирование на Python 13 | 14 | 15 | 16 | 17 | = = = = = = = = = = = = = 18 | 19 | # Почему Python? 20 | 21 | 22 | - - - - - - - - - - - - - 23 | ## Популярность Python I 24 | 25 | Насколько Python популярен по версии Google 26 | 27 | По запросу [«Python»](https://trends.google.ru/trends/explore?date=all&q=Python) 28 | 29 | * Тенденция к росту интереса с 2009 г. 30 | * В основном, запросы по фреймворкам (не научным) 31 | 32 | По запросу [«Numpy»](https://trends.google.ru/trends/explore?date=all&q=Numpy) 33 | 34 | * Монотонный рост 35 | * В основном — более «научные» запросы 36 | 37 | В сравнении с [Java и С++](https://trends.google.ru/trends/explore?date=all&q=Python,Java,C%2B%2B) 38 | 39 | * С++ держит примерно постоянный уровень (это не стагнация на самом деле характер интереса меняется) 40 | * Python догоняет Java, Java падает навстречу Python 41 | 42 | 43 | - - - - - - - - - - - - - 44 | ## Популярность Python II 45 | 46 | Самые популярные языки программирования по версии GitHub и Tiobe 47 | 48 | ![](images/octoverse-2020.png) 49 | 50 | 51 | * По версии GitHub https://octoverse.github.com/ 52 | * Значительный прогресс с 2016 г. [2016](https://octoverse.github.com/2016/) → [2017](https://octoverse.github.com/2017/) → 53 | [2018](https://octoverse.github.com/2018/projects#languages) → [2019](https://octoverse.github.com/2019/) 54 | * Невыраженная положительная динамика [по индексу TIOBE](https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81_TIOBE) с 2002 г. https://www.tiobe.com/tiobe-index/ 55 | 56 | А что это за GitHub?.. А об этом позже. 57 | 58 | = = = = = = = = = = = = = 59 | # Происхождение и история Python 60 | 61 | 62 | - - - - - - - - - - - - - 63 | ## Где и для чего появился Python? 64 | 65 | Для чего изначально создавался Python? 66 | 67 | * Администрирование системы Amoeba, разрабатываемой под руководством Э. Танненбаума в Free University Amsterdam 68 | https://docs.python.org/3/faq/general.html#why-was-python-created-in-the-first-place 69 | 70 | На какие существующие языки он был похож? 71 | 72 | * Python создавался под влиянием языков Modula 3, C и ABC 73 | https://www.levenez.com/lang/lang.pdf 74 | 75 | - - - - - - - - - - - - - 76 | ## Развитие Python 77 | 78 | * G. van Rossum начал работу в 1989 г. 79 | * В 1991 г. анонсирована версия 0.9.0, в 1994 – 1.0 80 | * В 2002 г. запущен репозиторий библиотек для Python – Python Package Index 81 | * Поступательное развитие до 2009 года, с 2009 – разделение на ветки 2.Х (сопровождение) и 3.X 82 | 83 | = = = = = = = = = = = = = 84 | # Актуальные свойства Python 85 | 86 | 87 | - - - - - - - - - - - - - 88 | ## Свойства языка (строго) 89 | 90 | * Интерпретируемый (с оговорками) 91 | * Возможность интеграции с библиотеками на C/C++ для повышения производительности 92 | * Сильно типизированный 93 | * Динамически типизированный 94 | * Мультипарадигменный 95 | * Императивный 96 | * Процедурный, объектно-ориентирвоанный 97 | * Чуть-чуть функциональный 98 | * Не структурный 99 | * и, конечно же, не expresion oriented — см. принцип [command-query separation](https://en.wikipedia.org/wiki/Command\%E2\%80\%93query_separation) 100 | 101 | 102 | - - - - - - - - - - - - - 103 | ## Свойства языка (нестрого) 104 | 105 | * Небыстрый (с оговорками) 106 | * Универсальный (не DSL) 107 | * Удобен для обработки строк и списков 108 | * Простой в освоении 109 | * Очень легко читается — почти псевдокод 110 | * Достаточно лаконичный 111 | * Общность приносится в жертву удобству в типичных частных случаях 112 | * Очень богатая экосистема модулей 113 | * вычислительные библиотеки — научные и инженерные 114 | * системные — реализация и сопряжение с серверами, СУБД и т.д. 115 | 116 | - - - - - - - - - - - - - 117 | ## Свойства языка (совсем нестрого) 118 | 119 | 120 | Python — язык, позволяющий эффективно и комфортно решать задачу, минимально отвлекаясь на программирование как таковое 121 | 122 | - - - - - - - - - - - - - 123 | ## Zen of Python 124 | 125 | >>> import this 126 | 127 | The Zen of Python, by Tim Peters 128 | 129 | Beautiful is better than ugly. 130 | Explicit is better than implicit. 131 | Simple is better than complex. 132 | Complex is better than complicated. 133 | Flat is better than nested. 134 | Sparse is better than dense. 135 | Readability counts. 136 | Special cases aren't special enough to break the rules. 137 | Although practicality beats purity. 138 | Errors should never pass silently. 139 | Unless explicitly silenced. 140 | In the face of ambiguity, refuse the temptation to guess. 141 | There should be one-- and preferably only one --obvious way to do it. 142 | Although that way may not be obvious at first unless you're Dutch. 143 | Now is better than never. 144 | Although never is often better than *right* now. 145 | If the implementation is hard to explain, it's a bad idea. 146 | If the implementation is easy to explain, it may be a good idea. 147 | Namespaces are one honking great idea -- let's do more of those! 148 | 149 | = = = = = = = = = = = = = 150 | # Кто использует Python? 151 | 152 | - - - - - - - - - - - - - 153 | ## Программные проекты 154 | 155 | * Blender – популярный 3D-редактор 156 | * BitTorrent, Deluge – файлообменные программы 157 | * Mercurial – система управления версиями 158 | * Dropbox – облачное хранилище 159 | * Компьютерные игры и фреймворки для них – World of Tanks, Civilization IV, The Sims, RenPy 160 | * Plone, Zope – системы управления контентом 161 | * NLTK – анализаторы текстов на естественных языках 162 | * NumPy, SciPy, Pandas – библиотеки для научных вычислений и анализа данных 163 | * Внутренние проекты Google и Yahoo 164 | * ... 165 | 166 | - - - - - - - - - - - - - 167 | ## Исследователи 168 | 169 | * Метеорологи 170 | * Экологи 171 | * Боинформатики 172 | * Специалисты по вычислительной и теоретической физике 173 | * NASA 174 | * ... 175 | 176 | - - - - - - - - - - - - - 177 | ## И так далее... 178 | 179 | https://wiki.python.org/moin/OrganizationsUsingPython 180 | 181 | = = = = = = = = = = = = = 182 | # A теперь немного о Git и GitHub 183 | 184 | 185 | - - - - - - - - - - - - - 186 | ## Configuration & change management 187 | 188 | Организация работы с активами проекта (кодом, документацией, тестами, требованиями, дизайн-спецификациями и пр.) 189 | 190 | * Организация корректного внесения изменений 191 | * Доступ, хранение, история изменений, поддержка распределённого доступа 192 | * Контроль целостности 193 | 194 | 195 | \* Д.В. Кознов. Введение в программную инженерию 196 | 197 | - - - - - - - - - - - - - 198 | ## Типичные проблемы отсутствия конфигурационного управления 199 | 200 | * Не может быть найдена последняя версия кода 201 | * Трудная ошибка, которая была исправлена, неожиданно появилась 202 | * Разработанная и оттестированная функциональность (feature) таинственным образом исчезла 203 | * Полностью оттестированная программа не работает 204 | * Тестируется неактуальная версия 205 | 206 | 207 | \* Д.В. Кознов. Введение в программную инженерию 208 | 209 | - - - - - - - - - - - - - 210 | ## Git 211 | 212 | * Конфигурационное управление в программных проектах 213 | * Управление версиями: централизованное и распределённое 214 | 215 | - - - - - - - - - - - - - 216 | ## Git Workflow 217 | 218 |
219 | ![Git Workflow](images/git-workflow.png) 220 |
221 | 222 | Источник: http://slidedeck.io/themouette/slides-git 223 | 224 | - - - - - - - - - - - - - 225 | ## GitHub 226 | 227 | * Хранилище исходных кодов программных проектов 228 | * Исходные коды как открытых (open source), так и коммерческих проектов 229 | * Здесь держат исходные коды такие компании как IBM, Microsoft, HP, JetBrains 230 | * Владелец ресурса – компания Microsoft (с 2018 г.) 231 | * https://github.com/ 232 | 233 | --- 234 | 235 | * Программные проекты с открытым исходным кодом 236 | * Хостинг открытых программных проектов 237 | * Один из «старых» известных – SourceForge 238 | * Идея «Social Coding» 239 | * [GitHub](https://github.com/), [GitLab](https://gitlab.com/), [BitBucket](https://bitbucket.org/) 240 | 241 | 242 | - - - - - - - - - - - - - 243 | ## GitHub Workflow 244 | 245 |
246 | ![GitHub Workflow](images/github-workflow.png) 247 |
248 | 249 | - - - - - - - - - - - - - 250 | ## GitHub для нас 251 | 252 | * Хранение и публикация выполненных заданий 253 | * Проверка и обсуждение решений 254 | 255 | = = = = = = = = = = = = = 256 | # Спасибо! 257 | 258 | ![](../common/images/qr-edu.dluciv.name-address.png) 259 | 260 | -------------------------------------------------------------------------------- /docs/slides/common/images/vignette-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 66 | 67 | 68 | --------------------------------------------------------------------------------