├── .gitattributes
├── LICENSE
├── README.md
├── basic.ipynb
├── black_magics.ipynb
├── html
├── basic.html
├── black_magics.html
└── iterables.html
└── iterables.ipynb
/.gitattributes:
--------------------------------------------------------------------------------
1 | html/* linguist-vendored
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 recnac
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python Tricks
2 |
3 |
4 |
5 | ## Description
6 |
7 | ***"life is short, you need python"***
8 |
9 | **Python Tricks** here you can understand them as something like advanced techniques, patterns or philosophy of Python. (Some are indeed tricks, by the way☺)
10 |
11 |
12 |
13 | Currently, it mainly contains three parts: **basic**, **iterable** and **black magic**.
14 |
15 | The most valuable part is `Inspiration`. Even though you already knew this trick, but you rarely use it, because you don't know where to use, and `Inspiration` part will enlighten you.
16 |
17 |
18 |
19 | Many example codes come from [LeetCode](https://leetcode.com/problemset/all/), I have tried my best to make a brief problem description, useful enough for most case, actually you can only focus on the trick. If you are also interested in problem, and want to go deep, you can visit [LeetCode](https://leetcode.com/problemset/all/) for more information.
20 |
21 |
22 |
23 | ## Further Elaboration of Python
24 |
25 | Python is a **high-level**, **interpreted**, and **dynamic** programming language. Python offers a great compromise between development and execution time. It improves **development efficiency** within acceptable performance loss. With the **simplicity of grammar**, **high-level language features**, and **rich extensive support libraries**, Python helps you to focus more on your **own logic**.
26 |
27 | Python used to be a glue or scripting language to increase productivity. Now Python has a complete technology stack, community ecology, and supporting tools and frameworks. On the strength of **conciseness**, **readability** and **interactivity**, python also play an important role in the field of data science and artificial intelligence.
28 |
29 |
30 |
31 | ## Why should use tricks?
32 |
33 | To make code concise, elegant and high-quality. (also efficient enough)
34 |
35 | In my opinion, it obeys Python's philosophy: trading maybe a little bit performance for readability, usability, coding efficiency, and reducing possibility of bugs.
36 |
37 | Or you simply want to play and have fun with Python.
38 |
39 |
40 |
41 | ## Attention
42 |
43 | Like I said here: https://stackoverflow.com/q/55684960/11263560
44 |
45 | > Discussion about whether should use python tricks like `~`:
46 | >
47 | > In my opinion, If it is a code maintained by yourself, you can use any trick to avoid potential bug or achieve goal easier, because of maybe a high readability and usability. But in team work, **avoid using 'too clever' code**, may bring troubles to your co-workers.
48 |
49 | In Python Tricks, not all tricks are tricky, and not all tricks are suitable in your scenario. So It is you to decide which tricks should use, and which are not.
50 |
51 | Actually I have added extra comments about the defect of tricks, to help you decide.
52 |
53 |
54 |
55 |
56 | ## Who is this for?
57 | 1. If you have mastered basic python skills or transferring from another language, and you want to enhance your python skills, to write a more pythonic-style code, here you can learn how to and where to use these advanced python tricks.
58 |
59 | 2. If you want to write more cool or hackish python code, or just have a fun with python, here you will be enlightened and enjoy it.
60 |
61 |
62 |
63 | ## Who isn't this for?
64 |
65 | This is not for Python beginners. I try to make it short and refined, just pick the most valuable tricks.
66 |
67 | So it not includes all the advanced Python features, and I will not give a complete tutorial, either. Maybe you need to refer to other materials to help you understand.
68 |
69 |
70 |
71 | ## Environment
72 |
73 | ### Version
74 |
75 | Python 3.6 or Above. But some examples have no version restrictions.
76 |
77 |
78 |
79 | ### **Development Environment**
80 |
81 | In the learning and debugging stage, it is recommended to use an **interactive** development environment such as **Jupyter Notebook**.
82 |
83 |
84 |
85 | ## Future work
86 |
87 | Currently, it mainly contains three parts: **basic**, **iterable** and **black magic**. I will continuously update once I dig a gold.
88 |
89 | Maybe I will add the advanced usages and black magic of standard library, such as cool operator overloading, and also pick cool ideas from extra python tools.
90 |
91 | Maybe I will add more design patterns and philosophy of Python.
92 |
93 |
94 |
95 | ## Thanks to
96 | * Special thanks to [Stefan Pochmann](https://leetcode.com/stefanpochmann/). Many enlightening codes come from Stefan. I really learned a lot from him.
97 | * Thanks to [Stack Overflow](https://stackoverflow.com/) for the great questions and answers.
98 | * Thanks to [LeetCode](https://leetcode.com/problemset/all/) for the algorithm problems. Moreover, LeetCode has a great OJ platform. It is very suitable for functional and performance tests.
99 | * Many templates and tricks are enlightened by discussions in LeetCode. Thanks to all the code masters.
100 | * Thanks to many other articles and code snippets that I have referred to.
101 |
102 |
103 |
104 | ## MIT License
105 |
106 | Feel free to use, copy, modify, merge or publish. just add a reference, please.
107 |
108 | If it helps you or you like it, you can star or share it. thanks : )
109 |
110 | I'm glad if you want to contribute to make it better.
111 |
112 |
113 |
114 | ## Source
115 |
116 | This project is the practical part of a series of Pluralsight Guides.
117 |
118 | - [Python Tricks - Introduction](/guides/python-tricks-introduction)
119 | - [Python Tricks - Basic - Part 1](/guides/python-tricks-basic-part-1)
120 | - [Python Tricks - Basic - Part 2](/guides/python-tricks-basic-part-2)
121 | - [Python Tricks - Iterable - Part 1](/guides/python-tricks-iterable-part-1)
122 | - [Python Tricks - Iterable - Part 2](/guides/python-tricks-iterable-part-2)
123 | - [Python Tricks - Black Magic - Part 1](/guides/python-tricks-black-magic-part-1)
124 | - [Python Tricks - Black Magic - Part 2](/guides/python-tricks-black-magic-part-2)
125 |
126 |
127 |
128 | ## Contact
129 | recnac@foxmail.com
--------------------------------------------------------------------------------
/basic.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 🐍Python Tricks - Basics"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "This notebook is the practical part of Pluralsight Guides: [Python Tricks - Basic - Part 1](https://www.pluralsight.com/guides/python-tricks-basic-part-1) and [Python Tricks - Basic - Part 2](https://www.pluralsight.com/guides/python-tricks-basic-part-2)"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "## Table of Content:\n",
22 | "* [Boolean Expression](#anchor1)\n",
23 | "* [Build Tuple](#anchor2)\n",
24 | "* [Value of Boolean](#anchor3)\n",
25 | "* [Ternary Operator](#anchor4)\n",
26 | "* [Chained Operations](#anchor5)\n",
27 | "* [Multiple Assignment](#anchor6)\n",
28 | "* [Mirror Index](#anchor7)\n",
29 | "* [return None](#anchor8)\n",
30 | "* [Slice Assignment](#anchor9)\n",
31 | "* [Maximum/Minimum Integer](#anchor10)\n",
32 | "* [for else / while else](#anchor11)\n",
33 | "* [Emulate switch](#anchor12)\n",
34 | "* [Decorator](#anchor13)\n",
35 | "* [Modify While Iteration](#anchor14)"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "***"
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {},
48 | "source": [
49 | "## Boolean Expression "
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "metadata": {},
55 | "source": [
56 | "lazy evaluation, `b` may not be executed"
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "metadata": {},
62 | "source": [
63 | "### a or b"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {},
69 | "source": [
70 | "if `a` is the truthy value, return `a`. Otherwise, return `b`."
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "Here is the Falsy Values list from official document: \n",
78 | "\n",
79 | "- Constants defined to be false: None and False.\n",
80 | "- Zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1).\n",
81 | "- Empty sequences and collections: '', (), [], {}, set(), range(0)."
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": 1,
87 | "metadata": {},
88 | "outputs": [
89 | {
90 | "data": {
91 | "text/plain": [
92 | "42"
93 | ]
94 | },
95 | "execution_count": 1,
96 | "metadata": {},
97 | "output_type": "execute_result"
98 | }
99 | ],
100 | "source": [
101 | "a, b = 0, 42\n",
102 | "a or b"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": 2,
108 | "metadata": {},
109 | "outputs": [
110 | {
111 | "data": {
112 | "text/plain": [
113 | "0"
114 | ]
115 | },
116 | "execution_count": 2,
117 | "metadata": {},
118 | "output_type": "execute_result"
119 | }
120 | ],
121 | "source": [
122 | "a, b = '', 0\n",
123 | "a or b"
124 | ]
125 | },
126 | {
127 | "cell_type": "markdown",
128 | "metadata": {},
129 | "source": [
130 | "#### Inspiration:\n",
131 | "```python\n",
132 | "\"\"\"connect with a non-empty linked list\"\"\"\n",
133 | "cur.next = l1 or l2\n",
134 | "```"
135 | ]
136 | },
137 | {
138 | "cell_type": "markdown",
139 | "metadata": {},
140 | "source": [
141 | "### a and b"
142 | ]
143 | },
144 | {
145 | "cell_type": "markdown",
146 | "metadata": {},
147 | "source": [
148 | "if `a` is the falsy value, return `a`. Otherwise, return `b`."
149 | ]
150 | },
151 | {
152 | "cell_type": "code",
153 | "execution_count": 3,
154 | "metadata": {},
155 | "outputs": [
156 | {
157 | "data": {
158 | "text/plain": [
159 | "2"
160 | ]
161 | },
162 | "execution_count": 3,
163 | "metadata": {},
164 | "output_type": "execute_result"
165 | }
166 | ],
167 | "source": [
168 | "a, b = 1, 2\n",
169 | "a and b"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 4,
175 | "metadata": {},
176 | "outputs": [
177 | {
178 | "data": {
179 | "text/plain": [
180 | "''"
181 | ]
182 | },
183 | "execution_count": 4,
184 | "metadata": {},
185 | "output_type": "execute_result"
186 | }
187 | ],
188 | "source": [
189 | "a, b = '', 'abc'\n",
190 | "a and b"
191 | ]
192 | },
193 | {
194 | "cell_type": "markdown",
195 | "metadata": {},
196 | "source": [
197 | "#### Inspiration:\n",
198 | "```python\n",
199 | "\"\"\"call function before assignment\"\"\"\n",
200 | "last = not arr.append(x) and arr[-1]\n",
201 | "```"
202 | ]
203 | },
204 | {
205 | "cell_type": "markdown",
206 | "metadata": {},
207 | "source": [
208 | "## Build Tuple "
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "metadata": {},
214 | "source": [
215 | "Construct a temporary `tuple` for specific usages"
216 | ]
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "metadata": {},
221 | "source": [
222 | "#### Inspirations:\n",
223 | "```python\n",
224 | "\"\"\"build in condition\"\"\"\n",
225 | "if elem in (elem1, elem2): # equals to: if elem == elem1 or elem == elem2\n",
226 | "\n",
227 | "\"\"\"build result\"\"\"\n",
228 | "return [dp[-1], -1][dp[-1] == float('inf')]\t\t# equals to if-else, but more concise\n",
229 | "\n",
230 | "\"\"\"build 4-neighbors as range\"\"\"\n",
231 | "for I, J in (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1):\n",
232 | "```"
233 | ]
234 | },
235 | {
236 | "cell_type": "markdown",
237 | "metadata": {},
238 | "source": [
239 | "## Value of Boolean "
240 | ]
241 | },
242 | {
243 | "cell_type": "code",
244 | "execution_count": 5,
245 | "metadata": {},
246 | "outputs": [
247 | {
248 | "data": {
249 | "text/plain": [
250 | "(1, 0)"
251 | ]
252 | },
253 | "execution_count": 5,
254 | "metadata": {},
255 | "output_type": "execute_result"
256 | }
257 | ],
258 | "source": [
259 | "int(True), int(False)"
260 | ]
261 | },
262 | {
263 | "cell_type": "markdown",
264 | "metadata": {},
265 | "source": [
266 | "#### Inspirations:\n",
267 | "```python\n",
268 | "\"\"\"used in sum\"\"\"\n",
269 | "A = sum(a == b for a, b in zip(secret, guess)) \n",
270 | "\n",
271 | "\"\"\"used as index\"\"\"\n",
272 | "return [dp[-1], -1][dp[-1] == float('inf')] \n",
273 | "\n",
274 | "\"\"\"used in temporary tuple\"\"\"\n",
275 | "# L236: find the lowest common ancestor in the binary search tree\n",
276 | "def lowest_common_ancestor(root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:\n",
277 | " while (root.val - p.val) * (root.val - q.val) > 0:\n",
278 | " root = (root.left, root.right)[p.val > root.val] # equals to if else, seems more clear\n",
279 | " return root\n",
280 | "\n",
281 | "# a strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down)\n",
282 | "# L247: find all strobogrammatic numbers that are of length = n\n",
283 | "def find_strobogrammatic(n: int) -> List[str]:\n",
284 | " \"\"\"used in list initialization\"\"\"\n",
285 | " nums = n % 2 * list('018') or ['']\n",
286 | " while n > 1:\n",
287 | " n -= 2\n",
288 | " \"\"\"used in slice\"\"\"\n",
289 | " nums = [a + num + b for a, b in '00 11 88 69 96'.split()[n < 2:] for num in nums]\n",
290 | " return nums\n",
291 | "```"
292 | ]
293 | },
294 | {
295 | "cell_type": "markdown",
296 | "metadata": {},
297 | "source": [
298 | "## Ternary Operator "
299 | ]
300 | },
301 | {
302 | "cell_type": "markdown",
303 | "metadata": {},
304 | "source": [
305 | "### Built-in Grammar"
306 | ]
307 | },
308 | {
309 | "cell_type": "markdown",
310 | "metadata": {},
311 | "source": [
312 | "This is the most direct way which is supported since version 2.5. The expression syntax is:"
313 | ]
314 | },
315 | {
316 | "cell_type": "markdown",
317 | "metadata": {},
318 | "source": [
319 | "```python\n",
320 | "\"\"\"conditional operator\"\"\"\n",
321 | "true_expression if condition else false_expression\n",
322 | "```"
323 | ]
324 | },
325 | {
326 | "cell_type": "code",
327 | "execution_count": 6,
328 | "metadata": {},
329 | "outputs": [
330 | {
331 | "data": {
332 | "text/plain": [
333 | "20"
334 | ]
335 | },
336 | "execution_count": 6,
337 | "metadata": {},
338 | "output_type": "execute_result"
339 | }
340 | ],
341 | "source": [
342 | "\"\"\"find maximum in pairs\"\"\"\n",
343 | "a, b = 10, 20\n",
344 | "max_val = a if a > b else b\n",
345 | "max_val"
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": 7,
351 | "metadata": {},
352 | "outputs": [
353 | {
354 | "data": {
355 | "text/plain": [
356 | "5"
357 | ]
358 | },
359 | "execution_count": 7,
360 | "metadata": {},
361 | "output_type": "execute_result"
362 | }
363 | ],
364 | "source": [
365 | "\"\"\"find mininmum in triplets\"\"\"\n",
366 | "a, b, c = 10, 20, 5\n",
367 | "min_val = a if a < b and a < c else (b if b < c else c)\n",
368 | "min_val"
369 | ]
370 | },
371 | {
372 | "cell_type": "markdown",
373 | "metadata": {},
374 | "source": [
375 | "### Boolean Expression Technique"
376 | ]
377 | },
378 | {
379 | "cell_type": "markdown",
380 | "metadata": {},
381 | "source": [
382 | "For versions prior to 2.5, we can use the boolean expression trick that we have discussed before:"
383 | ]
384 | },
385 | {
386 | "cell_type": "markdown",
387 | "metadata": {},
388 | "source": [
389 | "```python\n",
390 | "\"\"\"correct way by encapsulation with tuples\"\"\"\n",
391 | "\"\"\"(False,) is truthy value\"\"\"\n",
392 | "(condition and (true_expression,) or (false_expression,))[0]\n",
393 | "\n",
394 | "\"\"\"more concise and faster way with limit\"\"\"\n",
395 | "\"\"\"should make sure true_expression is never falsy\"\"\"\n",
396 | "condition and true_expression or false_expression\n",
397 | "```"
398 | ]
399 | },
400 | {
401 | "cell_type": "markdown",
402 | "metadata": {},
403 | "source": [
404 | "### Boolean Index Technique"
405 | ]
406 | },
407 | {
408 | "cell_type": "markdown",
409 | "metadata": {},
410 | "source": [
411 | "This solution uses Value of Boolean and Build Tuple tricks."
412 | ]
413 | },
414 | {
415 | "cell_type": "markdown",
416 | "metadata": {},
417 | "source": [
418 | "```python\n",
419 | "\"\"\"tuple index\"\"\"\n",
420 | "\"\"\"for safety we explicitly test for truthiness by bool()\"\"\"\n",
421 | "(false_expression, true_expression)[bool(condition)]\n",
422 | "\n",
423 | "\"\"\"dict index\"\"\"\n",
424 | "{True: true_expression, False: false_expression}[bool(condition)]\n",
425 | "\n",
426 | "\"\"\"lambda version supports lazy evaluation\"\"\"\n",
427 | "(lambda: false_expression, lambda: true_expression)[bool(condition)]()\n",
428 | "```"
429 | ]
430 | },
431 | {
432 | "cell_type": "markdown",
433 | "metadata": {},
434 | "source": [
435 | "### Pack with Boolean Index"
436 | ]
437 | },
438 | {
439 | "cell_type": "markdown",
440 | "metadata": {},
441 | "source": [
442 | "Here is a elegant pythonic solution. It is packed as function based on Boolean Index Technique solution. It looks pretty clean and easy to read for the caller."
443 | ]
444 | },
445 | {
446 | "cell_type": "code",
447 | "execution_count": 8,
448 | "metadata": {},
449 | "outputs": [],
450 | "source": [
451 | "\"\"\"pack as function\"\"\"\n",
452 | "\"\"\"not not x equals to bool(x)\"\"\"\n",
453 | "def _if(condition):\n",
454 | " return lambda false_expression: lambda true_expression:\t\\\n",
455 | " [delay(true_expression), delay(false_expression)][not not condition]()\n",
456 | "\n",
457 | "\"\"\"unified as callable\"\"\"\n",
458 | "def delay(f):\n",
459 | " if callable(f): return f\n",
460 | " else: return lambda: f"
461 | ]
462 | },
463 | {
464 | "cell_type": "code",
465 | "execution_count": 9,
466 | "metadata": {},
467 | "outputs": [
468 | {
469 | "data": {
470 | "text/plain": [
471 | "10"
472 | ]
473 | },
474 | "execution_count": 9,
475 | "metadata": {},
476 | "output_type": "execute_result"
477 | }
478 | ],
479 | "source": [
480 | "a, b = 10, 20\n",
481 | "min_val = _if(a < b) (a) (b)\n",
482 | "min_val"
483 | ]
484 | },
485 | {
486 | "cell_type": "markdown",
487 | "metadata": {},
488 | "source": [
489 | "## Chained Operations "
490 | ]
491 | },
492 | {
493 | "cell_type": "markdown",
494 | "metadata": {},
495 | "source": [
496 | "In Python, assignment statements are not expressions and thus do not have a value. Instead, chained assignments are a series of statements with multiple targets for a single expression. The assignments are executed left-to-right."
497 | ]
498 | },
499 | {
500 | "cell_type": "markdown",
501 | "metadata": {},
502 | "source": [
503 | "### Chained Assignment"
504 | ]
505 | },
506 | {
507 | "cell_type": "markdown",
508 | "metadata": {},
509 | "source": [
510 | "```python\n",
511 | "x = y = f()\n",
512 | "```"
513 | ]
514 | },
515 | {
516 | "cell_type": "markdown",
517 | "metadata": {},
518 | "source": [
519 | "is equivalent to"
520 | ]
521 | },
522 | {
523 | "cell_type": "markdown",
524 | "metadata": {},
525 | "source": [
526 | "```python\n",
527 | "temp = f()\n",
528 | "x = temp\t# assign from left to right\n",
529 | "y = temp\n",
530 | "```"
531 | ]
532 | },
533 | {
534 | "cell_type": "code",
535 | "execution_count": 10,
536 | "metadata": {},
537 | "outputs": [
538 | {
539 | "data": {
540 | "text/plain": [
541 | "(1, 1)"
542 | ]
543 | },
544 | "execution_count": 10,
545 | "metadata": {},
546 | "output_type": "execute_result"
547 | }
548 | ],
549 | "source": [
550 | "a = b = 1\n",
551 | "a, b"
552 | ]
553 | },
554 | {
555 | "cell_type": "code",
556 | "execution_count": 11,
557 | "metadata": {},
558 | "outputs": [
559 | {
560 | "data": {
561 | "text/plain": [
562 | "True"
563 | ]
564 | },
565 | "execution_count": 11,
566 | "metadata": {},
567 | "output_type": "execute_result"
568 | }
569 | ],
570 | "source": [
571 | "import random\n",
572 | "x = y = random.random()\n",
573 | "x == y"
574 | ]
575 | },
576 | {
577 | "cell_type": "code",
578 | "execution_count": 12,
579 | "metadata": {},
580 | "outputs": [
581 | {
582 | "data": {
583 | "text/plain": [
584 | "False"
585 | ]
586 | },
587 | "execution_count": 12,
588 | "metadata": {},
589 | "output_type": "execute_result"
590 | }
591 | ],
592 | "source": [
593 | "x = random.random()\n",
594 | "y = random.random()\n",
595 | "x == y"
596 | ]
597 | },
598 | {
599 | "cell_type": "markdown",
600 | "metadata": {},
601 | "source": [
602 | "#### Inspiration:\n",
603 | "```python\n",
604 | "\"\"\"initialize dummy node and assign to cur\"\"\"\n",
605 | "dummy = ListNode(0)\n",
606 | "cur = dummy.next = head\n",
607 | "```"
608 | ]
609 | },
610 | {
611 | "cell_type": "markdown",
612 | "metadata": {},
613 | "source": [
614 | "### Chained Comparison\n",
615 | "Chained Comparison is a nice syntactic sugar in Python to simplify expression."
616 | ]
617 | },
618 | {
619 | "cell_type": "code",
620 | "execution_count": 13,
621 | "metadata": {
622 | "scrolled": true
623 | },
624 | "outputs": [
625 | {
626 | "data": {
627 | "text/plain": [
628 | "True"
629 | ]
630 | },
631 | "execution_count": 13,
632 | "metadata": {},
633 | "output_type": "execute_result"
634 | }
635 | ],
636 | "source": [
637 | "a == b == 1"
638 | ]
639 | },
640 | {
641 | "cell_type": "code",
642 | "execution_count": 14,
643 | "metadata": {},
644 | "outputs": [
645 | {
646 | "data": {
647 | "text/plain": [
648 | "True"
649 | ]
650 | },
651 | "execution_count": 14,
652 | "metadata": {},
653 | "output_type": "execute_result"
654 | }
655 | ],
656 | "source": [
657 | "a == b != 2"
658 | ]
659 | },
660 | {
661 | "cell_type": "code",
662 | "execution_count": 15,
663 | "metadata": {},
664 | "outputs": [
665 | {
666 | "data": {
667 | "text/plain": [
668 | "True"
669 | ]
670 | },
671 | "execution_count": 15,
672 | "metadata": {},
673 | "output_type": "execute_result"
674 | }
675 | ],
676 | "source": [
677 | "0 <= a < 4"
678 | ]
679 | },
680 | {
681 | "cell_type": "markdown",
682 | "metadata": {},
683 | "source": [
684 | "#### Inspirations:\n",
685 | "```python\n",
686 | "\"\"\"short for a > 0 and b >= 0\"\"\"\n",
687 | "if a > 0 <= b:\n",
688 | " \n",
689 | "\"\"\"chained comparison in binary search\"\"\"\n",
690 | "if nums[lo] < target < nums[mid]:\n",
691 | " hi = mid - 1\n",
692 | " \n",
693 | "\"\"\"check matrix boundary in a unified way\"\"\"\n",
694 | "for i in range(len(matrix)):\n",
695 | " for j in range(len(matrix[0])):\n",
696 | " # construct neighbor iterator\n",
697 | " for I, J in (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1):\n",
698 | " # although add some unnecessary checks, expression is more concise\n",
699 | " if 0 <= I < len(matrix) and 0 <= J < len(matrix[0]):\n",
700 | " process_neighbor_logic(matrix[I][J])\n",
701 | "```"
702 | ]
703 | },
704 | {
705 | "cell_type": "markdown",
706 | "metadata": {},
707 | "source": [
708 | "## Multiple Assignment "
709 | ]
710 | },
711 | {
712 | "cell_type": "markdown",
713 | "metadata": {},
714 | "source": [
715 | "Multiple assignment can assign multiple variables at the \"same time\", and it is useful in multiple initialization or swap."
716 | ]
717 | },
718 | {
719 | "cell_type": "code",
720 | "execution_count": 16,
721 | "metadata": {},
722 | "outputs": [
723 | {
724 | "data": {
725 | "text/plain": [
726 | "(2, 1)"
727 | ]
728 | },
729 | "execution_count": 16,
730 | "metadata": {},
731 | "output_type": "execute_result"
732 | }
733 | ],
734 | "source": [
735 | "\"\"\"multiple initialization\"\"\"\n",
736 | "a, b = 1, 2\n",
737 | "\"\"\"swap\"\"\"\n",
738 | "a, b = b, a\n",
739 | "a, b"
740 | ]
741 | },
742 | {
743 | "cell_type": "markdown",
744 | "metadata": {},
745 | "source": [
746 | "#### Inspiration:\n",
747 | "```python\n",
748 | "\"\"\"next assignment in linked list\"\"\"\n",
749 | "# revserse a linked list\n",
750 | "def reverse(head: ListNode) -> ListNode:\n",
751 | " prev, cur = None, head\n",
752 | " while cur:\n",
753 | " cur.next, cur, prev = prev, cur.next, cur\n",
754 | " return prev\n",
755 | "```"
756 | ]
757 | },
758 | {
759 | "cell_type": "markdown",
760 | "metadata": {},
761 | "source": [
762 | "actually this is a **pack-unpack** technique.\n",
763 | "```python\n",
764 | "a, b = b, a\n",
765 | "```\n",
766 | "is equivalent to \n",
767 | "```python\n",
768 | "pack = (b, a)\n",
769 | "a = pack[0]\n",
770 | "b = pack[1]\n",
771 | "```"
772 | ]
773 | },
774 | {
775 | "cell_type": "markdown",
776 | "metadata": {},
777 | "source": [
778 | "so be careful with the **assignment order** in complex scenario, for example:\n",
779 | "\n",
780 | "**wrong version**:\n",
781 | "```python\n",
782 | "\"\"\"nums[0] has been changed before usage\"\"\"\n",
783 | "nums[0], nums[nums[0]] = nums[nums[0]], nums[0]\n",
784 | "```\n",
785 | "**right version**:\n",
786 | "```python\n",
787 | "nums[nums[0]], nums[0] = nums[0], nums[nums[0]]\n",
788 | "\n",
789 | "```\n",
790 | "**wrong version**:\n",
791 | "```python\n",
792 | "\"\"\"cur.next has been changed before usage\"\"\"\n",
793 | "cur.next, cur.next.next.next, cur.next.next = cur.next.next, cur.next, cur.next.next.next\n",
794 | "```\n",
795 | "**right version**:\n",
796 | "```python\n",
797 | "cur.next.next.next, cur.next.next, cur.next = cur.next, cur.next.next.next, cur.next.next\n",
798 | "```"
799 | ]
800 | },
801 | {
802 | "cell_type": "markdown",
803 | "metadata": {},
804 | "source": [
805 | "## Mirror Index "
806 | ]
807 | },
808 | {
809 | "cell_type": "markdown",
810 | "metadata": {},
811 | "source": [
812 | "If you are confused about negative index, and looking for another way to index backwards more understandably, you can try mirror index ~, it is a mirror of forward. It starts from right-most with ~0, which is more unified."
813 | ]
814 | },
815 | {
816 | "cell_type": "markdown",
817 | "metadata": {},
818 | "source": [
819 | "`~` actually is a math trick of inverse code and complement code, and it is more easy-understanding in some situations. \n",
820 | "refer to my answer in https://stackoverflow.com/q/55684960/11263560"
821 | ]
822 | },
823 | {
824 | "cell_type": "code",
825 | "execution_count": 17,
826 | "metadata": {},
827 | "outputs": [
828 | {
829 | "data": {
830 | "text/plain": [
831 | "('d', 'c')"
832 | ]
833 | },
834 | "execution_count": 17,
835 | "metadata": {},
836 | "output_type": "execute_result"
837 | }
838 | ],
839 | "source": [
840 | "\"\"\"index start from right to left\"\"\"\n",
841 | "arr = [\"a\", \"b\", \"c\", \"d\"]\n",
842 | "arr[~0], arr[~1]"
843 | ]
844 | },
845 | {
846 | "cell_type": "markdown",
847 | "metadata": {},
848 | "source": [
849 | "#### Inspirations:\n",
850 | "```python\n",
851 | "\"\"\"swap mirror node\"\"\"\n",
852 | "def reverse(arr: List[int]) -> None:\n",
853 | " for i in range(len(arr) // 2):\n",
854 | " arr[i], arr[~i] = arr[~i], arr[i]\n",
855 | "\n",
856 | "\"\"\"find median in a sort list\"\"\"\n",
857 | "def median(arr: List[float]) -> float:\n",
858 | " mid = len(arr) // 2\n",
859 | " return (arr[mid] + arr[~mid]) / 2\n",
860 | "\n",
861 | "\"\"\"deal with mirror pairs\"\"\"\n",
862 | "# L246: verify the number is strobogrammatic, strobogrammatic number looks the same when rotated 180 degrees\n",
863 | "def is_strobogrammatic(num: str) -> bool:\n",
864 | " return all(num[i] + num[~i] in '696 00 11 88' for i in range(len(num) // 2 + 1))\n",
865 | "```"
866 | ]
867 | },
868 | {
869 | "cell_type": "markdown",
870 | "metadata": {},
871 | "source": [
872 | "## return None "
873 | ]
874 | },
875 | {
876 | "cell_type": "markdown",
877 | "metadata": {},
878 | "source": [
879 | "`return None` equals to `return`, equals to no return at all"
880 | ]
881 | },
882 | {
883 | "cell_type": "markdown",
884 | "metadata": {},
885 | "source": [
886 | "#### Inspiration:\n",
887 | "```python\n",
888 | "\"\"\"no return equals to return None, more concise\"\"\"\n",
889 | "# L226: invert a binary tree\n",
890 | "def invert_tree(root: TreeNode) -> TreeNode:\n",
891 | " if root:\n",
892 | " root.left, root.right = invertTree(root.right), invertTree(root.left)\n",
893 | " return root\n",
894 | "```"
895 | ]
896 | },
897 | {
898 | "cell_type": "markdown",
899 | "metadata": {},
900 | "source": [
901 | "## Slice Assignment "
902 | ]
903 | },
904 | {
905 | "cell_type": "code",
906 | "execution_count": 18,
907 | "metadata": {},
908 | "outputs": [
909 | {
910 | "data": {
911 | "text/plain": [
912 | "[0, 6, 7, 1, 2, 3, 4, 5]"
913 | ]
914 | },
915 | "execution_count": 18,
916 | "metadata": {},
917 | "output_type": "execute_result"
918 | }
919 | ],
920 | "source": [
921 | "arr = [0, 1, 2, 3, 4, 5]\n",
922 | "\n",
923 | "\"\"\"insert\"\"\"\n",
924 | "arr[1:1] = [6, 7]\n",
925 | "arr"
926 | ]
927 | },
928 | {
929 | "cell_type": "code",
930 | "execution_count": 19,
931 | "metadata": {},
932 | "outputs": [
933 | {
934 | "data": {
935 | "text/plain": [
936 | "[0, 1, 2, 3, 4, 5]"
937 | ]
938 | },
939 | "execution_count": 19,
940 | "metadata": {},
941 | "output_type": "execute_result"
942 | }
943 | ],
944 | "source": [
945 | "\"\"\"delete\"\"\"\n",
946 | "arr[1:3] = []\n",
947 | "arr"
948 | ]
949 | },
950 | {
951 | "cell_type": "code",
952 | "execution_count": 20,
953 | "metadata": {},
954 | "outputs": [
955 | {
956 | "data": {
957 | "text/plain": [
958 | "[0, 6, 7, 3, 4, 5]"
959 | ]
960 | },
961 | "execution_count": 20,
962 | "metadata": {},
963 | "output_type": "execute_result"
964 | }
965 | ],
966 | "source": [
967 | "\"\"\"replace\"\"\"\n",
968 | "arr[1:3] = [6, 7]\n",
969 | "arr"
970 | ]
971 | },
972 | {
973 | "cell_type": "code",
974 | "execution_count": 21,
975 | "metadata": {},
976 | "outputs": [
977 | {
978 | "data": {
979 | "text/plain": [
980 | "[0, 6, 7, 3, 2, 2, 2]"
981 | ]
982 | },
983 | "execution_count": 21,
984 | "metadata": {},
985 | "output_type": "execute_result"
986 | }
987 | ],
988 | "source": [
989 | "\"\"\"replace slice with different size\"\"\"\n",
990 | "arr[-2:] = [2] * 3\n",
991 | "arr"
992 | ]
993 | },
994 | {
995 | "cell_type": "code",
996 | "execution_count": 22,
997 | "metadata": {},
998 | "outputs": [
999 | {
1000 | "data": {
1001 | "text/plain": [
1002 | "[1, 2, 3]"
1003 | ]
1004 | },
1005 | "execution_count": 22,
1006 | "metadata": {},
1007 | "output_type": "execute_result"
1008 | }
1009 | ],
1010 | "source": [
1011 | "\"\"\"replace the entire list\"\"\"\n",
1012 | "arr[:] = [1, 2, 3]\n",
1013 | "arr"
1014 | ]
1015 | },
1016 | {
1017 | "cell_type": "markdown",
1018 | "metadata": {},
1019 | "source": [
1020 | "#### Inspiration:\n",
1021 | "```python\n",
1022 | "\"\"\"replace(or batch assignment) by slice assignment\"\"\"\n",
1023 | "# L280: reorder unsort array in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3]...\n",
1024 | "def wiggle_sort(nums: List[int]) -> None:\n",
1025 | " for i in range(len(nums)):\n",
1026 | " nums[i:i+2] = sorted(nums[i:i+2], reverse=i%2)\n",
1027 | "```"
1028 | ]
1029 | },
1030 | {
1031 | "cell_type": "markdown",
1032 | "metadata": {},
1033 | "source": [
1034 | "## Maximum/Minimum Integer "
1035 | ]
1036 | },
1037 | {
1038 | "cell_type": "markdown",
1039 | "metadata": {},
1040 | "source": [
1041 | "If you need something like int32_max, int32_min, use `float('inf')`, `float('-inf')` instead. Thanks to the dynamic typing of Python."
1042 | ]
1043 | },
1044 | {
1045 | "cell_type": "code",
1046 | "execution_count": 23,
1047 | "metadata": {
1048 | "scrolled": true
1049 | },
1050 | "outputs": [
1051 | {
1052 | "data": {
1053 | "text/plain": [
1054 | "(0, 9)"
1055 | ]
1056 | },
1057 | "execution_count": 23,
1058 | "metadata": {},
1059 | "output_type": "execute_result"
1060 | }
1061 | ],
1062 | "source": [
1063 | "\"\"\"float('inf'), float('-inf') as the initial value of min_val and max_val\"\"\"\n",
1064 | "min_val, max_val = float('inf'), float('-inf')\n",
1065 | "\n",
1066 | "for i in range(10):\n",
1067 | " min_val = min(min_val, i)\n",
1068 | " max_val = max(max_val, i)\n",
1069 | "(min_val, max_val)"
1070 | ]
1071 | },
1072 | {
1073 | "cell_type": "markdown",
1074 | "metadata": {},
1075 | "source": [
1076 | "## for else / while else "
1077 | ]
1078 | },
1079 | {
1080 | "cell_type": "markdown",
1081 | "metadata": {},
1082 | "source": [
1083 | "Used when you care about both `break` and `non-break` logics. In this way, we can omit the tracking variable."
1084 | ]
1085 | },
1086 | {
1087 | "cell_type": "markdown",
1088 | "metadata": {},
1089 | "source": [
1090 | "```python\n",
1091 | "found_obj = None\n",
1092 | "for obj in objects:\n",
1093 | " if obj.key == search_key:\n",
1094 | " found_obj = obj\n",
1095 | " break\n",
1096 | "else:\t# no break\n",
1097 | " print('no object found')\n",
1098 | "```"
1099 | ]
1100 | },
1101 | {
1102 | "cell_type": "markdown",
1103 | "metadata": {},
1104 | "source": [
1105 | "## Emulate switch "
1106 | ]
1107 | },
1108 | {
1109 | "cell_type": "markdown",
1110 | "metadata": {},
1111 | "source": [
1112 | "Use dictionary mapping (associative array) with lambda:"
1113 | ]
1114 | },
1115 | {
1116 | "cell_type": "code",
1117 | "execution_count": 24,
1118 | "metadata": {
1119 | "scrolled": false
1120 | },
1121 | "outputs": [
1122 | {
1123 | "data": {
1124 | "text/plain": [
1125 | "6"
1126 | ]
1127 | },
1128 | "execution_count": 24,
1129 | "metadata": {},
1130 | "output_type": "execute_result"
1131 | }
1132 | ],
1133 | "source": [
1134 | "\"\"\"emulate switch/case with dict mapping\"\"\"\n",
1135 | "def op_dict(operator: str, x: float, y: float) -> float:\n",
1136 | " return {\n",
1137 | " '+': lambda: x + y,\n",
1138 | " '-': lambda: x - y,\n",
1139 | " '*': lambda: x * y,\n",
1140 | " '/': lambda: x / y,\n",
1141 | " }.get(operator, lambda: None)()\n",
1142 | "\n",
1143 | "op_dict('*', 2, 3)"
1144 | ]
1145 | },
1146 | {
1147 | "cell_type": "markdown",
1148 | "metadata": {},
1149 | "source": [
1150 | "## Decorator "
1151 | ]
1152 | },
1153 | {
1154 | "cell_type": "markdown",
1155 | "metadata": {},
1156 | "source": [
1157 | "Use decorator technique to decorate the Fibonacci function. Enhance it with memoization."
1158 | ]
1159 | },
1160 | {
1161 | "cell_type": "code",
1162 | "execution_count": 25,
1163 | "metadata": {},
1164 | "outputs": [
1165 | {
1166 | "data": {
1167 | "text/plain": [
1168 | "55"
1169 | ]
1170 | },
1171 | "execution_count": 25,
1172 | "metadata": {},
1173 | "output_type": "execute_result"
1174 | }
1175 | ],
1176 | "source": [
1177 | "from functools import wraps\n",
1178 | "def memoization(func):\n",
1179 | " cache = {}\n",
1180 | " miss = object()\n",
1181 | " \n",
1182 | " @wraps(func)\n",
1183 | " def wrapper(*args):\n",
1184 | " result = cache.get(args, miss)\n",
1185 | " if result is miss:\n",
1186 | " result = func(*args)\n",
1187 | " cache[args] = result\n",
1188 | " return result\n",
1189 | " \n",
1190 | " return wrapper\n",
1191 | " \n",
1192 | "@memoization\n",
1193 | "def fib(n):\n",
1194 | " if n < 2:\n",
1195 | " return n\n",
1196 | " return fib(n - 1) + fib(n - 2)\n",
1197 | "\n",
1198 | "fib(10)"
1199 | ]
1200 | },
1201 | {
1202 | "cell_type": "markdown",
1203 | "metadata": {},
1204 | "source": [
1205 | "## Modify While Iteration "
1206 | ]
1207 | },
1208 | {
1209 | "cell_type": "markdown",
1210 | "metadata": {},
1211 | "source": [
1212 | "instead of `queue` in [bfs](https://en.wikipedia.org/wiki/Breadth-first_search), more concise, but use more memory. for the scenario you need the whole path, it is a better way."
1213 | ]
1214 | },
1215 | {
1216 | "cell_type": "markdown",
1217 | "metadata": {},
1218 | "source": [
1219 | "#### Inspiration:\n",
1220 | "```python\n",
1221 | "\"\"\"bfs with list, append while iteration, if you need whole path, it is better here.\"\"\"\n",
1222 | "# L582: given n processes, each process has a unique PID (process id) and its PPID (parent process id)\n",
1223 | "# kill represents a process you want to kill, return a list of PIDs of processes that will be killed in the end\n",
1224 | "def kill_process(pid: List[int], ppid: List[int], kill: int) -> List[int]:\n",
1225 | " d = defaultdict(list)\n",
1226 | " for c, p in zip(pid, ppid):\n",
1227 | " d[p].append(c)\n",
1228 | " bfs = [kill]\n",
1229 | " for i in bfs:\n",
1230 | " bfs += d[i]\n",
1231 | " return bfs\n",
1232 | "```"
1233 | ]
1234 | },
1235 | {
1236 | "cell_type": "markdown",
1237 | "metadata": {},
1238 | "source": [
1239 | "***"
1240 | ]
1241 | }
1242 | ],
1243 | "metadata": {
1244 | "kernelspec": {
1245 | "display_name": "Python 3",
1246 | "language": "python",
1247 | "name": "python3"
1248 | },
1249 | "language_info": {
1250 | "codemirror_mode": {
1251 | "name": "ipython",
1252 | "version": 3
1253 | },
1254 | "file_extension": ".py",
1255 | "mimetype": "text/x-python",
1256 | "name": "python",
1257 | "nbconvert_exporter": "python",
1258 | "pygments_lexer": "ipython3",
1259 | "version": "3.5.5"
1260 | }
1261 | },
1262 | "nbformat": 4,
1263 | "nbformat_minor": 2
1264 | }
1265 |
--------------------------------------------------------------------------------
/black_magics.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 🐍Python Tricks - Black Magics"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "## Table of Content:\n",
15 | "* [EAFP](#anchor1)\n",
16 | "* [self assist](#anchor2)\n",
17 | "* [eval](#anchor3)\n",
18 | "* [str.find](#anchor4)\n",
19 | "* [str.replace](#anchor5)\n",
20 | "* [^ (xor)](#anchor6)\n",
21 | "* [Sentinel](#anchor7)\n",
22 | "* [+= ,](#anchor8)\n",
23 | "* [List Comprehension with Break](#anchor9)\n",
24 | "* [Integrated by Fractions](#anchor10)\n",
25 | "* [Complex Number in Matrix](#anchor11)"
26 | ]
27 | },
28 | {
29 | "cell_type": "markdown",
30 | "metadata": {},
31 | "source": [
32 | "***"
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {},
38 | "source": [
39 | "## EAFP "
40 | ]
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {},
45 | "source": [
46 | "use [**EAFP**](https://docs.python.org/2/glossary.html#term-eafp)(easier to ask for forgiveness than permission) against [**LBYL**](https://docs.python.org/2/glossary.html#term-lbyl) (look before you leap), actually this is the Pythonic design philosophy."
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "```python\n",
54 | "\"\"\"LBYL\"\"\"\n",
55 | "if \"key\" in dict_:\n",
56 | " value += dict_[\"key\"]\n",
57 | " \n",
58 | "\"\"\"EAFP\"\"\"\n",
59 | "# downplays the importance of the key\n",
60 | "try:\n",
61 | " value += dict_[\"key\"]\n",
62 | "except KeyError:\n",
63 | " pass\n",
64 | "```"
65 | ]
66 | },
67 | {
68 | "cell_type": "markdown",
69 | "metadata": {},
70 | "source": [
71 | "Let's write a Fibonacci example in a pythonic-style. We will provide the result in generator way and iterate in EAFP style."
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": 1,
77 | "metadata": {},
78 | "outputs": [
79 | {
80 | "name": "stdout",
81 | "output_type": "stream",
82 | "text": [
83 | "0 1 1 2 3 5 "
84 | ]
85 | }
86 | ],
87 | "source": [
88 | "\"\"\"pythonic fibonacci\"\"\"\n",
89 | "#generator function\n",
90 | "def fibonacci(n):\n",
91 | " a, b, counter = 0, 1, 0\n",
92 | " while counter <= n:\n",
93 | " yield a\n",
94 | " a, b = b, a + b\n",
95 | " counter += 1\n",
96 | " \n",
97 | "f = fibonacci(5) #f is iterator object\n",
98 | "while True:\n",
99 | " try:\n",
100 | " print (next(f), end=\" \")\n",
101 | " except StopIteration:\n",
102 | " break"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "#### Inspirations:\n",
110 | "```python\n",
111 | "\"\"\"used in detect cycle in linked list\"\"\"\n",
112 | "# L141: determine there is a cycle in a linked list\n",
113 | "def has_cycle_in_linked_list(head: ListNode) -> bool:\n",
114 | " try:\n",
115 | " slow = head\n",
116 | " fast = head.next\n",
117 | " while slow is not fast:\n",
118 | " slow = slow.next\n",
119 | " fast = fast.next.next\n",
120 | " return True\n",
121 | " except:\n",
122 | " return False\n",
123 | "\n",
124 | "\"\"\"used in binary search and insert\"\"\"\n",
125 | "# L334: given an unsorted array return whether an increasing subsequence (incontinuous) of length k exists or not in the array.\n",
126 | "def increasing_triplet(nums: List[int], k: int) -> bool:\n",
127 | " try:\n",
128 | " inc = [float('inf')] * (k - 1)\n",
129 | " for x in nums:\n",
130 | " inc[bisect.bisect_left(inc, x)] = x\n",
131 | " return k == 0\n",
132 | " except:\n",
133 | " return True\n",
134 | "\n",
135 | "\"\"\"used in eval\"\"\"\n",
136 | "# L301: Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.\n",
137 | "def remove_invalid_parentheses(s: str) -> List[str]:\n",
138 | " def isvalid(s):\n",
139 | " try:\n",
140 | " eval('0,' + ''.join(filter('()'.count, s)).replace(')', '),'))\n",
141 | " return True\n",
142 | " except:\n",
143 | " pass\n",
144 | " level = {s}\n",
145 | " while True:\n",
146 | " valid = list(filter(isvalid, level))\n",
147 | " if valid:\n",
148 | " return valid\n",
149 | " level = {s[:i] + s[i+1:] for s in level for i in range(len(s))}\n",
150 | "```"
151 | ]
152 | },
153 | {
154 | "cell_type": "markdown",
155 | "metadata": {},
156 | "source": [
157 | "## self assist "
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "metadata": {},
163 | "source": [
164 | "a trick of dynamic property, but a bit intrusive"
165 | ]
166 | },
167 | {
168 | "cell_type": "markdown",
169 | "metadata": {},
170 | "source": [
171 | "#### Inspiration:\n",
172 | "```python\n",
173 | "\"\"\"self as dummy node in linked list\"\"\"\n",
174 | "def traverse_linked_list(self, head: TreeNode) -> TreeNode:\n",
175 | " pre, pre.next = self, head\n",
176 | " while pre.next:\n",
177 | " self.process_logic(pre.next)\n",
178 | " return self.next # return head node\n",
179 | "```"
180 | ]
181 | },
182 | {
183 | "cell_type": "markdown",
184 | "metadata": {},
185 | "source": [
186 | "## eval \n",
187 | "eval() executes arbitrary strings as Python code. But note that eval may become a potential security risk."
188 | ]
189 | },
190 | {
191 | "cell_type": "markdown",
192 | "metadata": {},
193 | "source": [
194 | "#### Inspiration:\n",
195 | "```python\n",
196 | "\"\"\"build a tree constructor and use eval to execute\"\"\"\n",
197 | "# L536: construct binary tree from string\n",
198 | "def str2tree(s: str) -> TreeNode:\n",
199 | " def t(val, left=None, right=None):\n",
200 | " node, node.left, node.right = TreeNode(val), left, right\n",
201 | " return node\n",
202 | " return eval('t(' + s.replace('(', ',t(') + ')') if s else None\n",
203 | "```"
204 | ]
205 | },
206 | {
207 | "cell_type": "markdown",
208 | "metadata": {},
209 | "source": [
210 | "## str.find \n",
211 | "This is clever use of str.find. Although this usage is not very generalizable, the idea can be used for reference."
212 | ]
213 | },
214 | {
215 | "cell_type": "markdown",
216 | "metadata": {},
217 | "source": [
218 | "#### Inspiration:\n",
219 | "```python\n",
220 | "\"\"\"use find to construct a mechanism: if val is # return 1, if not return -1\"\"\"\n",
221 | "# L331: verify preorder serialization of a binary tree, if it is a null node, we record using a sentinel value #\n",
222 | "def is_valid_serialization(preorder: str) -> bool:\n",
223 | " need = 1\n",
224 | " for val in preorder.split(','):\n",
225 | " if not need:\n",
226 | " return False\n",
227 | " need -= ' #'.find(val)\n",
228 | " return not need\n",
229 | "```"
230 | ]
231 | },
232 | {
233 | "cell_type": "markdown",
234 | "metadata": {},
235 | "source": [
236 | "## str.replace "
237 | ]
238 | },
239 | {
240 | "cell_type": "markdown",
241 | "metadata": {},
242 | "source": [
243 | "Use str.replace to implement a string version's union-find (disjoint-set)."
244 | ]
245 | },
246 | {
247 | "cell_type": "markdown",
248 | "metadata": {},
249 | "source": [
250 | "#### Inspiration:\n",
251 | "```python\n",
252 | "\"\"\"convert int to unicode char, and use str.replace to merge the connected nodes by reduce\"\"\"\n",
253 | "# L323: given n nodes from 0 to n - 1 and a list of undirected edges, find the number of connected components in an undirected graph.\n",
254 | "def count_connected_components(n: int, edges: List[List[int]]) -> int:\n",
255 | " return len(set(reduce(lambda s, edge: s.replace(s[edge[1]], s[edge[0]]), edges, ''.join(map(chr, range(n))))))\n",
256 | "```"
257 | ]
258 | },
259 | {
260 | "cell_type": "markdown",
261 | "metadata": {},
262 | "source": [
263 | "## ^ (xor) \n",
264 | "^ is usually used for removing even exactly same numbers and save the odd, or save the distinct bits and remove the same."
265 | ]
266 | },
267 | {
268 | "cell_type": "markdown",
269 | "metadata": {},
270 | "source": [
271 | "#### Inspirations:\n",
272 | "```python\n",
273 | "\"\"\"Use ^ to remove even exactly same numbers and save the odd, or save the distinct bits and remove the same.\"\"\"\n",
274 | "\"\"\"bit manipulate: a^b^b = a\"\"\"\n",
275 | "# L268: Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array.\n",
276 | "def missing_number(nums: List[int]) -> int:\n",
277 | " res = 0\n",
278 | " for i, e in enumerate(nums):\n",
279 | " res = res ^ i ^ e\n",
280 | " return res ^ len(nums)\n",
281 | "\n",
282 | "\"\"\"simply find the first index whose \"partner index\" (the index xor 1) holds a different value.\"\"\"\n",
283 | "# L540: find the single element, in a sorted array where every element appears twice except for one. \n",
284 | "def single_non_duplicate(nums: List[int]) -> int:\n",
285 | " lo, hi = 0, len(nums) - 1\n",
286 | " while lo < hi:\n",
287 | " mid = (lo + hi) // 2\n",
288 | " if nums[mid] == nums[mid ^ 1]:\n",
289 | " lo = mid + 1\n",
290 | " else:\n",
291 | " hi = mid\n",
292 | " return nums[lo]\n",
293 | "\n",
294 | "\"\"\"parity in triple comparisons\"\"\"\n",
295 | "# L81: search a target in a rotated sorted array\n",
296 | "def search_in_rotated_sorted_arr(nums: List[int], target: int) -> int:\n",
297 | " \"\"\"I have three checks (nums[0] <= target), (target <= nums[i]) and (nums[i] < nums[0]), and I want to know\n",
298 | " whether exactly two of them are true. They can't all be true or all be false (check it), so I just need to\n",
299 | " distinguish between \"two true\" and \"one true\". Parity is enough for that, so instead of adding them I xor them\"\"\"\n",
300 | " self.__getitem__ = lambda i: (nums[0] <= target) ^ (nums[0] > nums[i]) ^ (target > nums[i])\n",
301 | " i = bisect.bisect_left(self, True, 0, len(nums))\n",
302 | " return i if target in nums[i:i+1] else -1\n",
303 | "```"
304 | ]
305 | },
306 | {
307 | "cell_type": "markdown",
308 | "metadata": {},
309 | "source": [
310 | "## Sentinel "
311 | ]
312 | },
313 | {
314 | "cell_type": "markdown",
315 | "metadata": {},
316 | "source": [
317 | "Sentinel can make the program simpler: a mechanism to distinguish useful data from placeholders which indicate data is absent.\n",
318 | "\n",
319 | "For example, we can put the key we search after the end of the array, this ensures that we will eventually find the element. When we find it, we only have to check whether we found a real element or just the sentinel.\n",
320 | "\n",
321 | "Here we compare two similar built-in functions: str.index without sentinel and str.find with sentinel."
322 | ]
323 | },
324 | {
325 | "cell_type": "markdown",
326 | "metadata": {},
327 | "source": [
328 | "#### Sentinel in Python:\n",
329 | "```python\n",
330 | "\"\"\"find vs index\"\"\" \n",
331 | "\"\"\"index without sentinel\"\"\"\n",
332 | "try:\n",
333 | " i = a.index(b)\n",
334 | "except:\n",
335 | " return\n",
336 | "\n",
337 | "\"\"\"index with sentinel\"\"\"\n",
338 | "i = a.find(b)\n",
339 | "if i == -1:\n",
340 | " return\n",
341 | "\n",
342 | "\"\"\"sentinel in dict.get\"\"\"\n",
343 | "sentinel = object()\n",
344 | "value = my_dict.get(key, sentinel)\n",
345 | "if value is not sentinel:\n",
346 | " # Do something with value\n",
347 | "\n",
348 | "\"\"\"sentinel in iter\"\"\"\n",
349 | "blocks = ''.join(iter(partial(f.read, 32), ''))\n",
350 | "```"
351 | ]
352 | },
353 | {
354 | "cell_type": "markdown",
355 | "metadata": {},
356 | "source": [
357 | "#### Inspirations:\n",
358 | "```python \n",
359 | "\"\"\"add a sentinel n at the end (which is the appropriate last insertion index then)\"\"\"\n",
360 | "# L47: given a collection of numbers that might contain duplicates, return all possible unique permutations.\n",
361 | "def permute_unique(nums: List[int]) -> List[List[int]]:\n",
362 | " perms = [[]]\n",
363 | " for n in nums:\n",
364 | " perms = [p[:i] + [n] + p[i:]\n",
365 | " for p in perms\n",
366 | " for i in range((p + [n]).index(n) + 1)]\n",
367 | " return perms\n",
368 | "\n",
369 | "\"\"\"sentinel in matrix\"\"\" \n",
370 | "def traverse_neighbors(matrix: List[List[int]]):\n",
371 | " m, n = len(matrix), len(matrix[0])\n",
372 | " \"\"\"augment matrix to void length check by sentinel\"\"\"\n",
373 | " matrix += [0] * n,\n",
374 | " for row in matrix:\n",
375 | " row.append(0)\n",
376 | " \n",
377 | " for i in range(m):\n",
378 | " for j in range(n):\n",
379 | " # construct neighbor iterator\n",
380 | " for I, J in (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1):\n",
381 | " \"\"\"no need to check boundary\"\"\"\n",
382 | " process_neighbor_logic(matrix[I][J])\n",
383 | "\n",
384 | "\"\"\"functional sentinel\"\"\"\n",
385 | "def get_element(matrix: List[List[int]], i: int, j: int) -> int:\n",
386 | " return matrix[i][j] if 0 <= i < m and 0 <= j < n else -1\n",
387 | "```"
388 | ]
389 | },
390 | {
391 | "cell_type": "markdown",
392 | "metadata": {},
393 | "source": [
394 | "## += , "
395 | ]
396 | },
397 | {
398 | "cell_type": "markdown",
399 | "metadata": {},
400 | "source": [
401 | "less to type and more clear, even faster according to the test. but if not familiar with, may bring confusion."
402 | ]
403 | },
404 | {
405 | "cell_type": "code",
406 | "execution_count": 2,
407 | "metadata": {
408 | "scrolled": true
409 | },
410 | "outputs": [
411 | {
412 | "data": {
413 | "text/plain": [
414 | "[1, 2, 3, 4]"
415 | ]
416 | },
417 | "execution_count": 2,
418 | "metadata": {},
419 | "output_type": "execute_result"
420 | }
421 | ],
422 | "source": [
423 | "\"\"\", means convert to tuple, += element, equals to append(element)\"\"\"\n",
424 | "arr = [1, 2, 3]\n",
425 | "arr += 4,\n",
426 | "arr"
427 | ]
428 | },
429 | {
430 | "cell_type": "markdown",
431 | "metadata": {},
432 | "source": [
433 | "## List Comprehension with Break "
434 | ]
435 | },
436 | {
437 | "cell_type": "markdown",
438 | "metadata": {},
439 | "source": [
440 | "We know we cannot use branch logic (conditional execution) like if/else in list comprehension, so how to simulate break in list comprehension?\n",
441 | "\n",
442 | "Notice it is just a study and exploration of list comprehension. Since this trick is too hackish with poor readability and performance, so it should not be used in production code. But the techniques and ideas can be used for reference and may provide some inspirations.\n",
443 | "\n",
444 | "Let's take this Stack Overflow question as an example:\n",
445 | ">How can I stop the iteration of list comprehension when a particular element is found."
446 | ]
447 | },
448 | {
449 | "cell_type": "markdown",
450 | "metadata": {},
451 | "source": [
452 | "```python\n",
453 | "# pseudo code\n",
454 | "new_list=[a for a in origin_list break if a==break_elment]\n",
455 | "```"
456 | ]
457 | },
458 | {
459 | "cell_type": "markdown",
460 | "metadata": {},
461 | "source": [
462 | "Here is the hackish solution:"
463 | ]
464 | },
465 | {
466 | "cell_type": "code",
467 | "execution_count": 3,
468 | "metadata": {},
469 | "outputs": [
470 | {
471 | "data": {
472 | "text/plain": [
473 | "[1, 2, 3]"
474 | ]
475 | },
476 | "execution_count": 3,
477 | "metadata": {},
478 | "output_type": "execute_result"
479 | }
480 | ],
481 | "source": [
482 | "# https://stackoverflow.com/a/56054962/11263560\n",
483 | "origin_list = [1, 2, 3, 3, 4, 3, 5]\n",
484 | "break_elment = 3\n",
485 | "\n",
486 | "new_list = [a for end in [[]] for a in origin_list\n",
487 | " if not end and not (a == break_elment and end.append(42))]\n",
488 | "new_list"
489 | ]
490 | },
491 | {
492 | "cell_type": "markdown",
493 | "metadata": {},
494 | "source": [
495 | "There are many techniques used in this trick:\n",
496 | "\n",
497 | "1. The key point is building an `end` condition in list comprehension. Skip the rest elements when end is not empty (Actually not break out, but indeed in logic).\n",
498 | "2. How to initialize a variable (`end`) in a list comprehension? Here is a trick to wrap it in a for list: `for end in [[]]`.\n",
499 | "3. How to implement branch logic in a list comprehension? Use lazy evaluation technique in `and`/`or` to divide branch logics.\n"
500 | ]
501 | },
502 | {
503 | "cell_type": "markdown",
504 | "metadata": {},
505 | "source": [
506 | "#### Inspiration:\n",
507 | "```python\n",
508 | "# https://stackoverflow.com/a/55671533/11263560\n",
509 | "# How can I break a list comprehension based on a condition, for instance when the number 412 is found?\n",
510 | "# requirement in pseudo code\n",
511 | "even = [n for n in numbers if 0 == n % 2 and break if n == 412]\n",
512 | "\n",
513 | "\"\"\"use end condition\"\"\"\n",
514 | "even = [n for end in [[]] for n in numbers\n",
515 | " if (False if end or n != 412 else end.append(42))\n",
516 | " or not end and not n % 2]\n",
517 | "\n",
518 | "# https://stackoverflow.com/q/55646039/11263560\n",
519 | "\"\"\"use push & pop to record last pair\"\"\"\n",
520 | "res = [last.pop() and last.append(b) or b for last in [[desired_list[0]]] for a, b in \n",
521 | " zip([desired_list[0]] + desired_list, desired_list) if abs(a[1] - b[1]) <= 5 and a == last[0]]\n",
522 | "\n",
523 | "\"\"\"use end condition\"\"\"\n",
524 | "res = [b for end in [[]] for a, b in zip([desired_list[0]] + desired_list, desired_list) \n",
525 | " if (False if end or abs(a[1] - b[1]) <= 5 else end.append(42)) or not end and abs(a[1] - b[1]) <= 5]\n",
526 | "```"
527 | ]
528 | },
529 | {
530 | "cell_type": "markdown",
531 | "metadata": {},
532 | "source": [
533 | "## Integrated by Fractions "
534 | ]
535 | },
536 | {
537 | "cell_type": "markdown",
538 | "metadata": {},
539 | "source": [
540 | "Integrate several dimensions into one dictionary by the index with fractions."
541 | ]
542 | },
543 | {
544 | "cell_type": "markdown",
545 | "metadata": {},
546 | "source": [
547 | "#### Inspiration:\n",
548 | "```python\n",
549 | "# L562: given a 01 matrix, find the longest line of consecutive one. the line could be horizontal, vertical, diagonal or anti-diagonal.\n",
550 | "def longest_line(matrix: List[List[int]]) -> int:\n",
551 | " max_len = 0\n",
552 | " cur_len = defaultdict(int)\n",
553 | " for i, row in enumerate(matrix):\n",
554 | " for j, v in enumerate(row):\n",
555 | " \"\"\"merge row, col, analog, anti-analog into one dict by fractions\"\"\"\n",
556 | " for key in i, j + .1, i + j + .2, i - j + .3: # analog: i+j, anti-analog: i-j\n",
557 | " cur_len[key] = (cur_len[key] + 1) * v # accumulate util v turn to zero\n",
558 | " max_len = max(max_len, cur_len[key])\n",
559 | " return max_len\n",
560 | "```"
561 | ]
562 | },
563 | {
564 | "cell_type": "markdown",
565 | "metadata": {},
566 | "source": [
567 | "## Complex Number in Matrix "
568 | ]
569 | },
570 | {
571 | "cell_type": "markdown",
572 | "metadata": {},
573 | "source": [
574 | "use complex number in 2d representation, and visit 4-directions by imaginary unit calculation."
575 | ]
576 | },
577 | {
578 | "cell_type": "markdown",
579 | "metadata": {},
580 | "source": [
581 | "#### Inspirations:\n",
582 | "```python\n",
583 | "\"\"\"simplify two-dimension index into one-dimension by complex number\"\"\"\n",
584 | "# traverse neighbors in matrix\n",
585 | "def traverse_neighbor_by_complex(matrix: List[List[int]]) -> None:\n",
586 | " matrix = {i + 1j * j: v for i, row in enumerate(matrix) for j, v in enumerate(row)}\n",
587 | " for z in matrix:\n",
588 | " \"\"\"visit 4-directional neighbor by complex calculation\"\"\"\n",
589 | " for k in range(4):\n",
590 | " process_neighbor_logic(matrix.get(z + 1j ** k))\n",
591 | " \n",
592 | "# L657: given a sequence of robot 4-directional moves \"LRUD\", judge if this robot ends up at (0, 0) after it completes its moves.\n",
593 | "def judge_circle(moves: str) -> bool:\n",
594 | " \"\"\"D: 1j**-1=-1j, R: 1j**0=1+0j, U: 1j**1=1j, L: 1j**2=-1+0j, result in D+U=0 and L+R=0\"\"\"\n",
595 | " return not sum(1j**'RUL'.find(m) for m in moves)\n",
596 | "\n",
597 | "\"\"\"use complex number to represent island(simplify 2d -> 1d and turn into a sparse representation)\"\"\"\n",
598 | "# L695: an island is a group of 1's connected 4-directionally, find the maximum area of an island in the given 2D array.\n",
599 | "def max_area_of_island(grid: List[List[int]]) -> int:\n",
600 | " grid = {i + j * 1j: val for i, row in enumerate(grid) for j, val in enumerate(row)}\n",
601 | " \"\"\"calculate the area of paricular island by visit neigher complex calculat\"\"\"\n",
602 | " def area(z):\n",
603 | " return grid.pop(z, 0) and 1 + sum(area(z + 1j ** k) for k in range(4))\n",
604 | " return max(map(area, set(grid)))\n",
605 | "```"
606 | ]
607 | },
608 | {
609 | "cell_type": "markdown",
610 | "metadata": {},
611 | "source": [
612 | "***"
613 | ]
614 | }
615 | ],
616 | "metadata": {
617 | "kernelspec": {
618 | "display_name": "Python 3",
619 | "language": "python",
620 | "name": "python3"
621 | },
622 | "language_info": {
623 | "codemirror_mode": {
624 | "name": "ipython",
625 | "version": 3
626 | },
627 | "file_extension": ".py",
628 | "mimetype": "text/x-python",
629 | "name": "python",
630 | "nbconvert_exporter": "python",
631 | "pygments_lexer": "ipython3",
632 | "version": "3.5.5"
633 | }
634 | },
635 | "nbformat": 4,
636 | "nbformat_minor": 2
637 | }
638 |
--------------------------------------------------------------------------------
/iterables.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# 🐍Python Tricks - Iterables"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "This notebook is the practical part of Pluralsight Guides: [Python Tricks - Iterable - Part 1](https://www.pluralsight.com/guides/python-tricks-iterable-part-1) and [Python Tricks - Iterable - Part 2](https://www.pluralsight.com/guides/python-tricks-iterable-part-2)"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "## Table of Content:\n",
22 | "* [Construct Iterator](#anchor1)\n",
23 | "* [Construct Generator](#anchor2)\n",
24 | "* [Comprehension](#anchor3)\n",
25 | "* [lambda](#anchor4)\n",
26 | "* [map](#anchor5)\n",
27 | "* [reduce](#anchor6)\n",
28 | "* [zip](#anchor7)\n",
29 | "* [Unpacking](#anchor8)\n",
30 | "* [sum](#anchor9)\n",
31 | "* [max & min](#anchor10)\n",
32 | "* [any & all](#anchor11)\n",
33 | "* [Customize iterable in bisect](#anchor12)"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "***"
41 | ]
42 | },
43 | {
44 | "cell_type": "markdown",
45 | "metadata": {},
46 | "source": [
47 | "## Construct Iterator "
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "metadata": {},
53 | "source": [
54 | "#### Inspiration:\n",
55 | "```python\n",
56 | "\"\"\"construct iterator to make sure check every char only once by 'in' \"\"\"\n",
57 | "# L392: check if s is subsequence of t. s = \"abc\", t = \"ahbgdc\", return true.\n",
58 | "def is_subsequence(s: str, t: str) -> bool:\n",
59 | " t = iter(t)\n",
60 | " return all(c in t for c in s)\n",
61 | "```"
62 | ]
63 | },
64 | {
65 | "cell_type": "markdown",
66 | "metadata": {},
67 | "source": [
68 | "## Construct Generator "
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "metadata": {},
74 | "source": [
75 | "`generator` is a kind of iterable you can only iterate over once. generators do not store all the values in memory, they generate the values on the fly."
76 | ]
77 | },
78 | {
79 | "cell_type": "markdown",
80 | "metadata": {},
81 | "source": [
82 | "#### Inspirations:\n",
83 | "```python\n",
84 | "\"\"\"generate all combinations of coins which sum to target amount\"\"\"\n",
85 | "def get_coin_changes(coins: List[int], amount[int]) -> List[List[int]]:\n",
86 | " for i, coin in enumerate(coins):\n",
87 | " if coin == amount:\n",
88 | " yield (coin,)\n",
89 | " elif coin < amount:\n",
90 | " yield from ((coin,) + x for x in change(coins[i:], amount - coin))\n",
91 | "\n",
92 | "\"\"\"generate all combinations of well-formed n pairs of parentheses\"\"\"\n",
93 | "# L22: generate all combinations of well-formed n pairs of parentheses.\n",
94 | "def generate_parenthesis(n: int) -> List[str]:\n",
95 | " def generate(p, left, right):\n",
96 | " if 0 <= left <= right:\n",
97 | " if not right:\n",
98 | " yield p\n",
99 | " for q in generate(p + '(', left-1, right): yield q\n",
100 | " for q in generate(p + ')', left, right-1): yield q\n",
101 | " return list(generate('', n, n))\n",
102 | "```"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "## Comprehension "
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": 1,
115 | "metadata": {},
116 | "outputs": [],
117 | "source": [
118 | "\"\"\"list comprehension\"\"\"\n",
119 | "odds_list = [x for x in range(20) if x % 2]\n",
120 | "\n",
121 | "\"\"\"generator_expression\"\"\"\n",
122 | "odds_generator = (x for x in range(20) if x % 2)\n",
123 | "\n",
124 | "\"\"\"set comprehension\"\"\"\n",
125 | "odds_set = {x for x in range(20) if x % 2}\n",
126 | "\n",
127 | "\"\"\"dict comprehension\"\"\"\n",
128 | "square_dict = {x: x**2 for x in range(10)}"
129 | ]
130 | },
131 | {
132 | "cell_type": "markdown",
133 | "metadata": {},
134 | "source": [
135 | "#### Inspirations:\n",
136 | "```python\n",
137 | "\"\"\"bfs in list comprehension way\"\"\"\n",
138 | "# L515: find the largest value in each row of a binary tree\n",
139 | "def find_largest_in_binary_tree_rows(root: TreeNode) -> List[int]:\n",
140 | " maxes, row = [], [root]\n",
141 | " while any(row):\n",
142 | " maxes.append(max(node.val for node in row))\n",
143 | " row = [kid for node in row for kid in (node.left, node.right) if kid]\n",
144 | " return maxes\n",
145 | "\n",
146 | "\"\"\"rotate image by list comprehension\"\"\"\n",
147 | "# L48: rotate the image by 90 degrees (clockwise)\n",
148 | "def rotate_image_90_degrees(matrix: List[List[int]]) -> None:\n",
149 | " matrix[:] = [[row[i] for row in matrix[::-1]] for i in range(len(A))]\n",
150 | "\n",
151 | "\"\"\"generate combinations by list comprehension recursively\"\"\"\n",
152 | "# L77: Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.\n",
153 | "def combine(n: int, k: int) -> List[List[int]]:\n",
154 | " if k == 0:\n",
155 | " return [[]]\n",
156 | " # add last element\n",
157 | " return [pre + [i] for i in range(k, n + 1) for pre in combine(i - 1, k - 1)]\n",
158 | "\n",
159 | "\"\"\"generate binary trees by list comprehension recursively\"\"\"\n",
160 | "# given an integer n, generate all structurally unique BST's (binary search trees) that store values 1 ... n.\n",
161 | "def generate_binary_search_trees(last: int, first: int=1) -> List[TreeNode]:\n",
162 | " def node(val, left, right):\n",
163 | " node = TreeNode(val)\n",
164 | " node.left = left\n",
165 | " node.right = right\n",
166 | " return node\n",
167 | " return [node(root, left, right) \n",
168 | " for root in range(first, last+1)\n",
169 | " for left in generate_binary_search_trees(root-1, first)\n",
170 | " for right in generate_binary_search_trees(last, root+1)] or [None]\n",
171 | "```"
172 | ]
173 | },
174 | {
175 | "cell_type": "markdown",
176 | "metadata": {},
177 | "source": [
178 | "## lambda \n",
179 | "single expression with implicit return"
180 | ]
181 | },
182 | {
183 | "cell_type": "markdown",
184 | "metadata": {},
185 | "source": [
186 | "```python\n",
187 | "lambda arguments : expression\n",
188 | "```"
189 | ]
190 | },
191 | {
192 | "cell_type": "code",
193 | "execution_count": 2,
194 | "metadata": {},
195 | "outputs": [
196 | {
197 | "data": {
198 | "text/plain": [
199 | "7"
200 | ]
201 | },
202 | "execution_count": 2,
203 | "metadata": {},
204 | "output_type": "execute_result"
205 | }
206 | ],
207 | "source": [
208 | "\"\"\"lexical closure\"\"\"\n",
209 | "def make_adder(n):\n",
210 | " return lambda x: x + n\n",
211 | "\n",
212 | "plus_3 = make_adder(3)\n",
213 | "plus_3(4)"
214 | ]
215 | },
216 | {
217 | "cell_type": "code",
218 | "execution_count": 3,
219 | "metadata": {
220 | "scrolled": true
221 | },
222 | "outputs": [
223 | {
224 | "data": {
225 | "text/plain": [
226 | "[('rotten tomato', 1), ('banana', 5), ('apple', 18), ('orange', 20)]"
227 | ]
228 | },
229 | "execution_count": 3,
230 | "metadata": {},
231 | "output_type": "execute_result"
232 | }
233 | ],
234 | "source": [
235 | "\"\"\"lambda used in key\"\"\"\n",
236 | "# sort dict by value\n",
237 | "d = {'apple': 18, 'orange': 20, 'banana': 5, 'rotten tomato': 1}\n",
238 | "sorted(d.items(), key=lambda x: x[1])\n",
239 | "\n",
240 | "# alternative\n",
241 | "from operator import itemgetter\n",
242 | "sorted(d.items(), key=itemgetter(1))"
243 | ]
244 | },
245 | {
246 | "cell_type": "markdown",
247 | "metadata": {},
248 | "source": [
249 | "#### Inspirations:\n",
250 | "```python\n",
251 | "for pos in range(len(S)):\n",
252 | " \"\"\"lambda used in filter\"\"\"\n",
253 | " matched_words = list(filter(lambda x: S[pos:].startswith(x), words))\n",
254 | " if matched_words:\n",
255 | " \"\"\"lambda used in max\"\"\"\n",
256 | " add_interval([pos, pos + len(max(matched_words, key=lambda x: len(x)))])\n",
257 | "\n",
258 | "\"\"\"call lambda in nested\"\"\"\n",
259 | "# L422: given a sequence of words, check whether it forms a valid word square.\n",
260 | "def valid_word_square(words: List[str]) -> bool:\n",
261 | " f = lambda x: map(None, *x) # python 2\n",
262 | " return f(f(words)) == f(words)\n",
263 | "\n",
264 | "\"\"\"use lambda to wrap parameter as callable\"\"\"\n",
265 | "def wrapped_callable(f):\n",
266 | " if callable(f): return f\n",
267 | " else: return lambda: f\n",
268 | "```"
269 | ]
270 | },
271 | {
272 | "cell_type": "markdown",
273 | "metadata": {},
274 | "source": [
275 | "## map \n",
276 | "`map` applies a function to all the items in a given iterable"
277 | ]
278 | },
279 | {
280 | "cell_type": "markdown",
281 | "metadata": {},
282 | "source": [
283 | "```python\n",
284 | "map(function, iterables) -> iterables\n",
285 | "```"
286 | ]
287 | },
288 | {
289 | "cell_type": "markdown",
290 | "metadata": {},
291 | "source": [
292 | "#### Inspirations:\n",
293 | "```python\n",
294 | "\"\"\"map to node children recursively\"\"\"\n",
295 | "# get depth in binary tree\n",
296 | "def max_depth(root: TreeNode) -> int:\n",
297 | " return 1 + max(map(maxDepth, (root.left, root.right))) if root else 0\n",
298 | "\n",
299 | "\"\"\"comparison map find with map index\"\"\"\n",
300 | "# given a pattern and a string words, find if words follows the same pattern. \n",
301 | "# i.e. pattern = 'abba', words = 'dog cat cat dog'\n",
302 | "def valid_word_pattern(pattern: str, words: str) -> bool:\n",
303 | " s, t = pattern, words.split()\n",
304 | " return list(map(s.find, s)) == list(map(t.index, t))\n",
305 | "\n",
306 | "\"\"\"map dict.get\"\"\"\n",
307 | "# given scores of N athletes, find their relative ranks and the people with the top three highest scores, who will be awarded medals: \"Gold Medal\", \"Silver Medal\" and \"Bronze Medal\"\n",
308 | "def find_relative_ranks(nums: List[int]) -> List[str]:\n",
309 | " sorted_nums = sorted(nums, reverse=True)\n",
310 | " rank = [\"Gold Medal\", \"Silver Medal\", \"Bronze Medal\"] + list(map(str, range(4, len(nums) + 1)))\n",
311 | " return list(map(dict(zip(sorted_nums, rank)).get, nums))\n",
312 | "```"
313 | ]
314 | },
315 | {
316 | "cell_type": "markdown",
317 | "metadata": {},
318 | "source": [
319 | "## reduce "
320 | ]
321 | },
322 | {
323 | "cell_type": "markdown",
324 | "metadata": {},
325 | "source": [
326 | "`reduce` returns a single value constructed by function on the first two items of the sequence, then on the result and the next item, and so on."
327 | ]
328 | },
329 | {
330 | "cell_type": "markdown",
331 | "metadata": {},
332 | "source": [
333 | "```python\n",
334 | "reduce(function, sequence[, initial]) -> value\n",
335 | "```"
336 | ]
337 | },
338 | {
339 | "cell_type": "markdown",
340 | "metadata": {},
341 | "source": [
342 | "#### Inspirations:\n",
343 | "```python\n",
344 | "# every numbers appears twice except for one, find that single one\n",
345 | "def single_number(nums: List[int]) -> int:\n",
346 | " return reduce(lambda x, y: x ^ y, nums)\n",
347 | "\n",
348 | "# generate all possible permutations\n",
349 | "def permute(nums: List[int]) -> List[List[int]]:\n",
350 | " return reduce(lambda P, n: [p[:i] + [n] + p[i:] for p in P for i in range(len(p)+1)], \n",
351 | " nums, [[]])\n",
352 | "```"
353 | ]
354 | },
355 | {
356 | "cell_type": "markdown",
357 | "metadata": {},
358 | "source": [
359 | "## zip "
360 | ]
361 | },
362 | {
363 | "cell_type": "markdown",
364 | "metadata": {},
365 | "source": [
366 | "`zip` takes iterator that aggregates elements based on the iterables passed, and returns an iterator of tuples."
367 | ]
368 | },
369 | {
370 | "cell_type": "markdown",
371 | "metadata": {},
372 | "source": [
373 | "```python\n",
374 | "zip(*iterables) -> iterator\n",
375 | "```"
376 | ]
377 | },
378 | {
379 | "cell_type": "markdown",
380 | "metadata": {},
381 | "source": [
382 | "Notice iterator stops when shortest iterable is exhausted. If you prefer to match the longest iterable, you can try `zip_longest(*iterables, fillvalue=None)` in `itertools` module."
383 | ]
384 | },
385 | {
386 | "cell_type": "code",
387 | "execution_count": 4,
388 | "metadata": {},
389 | "outputs": [
390 | {
391 | "data": {
392 | "text/plain": [
393 | "[(1, 'one'), (2, 'two'), (3, 'three')]"
394 | ]
395 | },
396 | "execution_count": 4,
397 | "metadata": {},
398 | "output_type": "execute_result"
399 | }
400 | ],
401 | "source": [
402 | "a = [1,2,3]\n",
403 | "b = ['one','two','three']\n",
404 | "zipped=zip(a,b)\n",
405 | "list(zipped)"
406 | ]
407 | },
408 | {
409 | "cell_type": "markdown",
410 | "metadata": {},
411 | "source": [
412 | "#### Inspirations:\n",
413 | "```python\n",
414 | "\"\"\"construct dict\"\"\"\n",
415 | "keys = ['a', 'b', 'c']\n",
416 | "vals = [1, 2, 3]\n",
417 | "zipped_dict = dict(zip(keys, vals))\n",
418 | "\n",
419 | "\"\"\"difference of neighbor pairs\"\"\"\n",
420 | "arr, diffs = [1, 2, 3, 4, 5], []\n",
421 | "for pre, post in zip(arr[:-1], arr[1:]): # zip(arr, arr[1:]) is ok too, zip matches the shortest\n",
422 | " diffs.append(post - pre)\n",
423 | "\n",
424 | "\"\"\"transpose matrix\"\"\"\n",
425 | "# L867: The transpose of a matrix is the matrix flipped over it's main diagonal, switching the row and column indices of the matrix.\n",
426 | "def transpose(matrix: List[List[int]]) -> List[List[int]]:\n",
427 | " return list(map(list, zip(*matrix)))\n",
428 | "\n",
429 | "\"\"\"zip into a set\"\"\"\n",
430 | "# L290: given a pattern and a string words, find if words follows the same pattern. \n",
431 | "# i.e. pattern = 'abba', words = 'dog cat cat dog'\n",
432 | "def valid_word_pattern(pattern: str, words: str) -> bool:\n",
433 | " s, t = pattern, words.split()\n",
434 | " return len(set(zip(s, t))) == len(set(s)) == len(set(t)) and len(s) == len(t)\n",
435 | "```"
436 | ]
437 | },
438 | {
439 | "cell_type": "markdown",
440 | "metadata": {},
441 | "source": [
442 | "## Unpacking "
443 | ]
444 | },
445 | {
446 | "cell_type": "code",
447 | "execution_count": 5,
448 | "metadata": {},
449 | "outputs": [
450 | {
451 | "data": {
452 | "text/plain": [
453 | "6"
454 | ]
455 | },
456 | "execution_count": 5,
457 | "metadata": {},
458 | "output_type": "execute_result"
459 | }
460 | ],
461 | "source": [
462 | "def product(a, b):\n",
463 | " return a * b\n",
464 | "\n",
465 | "\"\"\"use * to unpack tuple, list or other iterables\"\"\"\n",
466 | "param_tuple = (2, 3)\n",
467 | "product(*param_tuple)"
468 | ]
469 | },
470 | {
471 | "cell_type": "code",
472 | "execution_count": 6,
473 | "metadata": {},
474 | "outputs": [
475 | {
476 | "data": {
477 | "text/plain": [
478 | "6"
479 | ]
480 | },
481 | "execution_count": 6,
482 | "metadata": {},
483 | "output_type": "execute_result"
484 | }
485 | ],
486 | "source": [
487 | "\"\"\"use ** to unpack dict\"\"\"\n",
488 | "param_dict = {'a': 2, 'b': 3}\n",
489 | "product(**param_dict)"
490 | ]
491 | },
492 | {
493 | "cell_type": "markdown",
494 | "metadata": {},
495 | "source": [
496 | "#### Inspirations:\n",
497 | "```python\n",
498 | "\"\"\"get rest of all\"\"\"\n",
499 | "a, *b, c = range(5)\n",
500 | "# b = [1, 2, 3]\n",
501 | "[(c, *d, [*e]), f, *g] = [[1, 2, 3, 4, [5, 5, 5]], 6, 7, 8]\n",
502 | "# d = [2, 3, 4], e = [5, 5, 5], g = [7, 8]\n",
503 | "\n",
504 | "\"\"\"merge dicts\"\"\"\n",
505 | "a = {'1': 1, '2': 2}\n",
506 | "b = {'2': 3, '4': 4}\n",
507 | "merged_dict = {**a, **b} #{'1': 1, '2': 3, '4': 4}\n",
508 | "\n",
509 | "\"\"\"[*a] = list(a), [*zip(*matrix)]: transpose matrix\"\"\"\n",
510 | "# traverse matrix in spiral order.\n",
511 | "def traverse_spiral_order(matrix: List[List[int]]) -> List[int]:\n",
512 | " return matrix and [*matrix.pop(0)] + traverse_spiral_order([*zip(*matrix)][::-1])\n",
513 | "```"
514 | ]
515 | },
516 | {
517 | "cell_type": "markdown",
518 | "metadata": {},
519 | "source": [
520 | "## sum "
521 | ]
522 | },
523 | {
524 | "cell_type": "markdown",
525 | "metadata": {},
526 | "source": [
527 | "The `sum` function takes an iterable and returns the sum of items in it. Its syntax is as follows:"
528 | ]
529 | },
530 | {
531 | "cell_type": "markdown",
532 | "metadata": {},
533 | "source": [
534 | "```python\n",
535 | "sum(iterable[, start]) -> number\n",
536 | "```"
537 | ]
538 | },
539 | {
540 | "cell_type": "markdown",
541 | "metadata": {},
542 | "source": [
543 | "#### Inspirations:\n",
544 | "```python\n",
545 | "\"\"\"sum\"\"\"\n",
546 | "# L771: S is stones you have, J is types of stones which are jewels, and you want to know how many stones are also jewels.\n",
547 | "def num_jewels_in_stones(J: str, S: str) -> int:\n",
548 | " return sum(map(J.count, S))\n",
549 | "\n",
550 | "# L389: string t is generated by random shuffling string s and then add one more letter at a random position, find the letter.\n",
551 | "def find_the_difference(s: str, t: str) -> str:\n",
552 | " return chr(sum(map(ord, t)) - sum(map(ord, s)))\n",
553 | "\n",
554 | "# L266: determine if a permutation of the string could form a palindrome\n",
555 | "def can_permute_palindrome(s: str) -> bool:\n",
556 | " \"\"\"at most one odd count character\"\"\"\n",
557 | " return sum(v % 2 for v in Counter(s).values()) <= 1\n",
558 | "```"
559 | ]
560 | },
561 | {
562 | "cell_type": "markdown",
563 | "metadata": {},
564 | "source": [
565 | "## max & min "
566 | ]
567 | },
568 | {
569 | "cell_type": "markdown",
570 | "metadata": {},
571 | "source": [
572 | "The `max` function returns the largest of the input values. Its syntax is as follows:"
573 | ]
574 | },
575 | {
576 | "cell_type": "markdown",
577 | "metadata": {},
578 | "source": [
579 | "```python\n",
580 | "max(iterable[, default=obj, key=func]) -> value\n",
581 | "```"
582 | ]
583 | },
584 | {
585 | "cell_type": "markdown",
586 | "metadata": {},
587 | "source": [
588 | "The `min` function is similar to this."
589 | ]
590 | },
591 | {
592 | "cell_type": "markdown",
593 | "metadata": {},
594 | "source": [
595 | "#### Inspirations:\n",
596 | "```python\n",
597 | "\"\"\"max\"\"\"\n",
598 | "# L169: find the majority element which appears more than ⌊ n/2 ⌋ times.\n",
599 | "def majority_element(nums: List[int]) -> int:\n",
600 | " counts = Counter(nums)\n",
601 | " return max(counts.keys(), key=counts.get)\n",
602 | "\n",
603 | "\"\"\"min\"\"\"\n",
604 | "# L14: find the longest common prefix string amongst an array of strings.\n",
605 | "def longest_common_prefix(strs: List[str]) -> str:\n",
606 | " shortest = min(strs, key=len)\n",
607 | " for i, ch in enumerate(shortest):\n",
608 | " if any(s[i] != ch for s in strs):\n",
609 | " return shortest[:i]\n",
610 | " return shortest\n",
611 | "```"
612 | ]
613 | },
614 | {
615 | "cell_type": "markdown",
616 | "metadata": {},
617 | "source": [
618 | "## any & all "
619 | ]
620 | },
621 | {
622 | "cell_type": "markdown",
623 | "metadata": {},
624 | "source": [
625 | "The `any` function tests whether any item in the iterable evaluates to `True` to not. Its syntax is as follows:"
626 | ]
627 | },
628 | {
629 | "cell_type": "markdown",
630 | "metadata": {},
631 | "source": [
632 | "```python\n",
633 | "any(iterable) -> bool\n",
634 | "```"
635 | ]
636 | },
637 | {
638 | "cell_type": "markdown",
639 | "metadata": {},
640 | "source": [
641 | "The `all` function is similar to this."
642 | ]
643 | },
644 | {
645 | "cell_type": "markdown",
646 | "metadata": {},
647 | "source": [
648 | "#### Inspirations:\n",
649 | "```python\n",
650 | "\"\"\"any\"\"\"\n",
651 | "# L36: determine if a 9x9 Sudoku board is valid, which means 1-9 in row, column, 3x3 sub-boxes\n",
652 | "def is_valid_sudoku(board: List[List[str]]) -> bool:\n",
653 | " seen = set()\n",
654 | " return not any(x in seen or seen.add(x)\n",
655 | " for i, row in enumerate(board)\n",
656 | " for j, c in enumerate(row)\n",
657 | " if c != '.'\n",
658 | " for x in ((c, i), (j, c), (i/3, j/3, c)))\n",
659 | "\n",
660 | "\"\"\"all\"\"\"\n",
661 | "# L766: a matrix is Toeplitz if every diagonal from top-left to bottom-right has the same element.\n",
662 | "def is_toeplitz_matrix(matrix: List[List[int]]) -> bool:\n",
663 | " return all(r == 0 or c == 0 or matrix[r-1][c-1] == val\n",
664 | " for r, row in enumerate(matrix)\n",
665 | " for c, val in enumerate(row))\n",
666 | "\n",
667 | "# L246: verify the number is strobogrammatic, strobogrammatic number looks the same when rotated 180 degrees\n",
668 | "def is_strobogrammatic(num: str) -> bool:\n",
669 | " return all(map('696 00 11 88'.count, map(operator.add, num, num[::-1])))\n",
670 | "\n",
671 | "# L261: given n nodes labeled from 0 to n-1 and a list of undirected edges, check whether these edges make up a valid tree.\n",
672 | "def valid_tree(n: int, edges: List[List[int]]) -> bool:\n",
673 | " \"\"\"all check in union find\"\"\"\n",
674 | " parent = range(n)\n",
675 | " def find(x):\n",
676 | " return x if parent[x] == x else find(parent[x])\n",
677 | " def union(xy):\n",
678 | " x, y = map(find, xy)\n",
679 | " parent[x] = y\n",
680 | " return x != y\n",
681 | " return len(edges) == n-1 and all(map(union, edges))\n",
682 | "```"
683 | ]
684 | },
685 | {
686 | "cell_type": "markdown",
687 | "metadata": {},
688 | "source": [
689 | "## Customize iterable in bisect "
690 | ]
691 | },
692 | {
693 | "cell_type": "markdown",
694 | "metadata": {},
695 | "source": [
696 | "`bisect` module provides support for maintaining a list in sorted order without having to sort the list after each insertion.\n",
697 | "The `bisect_left`/`bisect` function locates the insertion point for `x` in `a` to maintain sorted order."
698 | ]
699 | },
700 | {
701 | "cell_type": "markdown",
702 | "metadata": {},
703 | "source": [
704 | "```python\n",
705 | "bisect.bisect_left(a, x[, lo=0, hi=len(a)]) -> int\n",
706 | "```"
707 | ]
708 | },
709 | {
710 | "cell_type": "markdown",
711 | "metadata": {},
712 | "source": [
713 | "#### Inspirations:\n",
714 | "In the following examples, bisect usage is generalized to customized iterables. The key point is the idea to construct an appropriate iterable. We need to use `__getitem__` for bisect to index.\n",
715 | "```python\n",
716 | "\"\"\"use binary search to find the first number that's less than or equal to the last.\"\"\"\n",
717 | "# L153: find the minimum element in rotated sorted array\n",
718 | "def find_min_in_rotated_sorted_arr(self, nums: List[int]) -> int:\n",
719 | " \"\"\"construct iterable which distinguish the two parts of rotated array\"\"\"\n",
720 | " self.__getitem__ = lambda i: nums[i] <= nums[-1]\n",
721 | " return nums[bisect.bisect(self, False, 0, len(nums))]\n",
722 | "\n",
723 | "\"\"\"construct a boolean iterable and use binary search\"\"\"\n",
724 | "# L4: find the median of the two sorted arrays\n",
725 | "def find_median_in_two_sorted_arrays(nums1: List[int], nums2: List[int]) -> float:\n",
726 | " a, b = sorted((nums1, nums2), key=len)\n",
727 | " m, n = len(a), len(b)\n",
728 | " after = (m + n - 1) // 2\n",
729 | " class Range:\n",
730 | " def __getitem__(self, i):\n",
731 | " return after-i-1 < 0 or a[i] >= b[after-i-1]\n",
732 | " i = bisect.bisect_left(Range(), True, 0, m)\n",
733 | " nextfew = sorted(a[i:i+2] + b[after-i:after-i+2])\n",
734 | " return (nextfew[0] + nextfew[1 - (m+n)%2]) / 2\n",
735 | "```"
736 | ]
737 | },
738 | {
739 | "cell_type": "markdown",
740 | "metadata": {},
741 | "source": [
742 | "***"
743 | ]
744 | }
745 | ],
746 | "metadata": {
747 | "kernelspec": {
748 | "display_name": "Python 3",
749 | "language": "python",
750 | "name": "python3"
751 | },
752 | "language_info": {
753 | "codemirror_mode": {
754 | "name": "ipython",
755 | "version": 3
756 | },
757 | "file_extension": ".py",
758 | "mimetype": "text/x-python",
759 | "name": "python",
760 | "nbconvert_exporter": "python",
761 | "pygments_lexer": "ipython3",
762 | "version": "3.5.5"
763 | }
764 | },
765 | "nbformat": 4,
766 | "nbformat_minor": 2
767 | }
768 |
--------------------------------------------------------------------------------