├── .gitignore └── python_201.ipynb /.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 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /python_201.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# decorators" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "def do_nothing(f):\n", 17 | " print(f)\n", 18 | " return f" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": {}, 25 | "outputs": [ 26 | { 27 | "name": "stdout", 28 | "output_type": "stream", 29 | "text": [ 30 | "\n" 31 | ] 32 | } 33 | ], 34 | "source": [ 35 | "# decorator syntax\n", 36 | "\n", 37 | "@do_nothing\n", 38 | "def greet():\n", 39 | " return \"hello\"" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 3, 45 | "metadata": {}, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "\n" 52 | ] 53 | } 54 | ], 55 | "source": [ 56 | "# same as (\"syntactic\" sugar for)\n", 57 | "\n", 58 | "def greet():\n", 59 | " return \"hello\"\n", 60 | "\n", 61 | "greet = do_nothing(greet)" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 4, 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "data": { 71 | "text/plain": [ 72 | "2" 73 | ] 74 | }, 75 | "execution_count": 4, 76 | "metadata": {}, 77 | "output_type": "execute_result" 78 | } 79 | ], 80 | "source": [ 81 | "# decorator can return anything\n", 82 | "\n", 83 | "def two(f):\n", 84 | " return 2\n", 85 | "\n", 86 | "@two\n", 87 | "def add(x, y):\n", 88 | " return x + y\n", 89 | "\n", 90 | "add" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## example: timer" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "# most often, returns a modified function\n", 107 | "\n", 108 | "def timer(func):\n", 109 | " from datetime import datetime\n", 110 | " def new_function(*args):\n", 111 | " start = datetime.now()\n", 112 | " result = func(*args)\n", 113 | " print('function call took', datetime.now() - start)\n", 114 | " return result\n", 115 | " \n", 116 | " return new_function\n", 117 | "\n", 118 | "@timer\n", 119 | "def add_up(n):\n", 120 | " return sum(range(1, n + 1))" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 6, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "text/plain": [ 131 | ".new_function(*args)>" 132 | ] 133 | }, 134 | "execution_count": 6, 135 | "metadata": {}, 136 | "output_type": "execute_result" 137 | } 138 | ], 139 | "source": [ 140 | "add_up" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 7, 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "function call took 0:00:00.110127\n" 153 | ] 154 | }, 155 | { 156 | "data": { 157 | "text/plain": [ 158 | "50000005000000" 159 | ] 160 | }, 161 | "execution_count": 7, 162 | "metadata": {}, 163 | "output_type": "execute_result" 164 | } 165 | ], 166 | "source": [ 167 | "add_up(10**7)" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 8, 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "(1, 2, 3)\n", 180 | "(1, 2, 3)\n" 181 | ] 182 | }, 183 | { 184 | "data": { 185 | "text/plain": [ 186 | "6" 187 | ] 188 | }, 189 | "execution_count": 8, 190 | "metadata": {}, 191 | "output_type": "execute_result" 192 | } 193 | ], 194 | "source": [ 195 | "# variadic arguments:\n", 196 | "\n", 197 | "def f(*nums):\n", 198 | " print(nums)\n", 199 | " return sum(nums)\n", 200 | "\n", 201 | "f(1, 2, 3)\n", 202 | "# same as\n", 203 | "f(*[1, 2, 3])" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 9, 209 | "metadata": {}, 210 | "outputs": [ 211 | { 212 | "data": { 213 | "text/plain": [ 214 | "2" 215 | ] 216 | }, 217 | "execution_count": 9, 218 | "metadata": {}, 219 | "output_type": "execute_result" 220 | } 221 | ], 222 | "source": [ 223 | "# builtin example\n", 224 | "max(*[1, 2, 0])" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": {}, 230 | "source": [ 231 | "## example: memoization" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 10, 237 | "metadata": {}, 238 | "outputs": [], 239 | "source": [ 240 | "def fib(n):\n", 241 | " if n < 2:\n", 242 | " return 1\n", 243 | " return fib(n - 2) + fib(n - 1)" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 11, 249 | "metadata": {}, 250 | "outputs": [ 251 | { 252 | "name": "stdout", 253 | "output_type": "stream", 254 | "text": [ 255 | "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, " 256 | ] 257 | } 258 | ], 259 | "source": [ 260 | "for n in range(10):\n", 261 | " print(fib(n), end=', ')" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 12, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "name": "stdout", 271 | "output_type": "stream", 272 | "text": [ 273 | "CPU times: user 1.78 s, sys: 4.76 ms, total: 1.79 s\n", 274 | "Wall time: 1.79 s\n" 275 | ] 276 | }, 277 | { 278 | "data": { 279 | "text/plain": [ 280 | "14930352" 281 | ] 282 | }, 283 | "execution_count": 12, 284 | "metadata": {}, 285 | "output_type": "execute_result" 286 | } 287 | ], 288 | "source": [ 289 | "%%time\n", 290 | "fib(35)" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 13, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "# problem: exponential number of mostly redundant calls\n", 300 | "# solution: cache results\n", 301 | "\n", 302 | "cache = {}\n", 303 | "def fib(n):\n", 304 | " if n in cache:\n", 305 | " return cache[n]\n", 306 | " \n", 307 | " if n < 2:\n", 308 | " return 1\n", 309 | " \n", 310 | " cache[n] = fib(n - 2) + fib(n - 1)\n", 311 | " return cache[n]" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": 14, 317 | "metadata": {}, 318 | "outputs": [ 319 | { 320 | "name": "stdout", 321 | "output_type": "stream", 322 | "text": [ 323 | "CPU times: user 11 µs, sys: 0 ns, total: 11 µs\n", 324 | "Wall time: 11.9 µs\n" 325 | ] 326 | }, 327 | { 328 | "data": { 329 | "text/plain": [ 330 | "14930352" 331 | ] 332 | }, 333 | "execution_count": 14, 334 | "metadata": {}, 335 | "output_type": "execute_result" 336 | } 337 | ], 338 | "source": [ 339 | "%%time\n", 340 | "fib(35)" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 15, 346 | "metadata": {}, 347 | "outputs": [], 348 | "source": [ 349 | "# problem: caching and fibonacci are separate concerns, and what if you want to cache other functions\n", 350 | "# solution: factor out caching into a decorator\n", 351 | "\n", 352 | "def memoize(func):\n", 353 | " cache = {}\n", 354 | " \n", 355 | " def inner(*args):\n", 356 | " if args in cache:\n", 357 | " return cache[args]\n", 358 | " cache[args] = func(*args)\n", 359 | " return cache[args]\n", 360 | "\n", 361 | " return inner\n", 362 | "\n", 363 | "@memoize\n", 364 | "def fib(n):\n", 365 | " if n < 2:\n", 366 | " return 1\n", 367 | " return fib(n - 2) + fib(n - 1)" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 16, 373 | "metadata": {}, 374 | "outputs": [ 375 | { 376 | "name": "stdout", 377 | "output_type": "stream", 378 | "text": [ 379 | "CPU times: user 1 µs, sys: 0 ns, total: 1 µs\n", 380 | "Wall time: 1.67 µs\n" 381 | ] 382 | }, 383 | { 384 | "data": { 385 | "text/plain": [ 386 | "14930352" 387 | ] 388 | }, 389 | "execution_count": 16, 390 | "metadata": {}, 391 | "output_type": "execute_result" 392 | } 393 | ], 394 | "source": [ 395 | "%time\n", 396 | "fib(35)" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 17, 402 | "metadata": {}, 403 | "outputs": [ 404 | { 405 | "data": { 406 | "text/plain": [ 407 | "(,\n", 408 | " )" 409 | ] 410 | }, 411 | "execution_count": 17, 412 | "metadata": {}, 413 | "output_type": "execute_result" 414 | } 415 | ], 416 | "source": [ 417 | "# where did the original function go?\n", 418 | "\n", 419 | "fib.__closure__" 420 | ] 421 | }, 422 | { 423 | "cell_type": "markdown", 424 | "metadata": {}, 425 | "source": [ 426 | "## builtin decorator: `property`" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": 18, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [ 435 | "class Person:\n", 436 | " def __init__(self, first, last):\n", 437 | " self.first = first\n", 438 | " self.last = last\n", 439 | " \n", 440 | " def full(self):\n", 441 | " return f'{self.first} {self.last}'" 442 | ] 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": 19, 447 | "metadata": {}, 448 | "outputs": [ 449 | { 450 | "name": "stdout", 451 | "output_type": "stream", 452 | "text": [ 453 | "Emmy\n", 454 | "Noether\n", 455 | "Emmy Noether\n" 456 | ] 457 | } 458 | ], 459 | "source": [ 460 | "emmy = Person('Emmy', 'Noether')\n", 461 | "print(emmy.first)\n", 462 | "print(emmy.last)\n", 463 | "print(emmy.full())" 464 | ] 465 | }, 466 | { 467 | "cell_type": "code", 468 | "execution_count": 20, 469 | "metadata": {}, 470 | "outputs": [], 471 | "source": [ 472 | "# problem: full should just be an attribute, but based on first and last\n", 473 | "# solution: the `property` decorator\n", 474 | "\n", 475 | "class Person:\n", 476 | " def __init__(self, first, last):\n", 477 | " self.first = first\n", 478 | " self.last = last\n", 479 | " \n", 480 | " @property\n", 481 | " def full(self):\n", 482 | " return f'{self.first} {self.last}'" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 21, 488 | "metadata": {}, 489 | "outputs": [ 490 | { 491 | "name": "stdout", 492 | "output_type": "stream", 493 | "text": [ 494 | "Emmy\n", 495 | "Noether\n", 496 | "Emmy Noether\n" 497 | ] 498 | } 499 | ], 500 | "source": [ 501 | "emmy = Person('Emmy', 'Noether')\n", 502 | "print(emmy.first)\n", 503 | "print(emmy.last)\n", 504 | "print(emmy.full)" 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 22, 510 | "metadata": {}, 511 | "outputs": [], 512 | "source": [ 513 | "# can also add setter\n", 514 | "\n", 515 | "class Person:\n", 516 | " def __init__(self, first, last):\n", 517 | " self.first = first\n", 518 | " self.last = last\n", 519 | " \n", 520 | " @property\n", 521 | " def full(self):\n", 522 | " return f'{self.first} {self.last}'\n", 523 | " \n", 524 | " @full.setter\n", 525 | " def full(self, value):\n", 526 | " self.first, self.last = value.strip().split()" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 23, 532 | "metadata": {}, 533 | "outputs": [ 534 | { 535 | "name": "stdout", 536 | "output_type": "stream", 537 | "text": [ 538 | "George\n", 539 | "Orwell\n" 540 | ] 541 | } 542 | ], 543 | "source": [ 544 | "eric = Person('Eric', 'Blair')\n", 545 | "eric.full = 'George Orwell'\n", 546 | "print(eric.first)\n", 547 | "print(eric.last)" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "``property`` is an example of a \"descriptor\". You can make custom descriptors, and they enable frameworks such as SQLAlchemy." 555 | ] 556 | }, 557 | { 558 | "cell_type": "markdown", 559 | "metadata": {}, 560 | "source": [ 561 | "# lambda" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": 24, 567 | "metadata": {}, 568 | "outputs": [], 569 | "source": [ 570 | "from random import randint" 571 | ] 572 | }, 573 | { 574 | "cell_type": "code", 575 | "execution_count": 25, 576 | "metadata": {}, 577 | "outputs": [ 578 | { 579 | "data": { 580 | "text/plain": [ 581 | "[-4, 1, -10, 0, 3, -3, -8, -5, 5, 2]" 582 | ] 583 | }, 584 | "execution_count": 25, 585 | "metadata": {}, 586 | "output_type": "execute_result" 587 | } 588 | ], 589 | "source": [ 590 | "lst = [randint(-10,10) for _ in range(10)]\n", 591 | "lst" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 26, 597 | "metadata": {}, 598 | "outputs": [ 599 | { 600 | "data": { 601 | "text/plain": [ 602 | "[-10, -8, -5, -4, -3, 0, 1, 2, 3, 5]" 603 | ] 604 | }, 605 | "execution_count": 26, 606 | "metadata": {}, 607 | "output_type": "execute_result" 608 | } 609 | ], 610 | "source": [ 611 | "# a bad but succinct implementation of quicksort\n", 612 | "\n", 613 | "# operator `:=` denotes an assignment expression\n", 614 | "# allowing us to inline the function\n", 615 | "\n", 616 | "# Python's \"ternary\" expression:\n", 617 | "# [expression] if [condition] else [expression]\n", 618 | "\n", 619 | "(qsort := lambda lst: [] if not lst else \\\n", 620 | " qsort([n for n in lst[:-1] if n < lst[-1]]) \\\n", 621 | " + [lst[-1]] \\\n", 622 | " + qsort([n for n in lst[:-1] if n >= lst[-1]])\n", 623 | ")(lst)" 624 | ] 625 | }, 626 | { 627 | "cell_type": "code", 628 | "execution_count": 27, 629 | "metadata": {}, 630 | "outputs": [ 631 | { 632 | "data": { 633 | "text/plain": [ 634 | "(lst)>" 635 | ] 636 | }, 637 | "execution_count": 27, 638 | "metadata": {}, 639 | "output_type": "execute_result" 640 | } 641 | ], 642 | "source": [ 643 | "qsort" 644 | ] 645 | }, 646 | { 647 | "cell_type": "markdown", 648 | "metadata": {}, 649 | "source": [ 650 | "\"qsort\" is a name, so ``qsort`` is not anonymous.\n", 651 | "\n", 652 | "Anonymous recursive functions are possible:" 653 | ] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": 28, 658 | "metadata": {}, 659 | "outputs": [ 660 | { 661 | "data": { 662 | "text/plain": [ 663 | "[-10, -8, -5, -4, -3, 0, 1, 2, 3, 5]" 664 | ] 665 | }, 666 | "execution_count": 28, 667 | "metadata": {}, 668 | "output_type": "execute_result" 669 | } 670 | ], 671 | "source": [ 672 | "(lambda g: (lambda f: lambda n: g(f(f))(n))(lambda f: lambda n: g(f(f))(n)))(\n", 673 | " lambda f: lambda lst: []\n", 674 | " if not lst\n", 675 | " else f([n for n in lst[1:] if n < lst[0]])\n", 676 | " + [lst[0]]\n", 677 | " + f([n for n in lst[1:] if n >= lst[0]])\n", 678 | ")(lst)" 679 | ] 680 | }, 681 | { 682 | "cell_type": "markdown", 683 | "metadata": {}, 684 | "source": [ 685 | "For details, see\n", 686 | "\n", 687 | "https://scotchka.github.io/blog/html/2020/06/28/a_recursive_function_that_does_not_call_itself.html" 688 | ] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": {}, 693 | "source": [ 694 | "# generators" 695 | ] 696 | }, 697 | { 698 | "cell_type": "code", 699 | "execution_count": 29, 700 | "metadata": {}, 701 | "outputs": [], 702 | "source": [ 703 | "def count():\n", 704 | " print('hello')\n", 705 | " yield 0\n", 706 | " print('world')\n", 707 | " yield 1\n", 708 | " print('!')\n", 709 | " yield 2" 710 | ] 711 | }, 712 | { 713 | "cell_type": "code", 714 | "execution_count": 30, 715 | "metadata": {}, 716 | "outputs": [ 717 | { 718 | "data": { 719 | "text/plain": [ 720 | "" 721 | ] 722 | }, 723 | "execution_count": 30, 724 | "metadata": {}, 725 | "output_type": "execute_result" 726 | } 727 | ], 728 | "source": [ 729 | "c = count()\n", 730 | "c" 731 | ] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": 31, 736 | "metadata": {}, 737 | "outputs": [ 738 | { 739 | "name": "stdout", 740 | "output_type": "stream", 741 | "text": [ 742 | "hello\n" 743 | ] 744 | }, 745 | { 746 | "data": { 747 | "text/plain": [ 748 | "0" 749 | ] 750 | }, 751 | "execution_count": 31, 752 | "metadata": {}, 753 | "output_type": "execute_result" 754 | } 755 | ], 756 | "source": [ 757 | "next(c)" 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "execution_count": 32, 763 | "metadata": {}, 764 | "outputs": [ 765 | { 766 | "name": "stdout", 767 | "output_type": "stream", 768 | "text": [ 769 | "0\n", 770 | "1\n", 771 | "2\n", 772 | "3\n", 773 | "4\n" 774 | ] 775 | } 776 | ], 777 | "source": [ 778 | "def count(n=None):\n", 779 | " i = 0\n", 780 | " while not n or i < n:\n", 781 | " yield i\n", 782 | " i += 1\n", 783 | " \n", 784 | "for num in count(5):\n", 785 | " print(num)" 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "execution_count": 33, 791 | "metadata": {}, 792 | "outputs": [ 793 | { 794 | "data": { 795 | "text/plain": [ 796 | "0" 797 | ] 798 | }, 799 | "execution_count": 33, 800 | "metadata": {}, 801 | "output_type": "execute_result" 802 | } 803 | ], 804 | "source": [ 805 | "# no upper bound\n", 806 | "c = count()\n", 807 | "next(c)" 808 | ] 809 | }, 810 | { 811 | "cell_type": "markdown", 812 | "metadata": {}, 813 | "source": [ 814 | "## example: tree traversal" 815 | ] 816 | }, 817 | { 818 | "cell_type": "code", 819 | "execution_count": 34, 820 | "metadata": {}, 821 | "outputs": [], 822 | "source": [ 823 | "class Node:\n", 824 | " def __init__(self, value, left=None, right=None):\n", 825 | " self.value = value\n", 826 | " self.left = left\n", 827 | " self.right = right\n", 828 | " \n", 829 | " @classmethod\n", 830 | " def make_tree(cls, lst):\n", 831 | " \"\"\"Make a balanced binary tree.\"\"\"\n", 832 | " if not lst:\n", 833 | " return None\n", 834 | " lst = sorted(lst)\n", 835 | " \n", 836 | " mid = len(lst) // 2\n", 837 | " \n", 838 | " node = cls(lst[mid])\n", 839 | " \n", 840 | " node.left = cls.make_tree(lst[:mid])\n", 841 | " node.right = cls.make_tree(lst[mid+1:])\n", 842 | " \n", 843 | " return node\n", 844 | " \n", 845 | " def __iter__(self):\n", 846 | " if self.left:\n", 847 | " for node in self.left:\n", 848 | " yield node\n", 849 | " \n", 850 | " yield self.value\n", 851 | " \n", 852 | " if self.right:\n", 853 | " for node in self.right:\n", 854 | " yield node\n", 855 | " " 856 | ] 857 | }, 858 | { 859 | "cell_type": "code", 860 | "execution_count": 35, 861 | "metadata": {}, 862 | "outputs": [ 863 | { 864 | "name": "stdout", 865 | "output_type": "stream", 866 | "text": [ 867 | "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, " 868 | ] 869 | } 870 | ], 871 | "source": [ 872 | "lst = list(range(25))\n", 873 | "\n", 874 | "bst = Node.make_tree(lst)\n", 875 | "\n", 876 | "for value in bst:\n", 877 | " print(value, end=', ')" 878 | ] 879 | }, 880 | { 881 | "cell_type": "code", 882 | "execution_count": null, 883 | "metadata": {}, 884 | "outputs": [], 885 | "source": [] 886 | } 887 | ], 888 | "metadata": { 889 | "kernelspec": { 890 | "display_name": "Python 3 (ipykernel)", 891 | "language": "python", 892 | "name": "python3" 893 | }, 894 | "language_info": { 895 | "codemirror_mode": { 896 | "name": "ipython", 897 | "version": 3 898 | }, 899 | "file_extension": ".py", 900 | "mimetype": "text/x-python", 901 | "name": "python", 902 | "nbconvert_exporter": "python", 903 | "pygments_lexer": "ipython3", 904 | "version": "3.9.10" 905 | } 906 | }, 907 | "nbformat": 4, 908 | "nbformat_minor": 2 909 | } 910 | --------------------------------------------------------------------------------