diff --git a/README.md b/README.md index c03f883..3d8ce77 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Contents **   ** **6. Advanced:** **   ** **[`Threading`](#threading)**__,__ **[`Operator`](#operator)**__,__ **[`Introspection`](#introspection)**__,__ **[`Metaprograming`](#metaprogramming)**__,__ **[`Eval`](#eval)**__,__ **[`Coroutines`](#coroutines)**__.__ **   ** **7. Libraries:** **      ** **[`Progress_Bar`](#progress-bar)**__,__ **[`Plot`](#plot)**__,__ **[`Tables`](#table)**__,__ **[`Curses`](#curses)**__,__ **[`Logging`](#logging)**__,__ **[`Scraping`](#scraping)**__,__ **[`Web`](#web)**__,__ **[`Profile`](#profiling)**__.__ **   ** **8. Multimedia:** **  ** **[`NumPy`](#numpy)**__,__ **[`Image`](#image)**__,__ **[`Animation`](#animation)**__,__ **[`Audio`](#audio)**__,__ **[`Pygame`](#pygame)**__,__ **[`Pandas`](#pandas)**__,__ **[`Plotly`](#plotly)**__,__ **[`PySimpleGUI`](#pysimplegui)**__.__ +**   ** **6. Advanced:** **   ** **[`Threading`](#threading)**__,__ **[`Operator`](#operator)**__,__ **[`Match_Stmt`](#match-statement)**__,__ **[`Introspection`](#introspection)**__,__ **[`Logging`](#logging)**__,__ **[`Coroutines`](#coroutines)**__.__ +**   ** **7. Libraries:** **      ** **[`Progress_Bar`](#progress-bar)**__,__ **[`Plots`](#plot)**__,__ **[`Tables`](#table)**__,__ **[`Curses`](#curses)**__,__ **[`GUIs`](#pysimplegui)**__,__ **[`Scraping`](#scraping)**__,__ **[`Web`](#web)**__,__ **[`Profiling`](#profiling)**__.__ +**   ** **8. Multimedia:** **  ** **[`NumPy`](#numpy)**__,__ **[`Image`](#image)**__,__ **[`Animation`](#animation)**__,__ **[`Audio`](#audio)**__,__ **[`Synthesizer`](#synthesizer)**__,__ **[`Pygame`](#pygame)**__,__ **[`Pandas`](#pandas)**__,__ **[`Plotly`](#plotly)**__.__ Main @@ -317,7 +320,6 @@ String ```python = in # Checks if string contains the substring. = .startswith() # Pass tuple of strings for multiple options. - = .endswith() # Pass tuple of strings for multiple options. = .find() # Returns start index of the first match or -1. = .index() # Same, but raises ValueError if missing. ``` @@ -333,6 +335,7 @@ String = ord() # Converts Unicode character to int. ``` * **Use `'unicodedata.normalize("NFC", )'` on strings that may contain characters like `'Ö'` before comparing them, because they can be stored as one or two characters.** +* **`'NFC'` converts such characters to a single character, while `'NFD'` converts them to two.** ### Property Methods ```python @@ -351,9 +354,6 @@ Regex ```python import re -``` - -```python = re.sub(, new, text, count=0) # Substitutes all occurrences with 'new'. = re.findall(, text) # Returns all occurrences as strings. = re.split(, text, maxsplit=0) # Add brackets around regex to include matches. @@ -368,6 +368,7 @@ import re * **Argument `'flags=re.DOTALL'` makes `'.'` also accept the `'\n'`.** * **Use `r'\1'` or `'\\1'` for backreference (`'\1'` returns a character with octal code 1).** * **Add `'?'` after `'*'` and `'+'` to make them non-greedy.** +* **`'re.compile()'` returns a Pattern object with listed methods.** ### Match Object ```python @@ -589,7 +590,7 @@ Datetime **Provides 'date', 'time', 'datetime' and 'timedelta' classes. All are immutable and hashable.** ```python -# pip3 install python-dateutil +# $ pip3 install python-dateutil from datetime import date, time, datetime, timedelta, timezone from dateutil.tz import tzlocal, gettz ``` @@ -740,7 +741,7 @@ def f(x, y, *, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z= = [* [, ...]] # Or: list() [+ ...] = (*, [...]) # Or: tuple() [+ ...] = {* [, ...]} # Or: set() [| ...] - = {** [, ...]} # Or: dict(** [, ...]) + = {** [, ...]} # Or: dict() [| ...] (since 3.9) ``` ```python @@ -2183,6 +2184,48 @@ first_element = op.methodcaller('pop', 0)() * **Also: `' = &|^ '` and `' = &|^ '`.** +Match Statement +--------------- +**Executes the first block with matching pattern. Added in Python 3.10.** + +```python +match : + case [if ]: + + ... +``` + +### Patterns +```python + = 1/'abc'/True/None/math.pi # Matches the literal or a dotted name. + = () # Matches any object of that type. + = # Matches any object and binds it to name. + = | [| ...] # Matches any of the patterns. + = as # Binds the match to the name. + = [, ...] # Matches sequence with matching items. + = {: , ...} # Matches dictionary with matching items. + = (=, ...) # Matches object with matching attributes. +``` +* **Sequence pattern can also be written as a tuple.** +* **Use `'*'` and `'**'` in sequence/mapping patterns to bind remaining items.** +* **Patterns can be surrounded with brackets to override precedence (`'|'` > `'as'` > `','`).** +* **Built-in types allow a single positional pattern that is matched against the entire object.** +* **All names that are bound in the matching case, as well as variables initialized in its block, are visible after the match statement.** + +### Example +```python +>>> from pathlib import Path +>>> match Path('/home/gto/python-cheatsheet/README.md'): +... case Path( +... parts=['/', 'home', user, *_], +... stem=stem, +... suffix=('.md' | '.txt') as suffix +... ) if stem.lower() == 'readme': +... print(f'{stem}{suffix} is a readme file that belongs to user {user}.') +'README.md is a readme file that belongs to user gto.' +``` + + Introspection ------------- ```python @@ -2191,7 +2234,6 @@ Introspection = globals() # Dict of global vars, etc. (incl. '__builtins__'). ``` -### Attributes ```python = dir() # Names of object's attributes (including methods). = vars() # Dict of writable attributes. Also .__dict__. @@ -2201,7 +2243,6 @@ setattr(, '', value) # Only works on objects with '__dict_ delattr(, '') # Same. Also `del .`. ``` -### Parameters ```python = inspect.signature() # Function's Signature object. = .parameters # Dict of Parameter objects. @@ -2211,101 +2252,56 @@ delattr(, '') # Same. Also `del . = type('', , ) -``` - -```python ->>> Z = type('Z', (), {'a': 'abcde', 'b': 12345}) ->>> z = Z() -``` - -### Meta Class -**A class that creates classes.** - -```python -def my_meta_class(name, parents, attrs): - attrs['a'] = 'abcde' - return type(name, parents, attrs) -``` - -#### Or: -```python -class MyMetaClass(type): - def __new__(cls, name, parents, attrs): - attrs['a'] = 'abcde' - return type.__new__(cls, name, parents, attrs) -``` -* **New() is a class method that gets called before init(). If it returns an instance of its class, then that instance gets passed to init() as a 'self' argument.** -* **It receives the same arguments as init(), except for the first one that specifies the desired type of the returned instance (MyMetaClass in our case).** -* **Like in our case, new() can also be called directly, usually from a new() method of a child class (**`def __new__(cls): return super().__new__(cls)`**).** -* **The only difference between the examples above is that my\_meta\_class() returns a class of type type, while MyMetaClass() returns a class of type MyMetaClass.** - -### Metaclass Attribute -**Right before a class is created it checks if it has the 'metaclass' attribute defined. If not, it recursively checks if any of its parents has it defined and eventually comes to type().** - +Logging +------- ```python -class MyClass(metaclass=MyMetaClass): - b = 12345 +import logging ``` ```python ->>> MyClass.a, MyClass.b -('abcde', 12345) +logging.basicConfig(filename=, level='DEBUG') # Configures the root logger (see Setup). +logging.debug/info/warning/error/critical() # Logs to the root logger. + = logging.getLogger(__name__) # Logger named after the module. +.() # Logs to the logger. +.exception() # Calls error() with caught exception. ``` -### Type Diagram +### Setup ```python -type(MyClass) == MyMetaClass # MyClass is an instance of MyMetaClass. -type(MyMetaClass) == type # MyMetaClass is an instance of type. -``` - -```text -+-------------+-------------+ -| Classes | Metaclasses | -+-------------+-------------| -| MyClass <-- MyMetaClass | -| | ^ | -| object <----- type <+ | -| | | +--+ | -| str <---------+ | -+-------------+-------------+ +logging.basicConfig( + filename=None, # Logs to console (stderr) by default. + format='%(levelname)s:%(name)s:%(message)s', # Add '%(asctime)s' for local datetime. + level=logging.WARNING, # Drops messages with lower priority. + handlers=[logging.StreamHandler(sys.stderr)] # Uses FileHandler if filename is set. +) ``` -### Inheritance Diagram ```python -MyClass.__base__ == object # MyClass is a subclass of object. -MyMetaClass.__base__ == type # MyMetaClass is a subclass of type. -``` - -```text -+-------------+-------------+ -| Classes | Metaclasses | -+-------------+-------------| -| MyClass | MyMetaClass | -| ^ | ^ | -| object -----> type | -| v | | -| str | | -+-------------+-------------+ + = logging.Formatter('') # Creates a Formatter. + = logging.FileHandler(, mode='a') # Creates a Handler. Also `encoding=None`. +.setFormatter() # Adds Formatter to the Handler. +.setLevel() # Processes all messages by default. +.addHandler() # Adds Handler to the Logger. +.setLevel() # What is sent to its/ancestor's handlers. ``` +* **Parent logger can be specified by naming the child logger `'.'`.** +* **If logger doesn't have a set level it inherits it from the first ancestor that does.** +* **Formatter also accepts: pathname, filename, funcName, lineno, thread and process.** +* **A `'handlers.RotatingFileHandler'` creates and deletes log files based on 'maxBytes' and 'backupCount' arguments.** - -Eval ----- +#### Creates a logger that writes all messages to file and sends them to the root's handler that prints warnings or higher: ```python ->>> from ast import literal_eval ->>> literal_eval('[1, 2, 3]') -[1, 2, 3] ->>> literal_eval('1 + 2') -ValueError: malformed node or string +>>> logger = logging.getLogger('my_module') +>>> handler = logging.FileHandler('test.log', encoding='utf-8') +>>> formatter = logging.Formatter('%(asctime)s %(levelname)s:%(name)s:%(message)s') +>>> handler.setFormatter(formatter) +>>> logger.addHandler(handler) +>>> logging.basicConfig(level='DEBUG') +>>> logging.root.handlers[0].setLevel('WARNING') +>>> logger.critical('Running out of disk space.') +CRITICAL:my_module:Running out of disk space. +>>> print(open('test.log').read()) +2023-02-07 23:21:01,430 CRITICAL:my_module:Running out of disk space. ``` @@ -2320,7 +2316,7 @@ Coroutines #### Runs a terminal game where you control an asterisk that must avoid numbers: ```python -import asyncio, collections, curses, curses.textpad, enum, random +import asyncio, collections, curses, curses.textpad, enum, random, time P = collections.namedtuple('P', 'x y') # Position D = enum.Enum('D', 'n e s w') # Direction @@ -2348,7 +2344,8 @@ async def random_controller(id_, moves): async def human_controller(screen, moves): while True: key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e} - if d := key_mappings.get(screen.getch()): + ch = screen.getch() + if d := key_mappings.get(ch): moves.put_nowait(('*', d)) await asyncio.sleep(0.005) @@ -2357,7 +2354,8 @@ async def model(moves, state): id_, d = await moves.get() x, y = state[id_] deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)} - state[id_] = P((x + deltas[d].x) % W, (y + deltas[d].y) % H) + dx, dy = deltas[d] + state[id_] = P((x + dx) % W, (y + dy) % H) async def view(state, screen): offset = P(curses.COLS//2 - W//2, curses.LINES//2 - H//2) @@ -2365,13 +2363,18 @@ async def view(state, screen): screen.erase() curses.textpad.rectangle(screen, offset.y-1, offset.x-1, offset.y+H, offset.x+W) for id_, p in state.items(): - screen.addstr(offset.y + (p.y - state['*'].y + H//2) % H, - offset.x + (p.x - state['*'].x + W//2) % W, str(id_)) + screen.addstr( + offset.y + (p.y - state['*'].y + H//2) % H, + offset.x + (p.x - state['*'].x + W//2) % W, + str(id_) + ) screen.refresh() await asyncio.sleep(0.005) if __name__ == '__main__': + start_time = time.perf_counter() curses.wrapper(main) + print(f'You survived {time.perf_counter() - start_time:.2f} seconds.') ```
@@ -2421,7 +2424,7 @@ Curses ------ #### Runs a basic file explorer in the console: ```python -# pip3 install windows-curses +# $ pip3 install windows-curses import curses, os from curses import A_REVERSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_ENTER @@ -2448,56 +2451,33 @@ if __name__ == '__main__': ``` -Logging -------- -```python -import logging -``` +PySimpleGUI +----------- +#### A weight converter GUI application: ```python -logging.basicConfig(filename=, level='DEBUG') # Configures the root logger (see Setup). -logging.debug/info/warning/error/critical() # Logs to the root logger. - = logging.getLogger(__name__) # Logger named after the module. -.() # Logs to the logger. -.exception() # Calls error() with caught exception. -``` +# $ pip3 install PySimpleGUI +import PySimpleGUI as sg -### Setup -```python -logging.basicConfig( - filename=None, # Logs to console (stderr) by default. - format='%(levelname)s:%(name)s:%(message)s', # Add '%(asctime)s' for local datetime. - level=logging.WARNING, # Drops messages with lower priority. - handlers=[logging.StreamHandler(sys.stderr)] # Uses FileHandler if filename is set. -) -``` +text_box = sg.Input(default_text='100', enable_events=True, key='-VALUE-') +dropdown = sg.InputCombo(['g', 'kg', 't'], 'kg', readonly=True, enable_events=True, k='-UNIT-') +label = sg.Text('100 kg is 220.462 lbs.', key='-OUTPUT-') +button = sg.Button('Close') +window = sg.Window('Weight Converter', [[text_box, dropdown], [label], [button]]) -```python - = logging.Formatter('') # Creates a Formatter. - = logging.FileHandler(, mode='a') # Creates a Handler. Also `encoding=None`. -.setFormatter() # Adds Formatter to the Handler. -.setLevel() # Processes all messages by default. -.addHandler() # Adds Handler to the Logger. -.setLevel() # What is sent to its/ancestor's handlers. -``` -* **Parent logger can be specified by naming the child logger `'.'`.** -* **If logger doesn't have a set level it inherits it from the first ancestor that does.** -* **Formatter also accepts: pathname, filename, funcName, lineno, thread and process.** -* **A `'handlers.RotatingFileHandler'` creates and deletes log files based on 'maxBytes' and 'backupCount' arguments.** - -#### Creates a logger that writes all messages to file and sends them to the root's handler that prints warnings or higher: -```python ->>> logger = logging.getLogger('my_module') ->>> handler = logging.FileHandler('test.log', encoding='utf-8') ->>> formatter = logging.Formatter('%(asctime)s %(levelname)s:%(name)s:%(message)s') ->>> handler.setFormatter(formatter) ->>> logger.addHandler(handler) ->>> logging.basicConfig(level='DEBUG') ->>> logging.root.handlers[0].setLevel('WARNING') ->>> logger.critical('Running out of disk space.') -CRITICAL:my_module:Running out of disk space. ->>> print(open('test.log').read()) -2023-02-07 23:21:01,430 CRITICAL:my_module:Running out of disk space. +while True: + event, values = window.read() + if event in [sg.WIN_CLOSED, 'Close']: + break + try: + value = float(values['-VALUE-']) + except ValueError: + continue + unit = values['-UNIT-'] + units = {'g': 0.001, 'kg': 1, 't': 1000} + lbs = value * units[unit] / 0.45359237 + window['-OUTPUT-'].update(value=f'{value} {unit} is {lbs:g} lbs.') +window.close() ``` @@ -2506,21 +2486,43 @@ Scraping #### Scrapes Python's URL and logo from its Wikipedia page: ```python # $ pip3 install requests beautifulsoup4 -import requests, bs4, os, sys +import requests, bs4, os -try: - response = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)') - document = bs4.BeautifulSoup(response.text, 'html.parser') - table = document.find('table', class_='infobox vevent') - python_url = table.find('th', text='Website').next_sibling.a['href'] - logo_url = table.find('img')['src'] - logo = requests.get(f'https:{logo_url}').content - filename = os.path.basename(logo_url) - with open(filename, 'wb') as file: - file.write(logo) - print(f'{python_url}, file://{os.path.abspath(filename)}') -except requests.exceptions.ConnectionError: - print("You've got problems with connection.", file=sys.stderr) +response = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)') +document = bs4.BeautifulSoup(response.text, 'html.parser') +table = document.find('table', class_='infobox vevent') +python_url = table.find('th', text='Website').next_sibling.a['href'] +logo_url = table.find('img')['src'] +logo = requests.get(f'https:{logo_url}').content +filename = os.path.basename(logo_url) +with open(filename, 'wb') as file: + file.write(logo) +print(f'{python_url}, file://{os.path.abspath(filename)}') +``` + +### Selenium +**Library for scraping websites with dynamic content.** +```python +# $ pip3 install selenium +from selenium import webdriver + + = webdriver.Chrome/Firefox/Safari/Edge() # Opens a browser. Also .quit(). +.get('') # Also .implicitly_wait(seconds). + = .find_element('css selector', '') # '#.[=""]'. + = .find_elements('xpath', '') # '//[@=""]'. + = .get_attribute/get_property() # Also .text/tag_name. +.click/clear() # Also .send_keys(). +``` + +#### XPath — also available in browser's console via `'$x()'`: +```python + = //[/ or // ] # Child: /, Descendant: //, Parent: /.. + = ///following:: # Next sibling. Also preceding/parent/… + = # ` = */a/…`, ` = [1/2/…]`. + = [ [and/or ]] # `and` is same as chaining conditions. + = @="" # `.=""` matches complete text. + = contains(@, "") # Is a substring of attr's value? + = [//] # Has matching child? Descendant if //. ``` @@ -2763,9 +2765,9 @@ from PIL import Image, ImageFilter, ImageEnhance ``` ```python - = .getpixel((x, y)) # Returns pixel's color. -.putpixel((x, y), ) # Changes pixel's color. - = .getdata() # Returns a flattened view of all pixels. + = .getpixel((x, y)) # Returns pixel's value (its color). +.putpixel((x, y), ) # Updates pixel's value. + = .getdata() # Returns a flattened view of pixel values. .putdata() # Updates pixels with a copy of the sequence. .paste(, (x, y)) # Draws passed image at specified location. ``` @@ -2776,8 +2778,8 @@ from PIL import Image, ImageFilter, ImageEnhance ``` ```python - = np.array() # Creates a NumPy array from the image. - = Image.fromarray(np.uint8()) # Use .clip(0, 255) to clip values. + = np.array() # Creates a 2d/3d NumPy array from the image. + = Image.fromarray(np.uint8()) # Use `.clip(0, 255)` to clip values. ``` ### Modes @@ -3496,19 +3498,6 @@ if __name__ == '__main__': ``` -PySimpleGUI ------------ -```python -# $ pip3 install PySimpleGUI -import PySimpleGUI as sg - -layout = [[sg.Text("What's your name?")], [sg.Input()], [sg.Button('Ok')]] -window = sg.Window('Window Title', layout) -event, values = window.read() -print(f'Hello {values[0]}!' if event == 'Ok' else '') -``` - - Appendix -------- ### Cython diff --git a/index.html b/index.html index 9ff258c..63ff1de 100644 --- a/index.html +++ b/index.html @@ -54,7 +54,7 @@
- +
@@ -85,9 +85,9 @@ '3. Syntax': [Args, Inline, Import, Decorator, Class, Duck_Types, Enum, Exception], '4. System': [Exit, Print, Input, Command_Line_Arguments, Open, Path, OS_Commands], '5. Data': [JSON, Pickle, CSV, SQLite, Bytes, Struct, Array, Memory_View, Deque], - '6. Advanced': [Threading, Operator, Introspection, Metaprograming, Eval, Coroutine], - '7. Libraries': [Progress_Bar, Plot, Tables, Curses, Logging, Scraping, Web, Profile], - '8. Multimedia': [NumPy, Image, Animation, Audio, Pygame, Pandas, Plotly, PySimpleGUI] + '6. Advanced': [Threading, Operator, Match_Stmt, Introspection, Logging, Coroutines], + '7. Libraries': [Progress_Bar, Plots, Tables, Curses, GUIs, Scraping, Web, Profiling], + '8. Multimedia': [NumPy, Image, Animation, Audio, Synthesizer, Pygame, Pandas, Plotly] } @@ -302,7 +302,6 @@ Point(x=1, y=2
<bool> = <sub_str> in <str>                  # Checks if string contains the substring.
 <bool> = <str>.startswith(<sub_str>)         # Pass tuple of strings for multiple options.
-<bool> = <str>.endswith(<sub_str>)           # Pass tuple of strings for multiple options.
 <int>  = <str>.find(<sub_str>)               # Returns start index of the first match or -1.
 <int>  = <str>.index(<sub_str>)              # Same, but raises ValueError if missing.
 
@@ -315,6 +314,7 @@ Point(x=1, y=2
  • Use 'unicodedata.normalize("NFC", <str>)' on strings that may contain characters like 'Ö' before comparing them, because they can be stored as one or two characters.
  • +
  • 'NFC' converts such characters to a single character, while 'NFD' converts them to two.

Property Methods

<bool> = <str>.isdecimal()                   # Checks for [0-9].
 <bool> = <str>.isdigit()                     # Checks for [²³¹] and isdecimal().
@@ -325,16 +325,15 @@ Point(x=1, y=2
 

#Regex

Functions for regular expression matching.

import re
-
- - -
<str>   = re.sub(<regex>, new, text, count=0)  # Substitutes all occurrences with 'new'.
+<str>   = re.sub(<regex>, new, text, count=0)  # Substitutes all occurrences with 'new'.
 <list>  = re.findall(<regex>, text)            # Returns all occurrences as strings.
 <list>  = re.split(<regex>, text, maxsplit=0)  # Add brackets around regex to include matches.
 <Match> = re.search(<regex>, text)             # First occurrence of the pattern or None.
 <Match> = re.match(<regex>, text)              # Searches only at the beginning of the text.
 <iter>  = re.finditer(<regex>, text)           # Returns all occurrences as Match objects.
-
+ + +
  • Argument 'new' can be a function that accepts a Match object and returns a string.
  • Argument 'flags=re.IGNORECASE' can be used with all functions.
  • @@ -342,6 +341,7 @@ Point(x=1, y=2
  • Argument 'flags=re.DOTALL' makes '.' also accept the '\n'.
  • Use r'\1' or '\\1' for backreference ('\1' returns a character with octal code 1).
  • Add '?' after '*' and '+' to make them non-greedy.
  • +
  • 're.compile(<regex>)' returns a Pattern object with listed methods.

Match Object

<str>   = <Match>.group()                      # Returns the whole match. Also group(0).
 <str>   = <Match>.group(1)                     # Returns the part inside first brackets.
@@ -511,7 +511,7 @@ Point(x=1, y=2
  ('b', 'a'), ('b', 'c'),                              # b x  .  x
  ('c', 'a'), ('c', 'b')]                              # c x  x  .
 
-

#Datetime

Provides 'date', 'time', 'datetime' and 'timedelta' classes. All are immutable and hashable.

# pip3 install python-dateutil
+

#Datetime

Provides 'date', 'time', 'datetime' and 'timedelta' classes. All are immutable and hashable.

# $ pip3 install python-dateutil
 from datetime import date, time, datetime, timedelta, timezone
 from dateutil.tz import tzlocal, gettz
 
@@ -635,7 +635,7 @@ func(*args, **kwargs)

Other Uses

<list>  = [*<coll.> [, ...]]    # Or: list(<collection>) [+ ...]
 <tuple> = (*<coll.>, [...])     # Or: tuple(<collection>) [+ ...]
 <set>   = {*<coll.> [, ...]}    # Or: set(<collection>) [| ...]
-<dict>  = {**<dict> [, ...]}    # Or: dict(**<dict> [, ...])
+<dict>  = {**<dict> [, ...]}    # Or: dict(<dict>) [| ...] (since 3.9)
 
head, *body, tail = <coll.>     # Head or tail can be omitted.
@@ -1799,93 +1799,100 @@ first_element    = op.methodcaller('pop', Bitwise operators require objects to have or(), xor(), and(), lshift(), rshift() and invert() special methods, unlike logical operators that work on all types of objects.
 
  • Also: '<bool> = <bool> &|^ <bool>' and '<int> = <bool> &|^ <int>'.
  • +

    #Match Statement

    Executes the first block with matching pattern. Added in Python 3.10.

    match <object/expression>:
    +    case <pattern> [if <condition>]:
    +        <code>
    +    ...
    +
    + + +

    Patterns

    <value_pattern> = 1/'abc'/True/None/math.pi        # Matches the literal or a dotted name.
    +<class_pattern> = <type>()                         # Matches any object of that type.
    +<capture_patt>  = <name>                           # Matches any object and binds it to name.
    +<or_pattern>    = <pattern> | <pattern> [| ...]    # Matches any of the patterns.
    +<as_pattern>    = <pattern> as <name>              # Binds the match to the name.
    +<sequence_patt> = [<pattern>, ...]                 # Matches sequence with matching items.
    +<mapping_patt>  = {<value_patt>: <pattern>, ...}   # Matches dictionary with matching items.
    +<class_pattern> = <type>(<attr_name>=<patt>, ...)  # Matches object with matching attributes.
    +
    + +
      +
    • Sequence pattern can also be written as a tuple.
    • +
    • Use '*<name>' and '**<name>' in sequence/mapping patterns to bind remaining items.
    • +
    • Patterns can be surrounded with brackets to override precedence ('|' > 'as' > ',').
    • +
    • Built-in types allow a single positional pattern that is matched against the entire object.
    • +
    • All names that are bound in the matching case, as well as variables initialized in its block, are visible after the match statement.
    • +
    +

    Example

    >>> from pathlib import Path
    +>>> match Path('/home/gto/python-cheatsheet/README.md'):
    +...     case Path(
    +...         parts=['/', 'home', user, *_],
    +...         stem=stem,
    +...         suffix=('.md' | '.txt') as suffix
    +...     ) if stem.lower() == 'readme':
    +...         print(f'{stem}{suffix} is a readme file that belongs to user {user}.')
    +'README.md is a readme file that belongs to user gto.'
    +
    +

    #Introspection

    <list> = dir()                             # Names of local variables, functions, classes, etc.
     <dict> = vars()                            # Dict of local variables, etc. Also locals().
     <dict> = globals()                         # Dict of global vars, etc. (incl. '__builtins__').
     
    -

    Attributes

    <list> = dir(<object>)                     # Names of object's attributes (including methods).
    +
    <list> = dir(<object>)                     # Names of object's attributes (including methods).
     <dict> = vars(<object>)                    # Dict of writable attributes. Also <obj>.__dict__.
     <bool> = hasattr(<object>, '<attr_name>')  # Checks if getattr() raises an AttributeError.
     value  = getattr(<object>, '<attr_name>')  # Raises AttributeError if attribute is missing.
     setattr(<object>, '<attr_name>', value)    # Only works on objects with '__dict__' attribute.
     delattr(<object>, '<attr_name>')           # Same. Also `del <object>.<attr_name>`.
    -
    - -

    Parameters

    <Sig>  = inspect.signature(<function>)     # Function's Signature object.
    +
    +
    <Sig>  = inspect.signature(<function>)     # Function's Signature object.
     <dict> = <Sig>.parameters                  # Dict of Parameter objects.
     <memb> = <Param>.kind                      # Member of ParameterKind enum.
     <obj>  = <Param>.default                   # Default value or Parameter.empty.
     <type> = <Param>.annotation                # Type or Parameter.empty.
    -
    - -

    #Metaprogramming

    Code that generates code.

    Type

    Type is the root class. If only passed an object it returns its type (class). Otherwise it creates a new class.

    <class> = type('<class_name>', <tuple_of_parents>, <dict_of_class_attributes>)
    - - - - -
    >>> Z = type('Z', (), {'a': 'abcde', 'b': 12345})
    ->>> z = Z()
    -
    -

    Meta Class

    A class that creates classes.

    def my_meta_class(name, parents, attrs):
    -    attrs['a'] = 'abcde'
    -    return type(name, parents, attrs)
    -
    - - -

    Or:

    class MyMetaClass(type):
    -    def __new__(cls, name, parents, attrs):
    -        attrs['a'] = 'abcde'
    -        return type.__new__(cls, name, parents, attrs)
    -
    - -
      -
    • New() is a class method that gets called before init(). If it returns an instance of its class, then that instance gets passed to init() as a 'self' argument.
    • -
    • It receives the same arguments as init(), except for the first one that specifies the desired type of the returned instance (MyMetaClass in our case).
    • -
    • Like in our case, new() can also be called directly, usually from a new() method of a child class (def __new__(cls): return super().__new__(cls)).
    • -
    • The only difference between the examples above is that my_meta_class() returns a class of type type, while MyMetaClass() returns a class of type MyMetaClass.
    • -
    -

    Metaclass Attribute

    Right before a class is created it checks if it has the 'metaclass' attribute defined. If not, it recursively checks if any of its parents has it defined and eventually comes to type().

    class MyClass(metaclass=MyMetaClass):
    -    b = 12345
    -
    - - -
    >>> MyClass.a, MyClass.b
    -('abcde', 12345)
     
    -

    Type Diagram

    type(MyClass) == MyMetaClass         # MyClass is an instance of MyMetaClass.
    -type(MyMetaClass) == type            # MyMetaClass is an instance of type.
    +

    #Logging

    import logging
     
    -
    ┏━━━━━━━━━━━━━┯━━━━━━━━━━━━━┓
    -┃   Classes   │ Metaclasses ┃
    -┠─────────────┼─────────────┨
    -┃   MyClass ←──╴MyMetaClass ┃
    -┃             │     ↑       ┃
    -┃    object ←─────╴type ←╮  ┃
    -┃             │     │ ╰──╯  ┃
    -┃     str ←─────────╯       ┃
    -┗━━━━━━━━━━━━━┷━━━━━━━━━━━━━┛
    +
    logging.basicConfig(filename=<path>, level='DEBUG')  # Configures the root logger (see Setup).
    +logging.debug/info/warning/error/critical(<str>)     # Logs to the root logger.
    +<Logger> = logging.getLogger(__name__)               # Logger named after the module.
    +<Logger>.<level>(<str>)                              # Logs to the logger.
    +<Logger>.exception(<str>)                            # Calls error() with caught exception.
     
    -

    Inheritance Diagram

    MyClass.__base__ == object           # MyClass is a subclass of object.
    -MyMetaClass.__base__ == type         # MyMetaClass is a subclass of type.
    +

    Setup

    logging.basicConfig(
    +    filename=None,                                   # Logs to console (stderr) by default.
    +    format='%(levelname)s:%(name)s:%(message)s',     # Add '%(asctime)s' for local datetime.
    +    level=logging.WARNING,                           # Drops messages with lower priority.
    +    handlers=[logging.StreamHandler(sys.stderr)]     # Uses FileHandler if filename is set.
    +)
     
    -
    ┏━━━━━━━━━━━━━┯━━━━━━━━━━━━━┓
    -┃   Classes   │ Metaclasses ┃
    -┠─────────────┼─────────────┨
    -┃   MyClass   │ MyMetaClass ┃
    -┃      ↑      │     ↑       ┃
    -┃    object╶─────→ type     ┃
    -┃      ↓      │             ┃
    -┃     str     │             ┃
    -┗━━━━━━━━━━━━━┷━━━━━━━━━━━━━┛
    +
    <Formatter> = logging.Formatter('<format>')          # Creates a Formatter.
    +<Handler> = logging.FileHandler(<path>, mode='a')    # Creates a Handler. Also `encoding=None`.
    +<Handler>.setFormatter(<Formatter>)                  # Adds Formatter to the Handler.
    +<Handler>.setLevel(<int/str>)                        # Processes all messages by default.
    +<Logger>.addHandler(<Handler>)                       # Adds Handler to the Logger.
    +<Logger>.setLevel(<int/str>)                         # What is sent to its/ancestor's handlers.
     
    -

    #Eval

    >>> from ast import literal_eval
    ->>> literal_eval('[1, 2, 3]')
    -[1, 2, 3]
    ->>> literal_eval('1 + 2')
    -ValueError: malformed node or string
    +
      +
    • Parent logger can be specified by naming the child logger '<parent>.<name>'.
    • +
    • If logger doesn't have a set level it inherits it from the first ancestor that does.
    • +
    • Formatter also accepts: pathname, filename, funcName, lineno, thread and process.
    • +
    • A 'handlers.RotatingFileHandler' creates and deletes log files based on 'maxBytes' and 'backupCount' arguments.
    • +
    +

    Creates a logger that writes all messages to file and sends them to the root's handler that prints warnings or higher:

    >>> logger = logging.getLogger('my_module')
    +>>> handler = logging.FileHandler('test.log', encoding='utf-8')
    +>>> formatter = logging.Formatter('%(asctime)s %(levelname)s:%(name)s:%(message)s')
    +>>> handler.setFormatter(formatter)
    +>>> logger.addHandler(handler)
    +>>> logging.basicConfig(level='DEBUG')
    +>>> logging.root.handlers[0].setLevel('WARNING')
    +>>> logger.critical('Running out of disk space.')
    +CRITICAL:my_module:Running out of disk space.
    +>>> print(open('test.log').read())
    +2023-02-07 23:21:01,430 CRITICAL:my_module:Running out of disk space.
     

    #Coroutines

      @@ -1894,7 +1901,7 @@ ValueError: malformed node or string
    • 'asyncio.run(<coroutine>)' is the main entry point for asynchronous programs.
    • Functions wait(), gather() and as_completed() start multiple coroutines at the same time.
    • Asyncio module also provides its own Queue, Event, Lock and Semaphore classes.
    • -

    Runs a terminal game where you control an asterisk that must avoid numbers:

    import asyncio, collections, curses, curses.textpad, enum, random
    +

    Runs a terminal game where you control an asterisk that must avoid numbers:

    import asyncio, collections, curses, curses.textpad, enum, random, time
     
     P = collections.namedtuple('P', 'x y')         # Position
     D = enum.Enum('D', 'n e s w')                  # Direction
    @@ -1922,7 +1929,8 @@ W, H = 15, 7
     async def human_controller(screen, moves):
         while True:
             key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e}
    -        if d := key_mappings.get(screen.getch()):
    +        ch = screen.getch()
    +        if d := key_mappings.get(ch):
                 moves.put_nowait(('*', d))
             await asyncio.sleep(0.005)
     
    @@ -1931,7 +1939,8 @@ W, H = 15, 7
             id_, d = await moves.get()
             x, y   = state[id_]
             deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
    -        state[id_] = P((x + deltas[d].x) % W, (y + deltas[d].y) % H)
    +        dx, dy = deltas[d]
    +        state[id_] = P((x + dx) % W, (y + dy) % H)
     
     async def view(state, screen):
         offset = P(curses.COLS//2 - W//2, curses.LINES//2 - H//2)
    @@ -1939,13 +1948,18 @@ W, H = 15, 7
             screen.erase()
             curses.textpad.rectangle(screen, offset.y-1, offset.x-1, offset.y+H, offset.x+W)
             for id_, p in state.items():
    -            screen.addstr(offset.y + (p.y - state['*'].y + H//2) % H,
    -                          offset.x + (p.x - state['*'].x + W//2) % W, str(id_))
    +            screen.addstr(
    +                offset.y + (p.y - state['*'].y + H//2) % H,
    +                offset.x + (p.x - state['*'].x + W//2) % W,
    +                str(id_)
    +            )
             screen.refresh()
             await asyncio.sleep(0.005)
     
     if __name__ == '__main__':
    +    start_time = time.perf_counter()
         curses.wrapper(main)
    +    print(f'You survived {time.perf_counter() - start_time:.2f} seconds.')
     
    @@ -1978,7 +1992,7 @@ print(table)
    -

    #Curses

    Runs a basic file explorer in the console:

    # pip3 install windows-curses
    +

    #Curses

    Runs a basic file explorer in the console:

    # $ pip3 install windows-curses
     import curses, os
     from curses import A_REVERSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_ENTER
     
    @@ -2005,68 +2019,68 @@ print(table)
     
    -

    #Logging

    import logging
    -
    +

    #PySimpleGUI

    A weight converter GUI application:

    # $ pip3 install PySimpleGUI
    +import PySimpleGUI as sg
     
    -
    logging.basicConfig(filename=<path>, level='DEBUG')  # Configures the root logger (see Setup).
    -logging.debug/info/warning/error/critical(<str>)     # Logs to the root logger.
    -<Logger> = logging.getLogger(__name__)               # Logger named after the module.
    -<Logger>.<level>(<str>)                              # Logs to the logger.
    -<Logger>.exception(<str>)                            # Calls error() with caught exception.
    -
    -

    Setup

    logging.basicConfig(
    -    filename=None,                                   # Logs to console (stderr) by default.
    -    format='%(levelname)s:%(name)s:%(message)s',     # Add '%(asctime)s' for local datetime.
    -    level=logging.WARNING,                           # Drops messages with lower priority.
    -    handlers=[logging.StreamHandler(sys.stderr)]     # Uses FileHandler if filename is set.
    -)
    -
    +text_box = sg.Input(default_text='100', enable_events=True, key='-VALUE-') +dropdown = sg.InputCombo(['g', 'kg', 't'], 'kg', readonly=True, enable_events=True, k='-UNIT-') +label = sg.Text('100 kg is 220.462 lbs.', key='-OUTPUT-') +button = sg.Button('Close') +window = sg.Window('Weight Converter', [[text_box, dropdown], [label], [button]]) + +while True: + event, values = window.read() + if event in [sg.WIN_CLOSED, 'Close']: + break + try: + value = float(values['-VALUE-']) + except ValueError: + continue + unit = values['-UNIT-'] + units = {'g': 0.001, 'kg': 1, 't': 1000} + lbs = value * units[unit] / 0.45359237 + window['-OUTPUT-'].update(value=f'{value} {unit} is {lbs:g} lbs.') +window.close() +
    -
    <Formatter> = logging.Formatter('<format>')          # Creates a Formatter.
    -<Handler> = logging.FileHandler(<path>, mode='a')    # Creates a Handler. Also `encoding=None`.
    -<Handler>.setFormatter(<Formatter>)                  # Adds Formatter to the Handler.
    -<Handler>.setLevel(<int/str>)                        # Processes all messages by default.
    -<Logger>.addHandler(<Handler>)                       # Adds Handler to the Logger.
    -<Logger>.setLevel(<int/str>)                         # What is sent to its/ancestor's handlers.
    -
    -
      -
    • Parent logger can be specified by naming the child logger '<parent>.<name>'.
    • -
    • If logger doesn't have a set level it inherits it from the first ancestor that does.
    • -
    • Formatter also accepts: pathname, filename, funcName, lineno, thread and process.
    • -
    • A 'handlers.RotatingFileHandler' creates and deletes log files based on 'maxBytes' and 'backupCount' arguments.
    • -
    -

    Creates a logger that writes all messages to file and sends them to the root's handler that prints warnings or higher:

    >>> logger = logging.getLogger('my_module')
    ->>> handler = logging.FileHandler('test.log', encoding='utf-8')
    ->>> formatter = logging.Formatter('%(asctime)s %(levelname)s:%(name)s:%(message)s')
    ->>> handler.setFormatter(formatter)
    ->>> logger.addHandler(handler)
    ->>> logging.basicConfig(level='DEBUG')
    ->>> logging.root.handlers[0].setLevel('WARNING')
    ->>> logger.critical('Running out of disk space.')
    -CRITICAL:my_module:Running out of disk space.
    ->>> print(open('test.log').read())
    -2023-02-07 23:21:01,430 CRITICAL:my_module:Running out of disk space.
    -

    #Scraping

    Scrapes Python's URL and logo from its Wikipedia page:

    # $ pip3 install requests beautifulsoup4
    -import requests, bs4, os, sys
    -
    -try:
    -    response   = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)')
    -    document   = bs4.BeautifulSoup(response.text, 'html.parser')
    -    table      = document.find('table', class_='infobox vevent')
    -    python_url = table.find('th', text='Website').next_sibling.a['href']
    -    logo_url   = table.find('img')['src']
    -    logo       = requests.get(f'https:{logo_url}').content
    -    filename   = os.path.basename(logo_url)
    -    with open(filename, 'wb') as file:
    -        file.write(logo)
    -    print(f'{python_url}, file://{os.path.abspath(filename)}')
    -except requests.exceptions.ConnectionError:
    -    print("You've got problems with connection.", file=sys.stderr)
    +import requests, bs4, os
    +
    +response   = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)')
    +document   = bs4.BeautifulSoup(response.text, 'html.parser')
    +table      = document.find('table', class_='infobox vevent')
    +python_url = table.find('th', text='Website').next_sibling.a['href']
    +logo_url   = table.find('img')['src']
    +logo       = requests.get(f'https:{logo_url}').content
    +filename   = os.path.basename(logo_url)
    +with open(filename, 'wb') as file:
    +    file.write(logo)
    +print(f'{python_url}, file://{os.path.abspath(filename)}')
     
    +

    Selenium

    Library for scraping websites with dynamic content.

    # $ pip3 install selenium
    +from selenium import webdriver
    +
    +<Drv> = webdriver.Chrome/Firefox/Safari/Edge()         # Opens a browser. Also <Drv>.quit().
    +<Drv>.get('<url>')                                     # Also <Drv>.implicitly_wait(seconds).
    +<El> = <Drv/El>.find_element('css selector', '<css>')  # '<tag>#<id>.<class>[<attr>="<val>"]'.
    +<list> = <Drv/El>.find_elements('xpath', '<xpath>')    # '//<tag>[@<attr>="<val>"]'.
    +<str> = <El>.get_attribute/get_property(<str>)         # Also <El>.text/tag_name.
    +<El>.click/clear()                                     # Also <El>.send_keys(<str>).
    +
    + + +

    XPath — also available in browser's console via '$x(<xpath>)':

    <xpath>     = //<element>[/ or // <element>]           # Child: /, Descendant: //, Parent: /..
    +<xpath>     = //<element>/following::<element>         # Next sibling. Also preceding/parent/…
    +<element>   = <tag><conditions><index>                 # `<tag> = */a/…`, `<index> = [1/2/…]`.
    +<condition> = [<sub_cond> [and/or <sub_cond>]]         # `and` is same as chaining conditions.
    +<sub_cond>  = @<attr>="<val>"                          # `.="<val>"` matches complete text.
    +<sub_cond>  = contains(@<attr>, "<val>")               # Is <val> a substring of attr's value?
    +<sub_cond>  = [//]<element>                            # Has matching child? Descendant if //.
    +
    +

    #Web

    Flask is a micro web framework/server. If you just want to open a html file in a web browser use 'webbrowser.open(<path>)' instead.

    # $ pip3 install flask
     import flask
     
    @@ -2219,7 +2233,7 @@ right = [[0.1, 0.60.1, 0.6, 0.8]]
    -

    Example

    For each point returns index of its nearest point ([0.1, 0.6, 0.8] => [1, 2, 1]):

    >>> points = np.array([0.1, 0.6, 0.8])
    +

    Example

    For each point returns index of its nearest point ([0.1, 0.6, 0.8] => [1, 2, 1]):

    >>> points = np.array([0.1, 0.6, 0.8])
      [ 0.1,  0.6,  0.8]
     >>> wrapped_points = points.reshape(3, 1)
     [[ 0.1],
    @@ -2252,17 +2266,17 @@ right = [[0.1,  0.6# Selects format based on the path extension.
     <Image>.show()                                    # Opens image in the default preview app.
     
    -
    <int/tuple> = <Image>.getpixel((x, y))            # Returns pixel's color.
    -<Image>.putpixel((x, y), <int/tuple>)             # Changes pixel's color.
    -<ImagingCore> = <Image>.getdata()                 # Returns a flattened view of all pixels.
    +
    <int/tuple> = <Image>.getpixel((x, y))            # Returns pixel's value (its color).
    +<Image>.putpixel((x, y), <int/tuple>)             # Updates pixel's value.
    +<ImagingCore> = <Image>.getdata()                 # Returns a flattened view of pixel values.
     <Image>.putdata(<list/ImagingCore>)               # Updates pixels with a copy of the sequence.
     <Image>.paste(<Image>, (x, y))                    # Draws passed image at specified location.
     
    <Image> = <Image>.filter(<Filter>)                # `<Filter> = ImageFilter.<name>([<args>])`
     <Image> = <Enhance>.enhance(<float>)              # `<Enhance> = ImageEnhance.<name>(<Image>)`
     
    -
    <array> = np.array(<Image>)                       # Creates a NumPy array from the image.
    -<Image> = Image.fromarray(np.uint8(<array>))      # Use <array>.clip(0, 255) to clip values.
    +
    <array> = np.array(<Image>)                       # Creates a 2d/3d NumPy array from the image.
    +<Image> = Image.fromarray(np.uint8(<array>))      # Use `<array>.clip(0, 255)` to clip values.
     

    Modes

    • 'L' - 8-bit pixels, greyscale.
    • @@ -2849,15 +2863,6 @@ ex.line(df, x='Date', y=#PySimpleGUI
      # $ pip3 install PySimpleGUI
      -import PySimpleGUI as sg
      -
      -layout = [[sg.Text("What's your name?")], [sg.Input()], [sg.Button('Ok')]]
      -window = sg.Window('Window Title', layout)
      -event, values = window.read()
      -print(f'Hello {values[0]}!' if event == 'Ok' else '')
      -
    -

    #Appendix

    Cython

    Library that compiles Python code into C.

    # $ pip3 install cython
     import pyximport; pyximport.install()
     import <cython_script>
    @@ -2926,7 +2931,7 @@ $ deactivate                  # Deactivates the activ
      
     
       
    - +
    diff --git a/parse.js b/parse.js index 5f31ca7..c68f636 100755 --- a/parse.js +++ b/parse.js @@ -38,9 +38,9 @@ const TOC = ' \'3. Syntax\': [Args, Inline, Import, Decorator, Class, Duck_Types, Enum, Exception],\n' + ' \'4. System\': [Exit, Print, Input, Command_Line_Arguments, Open, Path, OS_Commands],\n' + ' \'5. Data\': [JSON, Pickle, CSV, SQLite, Bytes, Struct, Array, Memory_View, Deque],\n' + - ' \'6. Advanced\': [Threading, Operator, Introspection, Metaprograming, Eval, Coroutine],\n' + - ' \'7. Libraries\': [Progress_Bar, Plot, Tables, Curses, Logging, Scraping, Web, Profile],\n' + - ' \'8. Multimedia\': [NumPy, Image, Animation, Audio, Pygame, Pandas, Plotly, PySimpleGUI]\n' + + ' \'6. Advanced\': [Threading, Operator, Match_Stmt, Introspection, Logging, Coroutines],\n' + + ' \'7. Libraries\': [Progress_Bar, Plots, Tables, Curses, GUIs, Scraping, Web, Profiling],\n' + + ' \'8. Multimedia\': [NumPy, Image, Animation, Audio, Synthesizer, Pygame, Pandas, Plotly]\n' + '}\n' + '
    \n'; @@ -105,18 +105,25 @@ const OS_RENAME = const STRUCT_FORMAT = '\'<n>s\''; -const TYPE = - '<class> = type(\'<class_name>\', <tuple_of_parents>, <dict_of_class_attributes>)'; - -const EVAL = - '>>> from ast import literal_eval\n' + - '>>> literal_eval(\'[1, 2, 3]\')\n' + - '[1, 2, 3]\n' + - '>>> literal_eval(\'1 + 2\')\n' + - 'ValueError: malformed node or string\n'; +const MATCH = + 'match <object/expression>:\n' + + ' case <pattern> [if <condition>]:\n' + + ' <code>\n' + + ' ...\n'; + +const MATCH_EXAMPLE = + '>>> from pathlib import Path\n' + + '>>> match Path(\'/home/gto/python-cheatsheet/README.md\'):\n' + + '... case Path(\n' + + '... parts=[\'/\', \'home\', user, *_],\n' + + '... stem=stem,\n' + + '... suffix=(\'.md\' | \'.txt\') as suffix\n' + + '... ) if stem.lower() == \'readme\':\n' + + '... print(f\'{stem}{suffix} is a readme file that belongs to user {user}.\')\n' + + '\'README.md is a readme file that belongs to user gto.\'\n'; const COROUTINES = - 'import asyncio, collections, curses, curses.textpad, enum, random\n' + + 'import asyncio, collections, curses, curses.textpad, enum, random, time\n' + '\n' + 'P = collections.namedtuple(\'P\', \'x y\') # Position\n' + 'D = enum.Enum(\'D\', \'n e s w\') # Direction\n' + @@ -144,7 +151,8 @@ const COROUTINES = 'async def human_controller(screen, moves):\n' + ' while True:\n' + ' key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e}\n' + - ' if d := key_mappings.get(screen.getch()):\n' + + ' ch = screen.getch()\n' + + ' if d := key_mappings.get(ch):\n' + ' moves.put_nowait((\'*\', d))\n' + ' await asyncio.sleep(0.005)\n' + '\n' + @@ -153,7 +161,8 @@ const COROUTINES = ' id_, d = await moves.get()\n' + ' x, y = state[id_]\n' + ' deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}\n' + - ' state[id_] = P((x + deltas[d].x) % W, (y + deltas[d].y) % H)\n' + + ' dx, dy = deltas[d]\n' + + ' state[id_] = P((x + dx) % W, (y + dy) % H)\n' + '\n' + 'async def view(state, screen):\n' + ' offset = P(curses.COLS//2 - W//2, curses.LINES//2 - H//2)\n' + @@ -161,16 +170,21 @@ const COROUTINES = ' screen.erase()\n' + ' curses.textpad.rectangle(screen, offset.y-1, offset.x-1, offset.y+H, offset.x+W)\n' + ' for id_, p in state.items():\n' + - ' screen.addstr(offset.y + (p.y - state[\'*\'].y + H//2) % H,\n' + - ' offset.x + (p.x - state[\'*\'].x + W//2) % W, str(id_))\n' + + ' screen.addstr(\n' + + ' offset.y + (p.y - state[\'*\'].y + H//2) % H,\n' + + ' offset.x + (p.x - state[\'*\'].x + W//2) % W,\n' + + ' str(id_)\n' + + ' )\n' + ' screen.refresh()\n' + ' await asyncio.sleep(0.005)\n' + '\n' + 'if __name__ == \'__main__\':\n' + - ' curses.wrapper(main)\n'; + ' start_time = time.perf_counter()\n' + + ' curses.wrapper(main)\n' + + ' print(f\'You survived {time.perf_counter() - start_time:.2f} seconds.\')\n'; const CURSES = - '# pip3 install windows-curses\n' + + '# $ pip3 install windows-curses\n' + 'import curses, os\n' + 'from curses import A_REVERSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_ENTER\n' + '\n' + @@ -814,8 +828,8 @@ function fixHighlights() { $(`code:contains(shutil.copy)`).html(SHUTIL_COPY); $(`code:contains(os.rename)`).html(OS_RENAME); $(`code:contains(\'s\')`).html(STRUCT_FORMAT); - $(`code:contains(\'\', , )`).html(TYPE); - $(`code:contains(ValueError: malformed node)`).html(EVAL); + $(`code:contains(match :)`).html(MATCH); + $(`code:contains(>>> match Path)`).html(MATCH_EXAMPLE); $(`code:contains(import asyncio, collections, curses, curses.textpad, enum, random)`).html(COROUTINES); $(`code:contains(import curses, os)`).html(CURSES); $(`code:contains(pip3 install tqdm)`).html(PROGRESS_BAR); diff --git a/web/script_2.js b/web/script_2.js index 0ea8c1b..2c1c57e 100644 --- a/web/script_2.js +++ b/web/script_2.js @@ -5,9 +5,9 @@ const TOC = ' \'3. Syntax\': [Args, Inline, Import, Decorator, Class, Duck_Types, Enum, Exception],\n' + ' \'4. System\': [Exit, Print, Input, Command_Line_Arguments, Open, Path, OS_Commands],\n' + ' \'5. Data\': [JSON, Pickle, CSV, SQLite, Bytes, Struct, Array, Memory_View, Deque],\n' + - ' \'6. Advanced\': [Threading, Operator, Introspection, Metaprograming, Eval, Coroutine],\n' + - ' \'7. Libraries\': [Progress_Bar, Plot, Tables, Curses, Logging, Scraping, Web, Profile],\n' + - ' \'8. Multimedia\': [NumPy, Image, Animation, Audio, Pygame, Pandas, Plotly, PySimpleGUI]\n' + + ' \'6. Advanced\': [Threading, Operator, Match_Stmt, Introspection, Logging, Coroutines],\n' + + ' \'7. Libraries\': [Progress_Bar, Plots, Tables, Curses, GUIs, Scraping, Web, Profiling],\n' + + ' \'8. Multimedia\': [NumPy, Image, Animation, Audio, Synthesizer, Pygame, Pandas, Plotly]\n' + '}\n'; const TOC_MOBILE = @@ -29,15 +29,14 @@ const TOC_MOBILE = ' Bytes, Struct, Array,\n' + ' Memory_View, Deque],\n' + ' \'6. Advanced\': [Threading, Operator,\n' + - ' Introspection,\n' + - ' Metaprograming, Eval,\n' + - ' Coroutine],\n' + + ' Match_Stmt, Introspection,\n' + + ' Logging, Coroutines],\n' + ' \'7. Libraries\': [Progress_Bar, Plot, Table,\n' + - ' Curses, Logging, Scraping,\n' + - ' Web, Profile],\n' + + ' Curses, GUIs, Scraping,\n' + + ' Web, Profiling],\n' + ' \'8. Multimedia\': [NumPy, Image, Animation,\n' + - ' Audio, Pygame, Pandas,\n' + - ' Plotly, PySimpleGUI]\n' + + ' Audio, Synthesizer,\n' + + ' Pygame, Pandas, Plotly]\n' + '}\n';