# Single argument to format method (replaces {}-specifier) 'answer = {}'.format(42) # Returns 'answer = 42' # Multiple positional arguments (replace {}-specifiers sequentially; number of arguments # must be equal to or greater than number of {}-specifiers, otherwise IndexError exception # is raised; extra arguments are ignored) '{} = {}'.format('answer', 42, 43) # Returns 'answer = 42''{} = {}'.format('answer',)# Raises IndexError exception # Identified positional arguments ('{0}' refers to first argument, '{1}' to second, etc.; # same argument can be referenced multiple times, and not all arguments need to be # referenced; if argument doesn't exist, IndexError exception is raised) '{1} + {1} = {0}'.format(4, 2, 8) # Returns '2 + 2 = 4''{1} + {1} = {0}'.format(4)# Raises IndexError exception # Identified named arguments (same argument can be referenced multiple times, and not all # arguments need to be referenced; if argument doesn't exist, KeyError exception is # raised) '{x} + {x} = {z}'.format(x=2, y=3, z=4) # Returns '2 + 2 = 4''{x} + {x} = {z}'.format(y=3, z=4)# Raises KeyError exception # Identified named arguments can be mixed with either identified or sequential positional # arguments, but ValueError exception will be raised if there are both identified and # sequential positional arguments '{x} + {x} = {1}'.format(8, 4, y=3, x=2) # Returns '2 + 2 = 4' '{x} + {x} = {}'.format(4, y=3, x=2) # Returns '2 + 2 = 4''{0} + {0} = {}'.format(2, 4)# Raises ValueError exception # Accessing indexed items and attributes of arguments '{[a][1].imag} {.stop}'.format({'a':[7, 8+9j]}, range(5)) # Returns '9.0 5' '{0[a][1].imag} {x.stop}'.format({'a':[7, 8+9j]}, x=range(5)) # Returns '9.0 5' # Minimum field width and left/center/right alignment (specified with '<'/'^'/'>'; # right is default for numbers, left for others, so this is different than when # formatting with the '%' operator) '{:3s}_{:3d}_{:7g}_{:3d}'.format('A', 6, -1e7, 1234) # Returns 'A _ 6_ -1e+07_1234' '{:>3s}_{:^3d}_{:<7g}'.format('A', 6, -1e7) # Returns ' A_ 6 _-1e+07 ' # Minimum field width and specified fill character (alignment must also be specified) '{:x<3s}_{:>>5d}'.format('A', -6) # Returns 'Axx_>>>-6' # Dynamic minimum field width (arguments are taken in the order of the '{' characters, so # this makes the order different than for the '%*' specifier when # formatting with the '%' operator) '{:{}s}_{:<{}d}'.format('A', 2, 6, 3) # Returns 'A _6 ' # Dynamic everything (same as above but now with all specifier parts (after colon) given as # arguments) '{:{}{}}_{:{}}'.format('A', 2, 's', 6, '<3d') # Returns 'A _6 ' '{0:{1}{2}}_{3:{4}}'.format('A', 2, 's', 6, '<3d') # Returns 'A _6 ' # Surrounding text (no whitespace needed) 'one{}all'.format(4) # Returns 'one4all' # '{{' and '}}' produce literal '{' and '}' '{{br{}s}}'.format('ace') # Returns '{braces}'
# Integer format ('{:d}' produces same result as '{}' but will raise ValueError exception # if argument is not an int (e.g. a float)) '{0} {0:d}'.format(-12) # Returns '-12 -12''{:d}'.format(-12.0)# Raises ValueError exception # Integer format with minimum field width and zero-padding (specified with '0' and field # width after ':'; nested '{}' is dynamic field width replaced with one of the arguments) '{:3d}_{:03d}_{:0{}d}'.format(-6, -7, 8, 3) # Returns ' -6_-07_008' # Integer format with forced sign (specified with '+'; '<' aligns left) '{:+d}_{:+d}_{:<+3d}_{:+03d}'.format(6, -7, 8, 9) # Returns '+6_-7_+8 _+09' # Integer format with forced room for sign (specified with ' '; '<' aligns left but leaves # room for sign) '{: d}_{: d}_{:< 3d}_{: 03d}'.format(6, -7, 8, 9) # Returns ' 6_-7_ 8 _ 09' # Integer format with minimum field width, left aligned sign, and right aligned number # (specified with '='; this is the default when using zero-padding; 'x' is an arbitrary # fill character) '{:=3d}_{:=+3d}_{:x=5d}'.format(-6, 7, -8) # Returns '- 6_+ 7_-xxx8' # Hexadecimal formats ('#' prepends '0x' or '0X') '{:x} {:x} {:X} {:04x} {:#x} {:#04X}'.format( 10, -11, 12, 13, 14, 15) # Returns 'a -b C 000d 0xe 0X0F' # Octal formats ('#' prepends '0o') '{:o} {:o} {:04o} {:#o} {:#05o}'.format( 8, -9, 10, 11, 12) # Returns '10 -11 0012 0o13 0o014' # Binary formats ('#' prepends '0b') '{:b} {:b} {:04b} {:#b} {:#05b}'.format(2, -3, 4, 5, 3) # Returns '10 -11 0100 0b101 0b011' # Integer format with thousands separator (specified with ',' or '_', the former is only # valid for 'd') '{:09,d} {:_d}'.format(6, 70000) # Returns '0,000,006 70_000' '{:_x} {:_o} {:_b}'.format(0x89abc, 0o34567, 31) # Returns '8_9abc 3_4567 1_1111''{:,x}'.format(1)# Raises ValueError exception'{:,o}'.format(1)# Raises ValueError exception'{:,b}'.format(1)# Raises ValueError exception
# Floating point formats (f/e/g never/always/sometimes uses exponential format) '{:f} {:e} {:g}'.format(1e-4, 2e-4, 3e-4) # Returns '0.000100 2.000000e-04 0.0003' '{:f} {:e} {:g}'.format(1e-5, 2e-5, 3e-5) # Returns '0.000010 2.000000e-05 3e-05' '{:f} {:e} {:g}'.format(1e5, 2e5, 3e5) # Returns '100000.000000 2.000000e+05 300000' '{:f} {:e} {:g}'.format(1e6, 2e6, 3e6) # Returns '1000000.000000 2.000000e+06 3e+06' '{0:f} {0:e} {0:g}'.format(float('-inf')) # Returns '-inf -inf -inf' '{0:f} {0:e} {0:g}'.format(float('nan')) # Returns 'nan nan nan' # Floating point format '{}' is similar to '{:g}' with a few exceptions '{0} {0:g}'.format(1.00) # Returns '1.0 1' '{0} {0:g}'.format(1.23456789012345678) # Returns '1.2345678901234567 1.23457' '{0} {0:g}'.format(1.2e15) # Returns '1200000000000000.0 1.2e+15' '{0} {0:g}'.format(1.2e16) # Returns '1.2e+16 1.2e+16' '{0} {0:g}'.format(1.2e-4) # Returns '0.00012 0.00012' '{0} {0:g}'.format(1.2e-5) # Returns '1.2e-05 1.2e-05' # Floating point formats with uppercase 'E' (F is same as f, except for inf and nan) '{:F} {:E} {:G}'.format(1e-5, 2e-5, 3e-5) # Returns '0.000010 2.000000E-05 3E-05' '{0:F} {0:E} {0:G}'.format(float('-inf')) # Returns '-INF -INF -INF' '{0:F} {0:E} {0:G}'.format(float('nan')) # Returns 'NAN NAN NAN' # Floating point percentage format (similar to format f, but multiplies by 100 and appends # '%') '{:%} {:%}'.format(1, 1e-5) # Returns '100.000000% 0.001000%' # Floating point formats f/e/% with maximum number of digits after '.' (default 6) '{:.1f} {:.1e} {:.1%}'.format(11.16, 22.6, .3336) # Returns '11.2 2.3e+01 33.4%' # Floating point format g with maximum total number of significant digits (default 6) '{:.2g} {:.2g} {:.2g} {:.2g}'.format( 11.6, 2.26, .336, 446) # Returns '12 2.3 0.34 4.5e+02' # Floating point formats with minimum field width and zero-padding (specified with '0' and # field width before '.') '{:4.1f}_{:04.1f}_{:08.1e}_{:04.2g}'.format( 0.06, 1.16, 2.26, 3.36) # Returns ' 0.1_01.2_02.3e+00_03.4' # Floating point formats with dynamic widths (nested '{}' is replaced with next argument) '{:0{}.{}f}_{:0{}.{}e}_{:0{}.{}g}'.format(1.16, 4, 1, 2.26, 8, 1, 3.36, 4, 2) # Returns '01.2_02.3e+00_03.4' # Floating point formats with forced sign (specified with '+') '{:+.1f}_{:+.1e}_{:+.2g}'.format(1.16, 2.26, 3.36) # Returns '+1.2_+2.3e+00_+3.4' '{:+.1f}_{:+.1e}_{:+.2g}'.format(-1.16, -2.26, -3.36) # Returns '-1.2_-2.3e+00_-3.4' # Floating point formats with forced room for sign (specified with ' ') '{: .1f}_{: .1e}_{: .2g}'.format(1.16, 2.26, 3.36) # Returns ' 1.2_ 2.3e+00_ 3.4' '{: .1f}_{: .1e}_{: .2g}'.format(-1.16, -2.26, -3.36) # Returns '-1.2_-2.3e+00_-3.4' # Alternate floating point formats (specified with '#' which always includes decimal point # and for format g also zeros); note that floating point formats accept int and complex # arguments '{:#.0f} {:#.0e} {:#g}'.format(1+0j, 2, 3) # Returns '1.+0.j 2.e+00 3.00000' '{:.0f} {:.0e} {:g}'.format(1+0j, 2, 3) # Returns '1+0j 2e+00 3' # Floating point format with thousands separator (specified with ',' or '_') '{:09,.0f} {:09,.0e} {:,g}'.format(6, 7, 8e4) # Returns '0,000,006 0,007e+00 80,000' '{:09_.0f} {:09_.0e} {:_g}'.format(6, 7, 8e4) # Returns '0_000_006 0_007e+00 80_000'
# String format ('{:s}' produces same result as '{}' but will raise ValueError exception if # argument is not a string '{0} {0:s}'.format('ABC') # Returns 'ABC ABC''{:s}'.format(42)# Raises ValueError exception # String format with forced conversion of argument to string (!s/r/a uses # str()/repr()/ascii() conversion) '{!s:s}'.format(42) # Returns '42' '{0!s} {0!r} {0!a}'.format('rosé') # Returns "rosé 'rosé' 'ros\\xe9'" # String format with maximum field width '{:.3s}_{:.3s}'.format('A', 'ABCD') # Returns 'A_ABC' # String format with minimum and maximum field width (maximum is applied first, and may be # smaller than minimum; '>' aligns right) '{:>2.3s}_{:2.3s}_{:3.2s}'.format('A', 'ABCD', 'ABC') # Returns ' A_ABC_AB ' # String format with dynamic minimum/maximum field width (nested '{}' is replaced with next # argument) '{:{}s}_{:.{}s}_{:{}.{}s}'.format('A', 3, 'ABC', 2, 'ABC', 3, 2) # Returns 'A _AB_AB '
# Single-character format (with or without minimum field width); raises ValueError if # argument is not an int '{:c}_{:3c}'.format(65, 0xe9) # Returns 'A_ é''{:c}'.format('A')# Raises ValueError exception'{:c}'.format(65.0)# Raises ValueError exception
# Misc initializations for use below k = 1 n = 4 x = 5+6j # F-strings are prefixed with 'f' or 'F' (no difference) and support same formatting as # str.format method f'{n:03d} {x.real:0{n}.{k}f} {x.imag!s:.2s}' # Returns '004 05.0 6.' F'{x:-^{k}{n}}' # Returns '----(5+6j)----' # Arbitrary expressions are allowed in f-strings (unlike in str.format) f'There {"is" if k==1 else "are"} {k} item{"s"[:k!=1]}' # Returns 'There is 1 item' f'There {"is" if n==1 else "are"} {n} item{"s"[:n!=1]}' # Returns 'There are 4 items' # But backslashes are not allowed in the expression part, only in the format part (and # elsewhere in the string); chr() is one way to get around this limitationf'{"\t"}'# SyntaxError due to '\' f'{"A"+chr(92)+"B":\t^7}\n' # Returns '\t\tA\\B\t\t\n' # The expressions themselves can be included in the result (specified with '=') f'{n=},{ n * 2 = :+d}' # Returns 'n=4, n * 2 = +8'
r = x if a else y # Assigns x to r if a is True (y is not evaluated), # otherwise assigns y to r (x is not evaluated) r = x if a \ else y if b \ else z # (Note: backslashes used to break single line)
import re # Imports regular expression module # Compile and match rs1 = r'(\w)(.*?)([0-5](Z)?)' # Assigns a (raw) string containing a regular expression rc1 = re.compile(rs1) # Compiles regular expr for faster repeated execution type(rc1) # Returns class re.Pattern isinstance(rc1, re.Pattern) # Returns Truere.compile(r'(')# Raises re.error exception due to bad regular expression s1 = 'abc950' # Some string to search for matches m1 = re.match(rs1, s1) # Finds a match at start of string s1, and returns # a match object - or None if no match was found m1 = rc1.match(s1) # Same as above, but uses compiled regular expression object if m1: # If a match was found, then... print(m1.group()) # Prints 'abc95' - the part of s1 matched by rs1 print(m1[0]) # Prints 'abc95' (same as above) print(m1.group(1)) # Prints 'a' - captured by 1st '()' in rs1 print(m1[1]) # Prints 'a' (same as above) print(m1.group(3, 2)) # Prints "('5', 'bc9')" - from 3rd & 2nd '()' in rs1 print(m1.groups()) # Prints "('a', 'bc9', '5', None)" - from all '()' in rs1 # Note: non-matching '()' returns None print(m1.start()) # Prints 0 - start index of match in s1 print(m1.end()) # Prints 5 - end index + 1 of match in s1 print(m1.span()) # Prints '(0, 5)' - start and end of match print(m1.start(2)) # Prints 1 - start index of 2nd '()' capture in s1 print(m1.end(2)) # Prints 4 - end index + 1 of 2nd '()' capture in s1 print(m1.span(2)) # Prints '(1, 4)' - start and end of 2nd '()' # Fullmatch m1 = rc1.fullmatch(s1) # Like rc1.match() but finds a match to whole string s1 print(m1.group()) # Prints 'abc950' - the part of s1 matched by rs1 print(m1.groups()) # Prints "('a', 'bc95', '0', None)" - from all '()' in rs1 # Search s2 = '.20 391Z' # A new string to search for matches m2 = rc1.search(s2) # Finds first match in string s2 if m2: print(m2.groups()) # Prints "('2', '', '0', None)" - from all '()' in rs1 m2 = rc1.search(s2, m2.end()) # Finds first match in string s2 starting from previous end if m2: print(m2.groups()) # Prints "('3', '9', '1Z', 'Z')" - from all '()' in rs1 # Finditer ri1 = rc1.finditer(s2) # Returns an iterator type(ri1) # Returns class with name 'callable_iterator' print(next(ri1).groups()) # Prints "('2', '', '0', None)" print(next(ri1).groups()) # Prints "('3', '9', '1Z', 'Z')" # Findall print(rc1.findall(s2)) # Prints "[('2', '', '0', ''), ('3', '9', '1Z', 'Z')]" # Note: non-matching '()' returns '' (not None) rs3 = r'\d\d' # String containing regular expression with no '()' s3 = ' 12.345-6789a' # A new string to search for matches print(re.findall(rs3, s3)) # Prints "['12', '34', '67', '89']" # Split print(re.split(rs3, s3)) # Prints "[' ', '.', '5-', '', 'a']" # Sub and subn (substitution) print(re.sub(rs3, 'xy', s3)) # Prints ' xy.xy5-xyxya' print(re.subn(rs3, 'xy', s3)) # Prints "(' xy.xy5-xyxya', 4)" (4 substitutions) print(rc1.sub(r'_\1_\g<1>4_', s1)) # Prints '_a_a4_0' (\1 = \g<1> = 1st captured group) print(rc1.sub(r'(\g<0>)', s1)) # Prints '(abc95)0' (\g<0> = whole match) def f(m): return m.group(1).upper() # Function that returns replacement string for a match print(rc1.sub(f, s1)) # Prints 'A0' (calls function f for each match)
Start/end indices may be supplied to compiled regular expression object methods to restrict their operation to a portion of the input string.
WARNING: ONLY compiled object methods (e.g. rc1.match()) support start/end index parameters! If used with module functions (e.g. re.match()), these parameters may be interpreted as flags and you may not get an error - just strange behavior!
import re rs1 = r'(\w)(.*?)([0-5](Z)?)' # Assigns a (raw) string containing a regular expression rc1 = re.compile(rs1) # Compiles regular expr s1 = 'abc950' m1 = rc1.match(s1, 2) # Finds a match at start of string s1[2:] if m1: # If a match was found, then... print(m1.groups()) # Prints "('c', '9', '5', None)" - from all '()' in rs1 print(rc1.match(s1, 1, 4)) # Prints 'None' because rs1 does not match s1[1:4] ('bc9')
Flags may be supplied to regular expression module functions to modify the behavior of a regular expression.
WARNING: ONLY module functions (e.g. re.match()) support flags! If used with compiled object methods (e.g. rc1.match()), the flags may be interpreted as indexes and you may not get an error - just strange behavior!
import re # Flags: # re.I (or re.IGNORECASE): case-insensitive matching; # re.M (or re.MULTILINE): '^' and '$' match start/end of each line within string; # re.S (or re.DOTALL): '.' matches also newline rs4 = r'^hi.\w+$' rc4 = re.compile(rs4, re.I|re.M|re.S) # Applies flags I, M, and S to rs4 in rc4 s4 = 'Hi\nHo\nHi\nHUM' print(rc4.findall(s4)) # Prints "['Hi\nHo', 'Hi\nHUM']" # Flags: # re.X (or re.VERBOSE): extra whitespace and comments allowed in regular expression rs5 = r""" # This is a verbose regular expression string (these comments are inside it) ( # Start of 1st capturing group \w # Word character ) # End of 1st capturing group ( # Start of 2nd capturing group .*? # Minimal sequence of 0 or more characters except \n ) # End of 2nd capturing group ( # Start of 3rd capturing group [0-5] # Digit in range 0-5 ( # Start of 4th capturing group Z # 'Z' character ) # End of 4th capturing group ? # 4th group is optional ) # End of 3rd capturing group """ m1 = re.match(rs5, 'abc950', re.X) # Applies flag X to rs5; returns the same result as # re.match(r'(\w)(.*?)([0-5](Z)?)', 'abc950') - see earlier # Flags: # (These character set flags cannot be combined with each other) # re.A (or re.ASCII): \d, \s, \w classes are restricted to ASCII characters (this also # affects \b, \B, \D, \S, \W); # this is the default for bytes-type regular expressions; # re.L (or re.LOCALE): \w class and re.I flag depend on the current locale (discouraged); # only allowed for bytes-type regular expressions; # re.U (or re.UNICODE): \d, \s, \w classes follow unicode rules (this also affects # \b, \B, \D, \S, \W); # this is the default for str-type regular expressions; # only allowed for str-type regular expressions re.search(r'\d\s\w', '३\u2003Ω 3 z')[0] # Returns '३\u2003Ω' re.search(r'\d\s\w', '३\u2003Ω 3 z', re.A)[0] # Returns '3 z' # Flags: # re.NOFLAG: no effect; convenient name for the value 0 which represents no flagsThere is also a DEBUG flag which prints detailed - but somewhat cryptic - information about the regular expression:
import re # Flags: # re.DEBUG: debug info is printed re.match(r'\w+', 'ab', re.DEBUG) # Returns 'ab' and prints the debug info below
MAX_REPEAT 1 MAXREPEAT IN CATEGORY CATEGORY_WORD 0. INFO 4 0b0 1 MAXREPEAT (to 5) 5: REPEAT_ONE 9 1 MAXREPEAT (to 15) 9. IN 4 (to 14) 11. CATEGORY UNI_WORD 13. FAILURE 14: SUCCESS 15: SUCCESS
import re # Imports regular expression module # Prepare a sequence of compiled regular expressions and associated functions; # each function converts the matched string to a token token_matchers = [(func, re.compile(regx)) for func, regx in ( (float, r'\s*((?:\d+\.\d*|\.\d+)(?:[eE][-+]?\d+)?)\s*'), (int, r'\s*(\d+)\s*'), (str, r'\s*(\*\*|\W)\s*'))] # Define a function which parses a string and returns a list of found tokens def get_tokens(in_str): tokens = [] pos = 0 # Sets matching position to start of string while pos < len(in_str): # Repeats while not at end of string for func, regx in token_matchers: # Tries each regular expr in turn if m := regx.match(in_str, pos): # When a regular expr matches at pos: tokens.append(func(m.group(1))) # Uses func to convert matched string to # token and adds it to list of tokens pos = m.end() # Moves position to next char after match break # Ends for-loop else: # If for-loop found nothing, then raise ValueError(f'Bad token: {in_str[pos:]!r}') # raises exception return tokens # Returns list of tokens to caller of # get_tokens get_tokens('23*(.45 + -6.7e-2)') # Returns [23, '*', '(', 0.45, '+', '-', 0.067, ')']
# Basic combinations r'XY' # X then Y; binds tighter than X|Y but looser than quantifiers; # r'abc' matches 'abc' in 'abcd', but not 'acb' r'X|Y' # X or Y; # r'ab|c|d' matches 'ab' in 'abcd', 'c' in 'cd', 'd' in 'de', but not 'ac'
# Character classes r'.' # Any single character - except newline if not in dot-all mode; # r'.' matches 'a' in 'ab', ' ' in ' a', '\r' in '\r', but not '\n'; # re.match(r'.', '\n', re.DOTALL)[0] returns '\n' r'\d' # Any digit character (as defined by o.isdecimal(), see section # Container Object Attributes); # r'\d' matches '7' in '78', '\u0e52' in '\u0e52', but not '\u00b2' r'\D' # Any non-digit character; # r'\D' matches 'a' in 'ab', '\n' in '\n', but not '7' r'\s' # Any whitespace character; # r'\s' matches ' ', '\t', '\n', '\r', '\f', '\v', but not '.' r'\S' # Any non-whitespace character; # r'\S' matches '.', but not ' ', '\t', '\n', '\r', '\f', '\v' r'\w' # Any word character (as defined by o.isalnum() plus '_', see section # Container Object Attributes); # r'\w' matches 'a', 'A', '7', '_', '\u00b2', but not ' ', '.', '-' r'\W' # Any non-word character; # r'\W' matches ' ', '.', '-', but not 'a', 'A', '7', '_', '\u00b2' r'[XYA-C]' # Any single character that is X, Y, or in range A-C; # r'[-a\]]' matches '-' in '-4', 'a' in 'ab', ']' in ']a', but not 'b'; # r'[a-c6-8]' matches 'b' in 'bc', 'c' in 'cd', '7' in '78', but not 'd'; # r'[\d\s]' matches '7' in '78', ' ' in ' a', but not 'a'; r'[^XYA-C]' # Any single character that is NOT X, Y, or in range A-C; # r'[^a-c3]' matches 'd' in 'de', '\n' in '\n', but not 'a', 'b', 'c', or '3'
# Exact quantifier r'X{3}' # Sequence of 3 Xs; # r'ab{3}' matches 'abbb' in 'abbbbb', but not 'abb' # Greedy quantifiers r'X?' # Largest sequence of 0 or 1 X; # r'ab?' matches 'a' in 'a', 'ab' in 'abbb' r'X*' # Largest sequence of 0 or more Xs; # r'ab*' matches 'a' in 'a', 'abbb' in 'abbb' r'X+' # Largest sequence of 1 or more Xs; # r'ab+' matches 'ab' in 'ab', 'abbb' in 'abbb', but not 'a' r'X{2,}' # Largest sequence of 2 or more Xs; # r'ab{2,}' matches 'abb' in 'abb', 'abbbb' in 'abbbb', but not 'ab' r'X{,3}' # Largest sequence of 0 to 3 Xs; # r'ab{,3}' matches 'a' in 'a', 'abbb' in 'abbbb' r'X{2,3}' # Largest sequence of 2 to 3 Xs; # r'ab{2,3}' matches 'abb' in 'abb', 'abbb' in 'abbbb', but not 'ab' # Minimal quantifiers r'X??' # Smallest sequence of 0 or 1 X; # r'ab??' matches 'a' in 'abbb'; # r'ab??c' matches 'ac' in 'ac', 'abc' in 'abc', but not 'abbc' r'X*?' # Smallest sequence of 0 or more items; # r'ab*?' matches 'a' in 'abbb'; # r'ab*?c' matches 'ac' in 'ac', 'abbbc' in 'abbbc' r'X+?' # Smallest sequence of 1 or more items; # r'ab+?' matches 'ab' in 'abbb'; # r'ab+?c' matches 'abc' in 'abc', 'abbbc' in 'abbbc', but not 'ac' r'X{2,}?' # Smallest sequence of 2 or more Xs; # r'ab{2,}?' matches 'abb' in 'abbb'; # r'ab{2,}?c' matches 'abbc' in 'abbc', 'abbbc' in 'abbbc', but not 'abc' r'X{,3}?' # Smallest sequence of 0 to 3 Xs; # r'ab{,3}?' matches 'a' in 'abbb'; # r'ab{,3}?c' matches 'ac' in 'ac', 'abbbc' in 'abbbc', but not 'abbbbc' r'X{2,3}?' # Smallest sequence of 2 to 3 Xs; # r'ab{2,3}?' matches 'abb' in 'abbb'; # r'ab{2,3}?c' matches 'abbc' in 'abbc', 'abbbc' in 'abbbc', but not 'abc' or # 'abbbbc' # Greedy and possessive quantifiers r'X?+' # Largest sequence of 0 or 1 X with no back-tracking; # r'ab?+' matches 'a' in 'a', 'ab' in 'abbb'; # r'ab?+b' matches 'abb' in 'abb', but not 'ab' r'X*+' # Largest sequence of 0 or more Xs with no back-tracking; # r'ab*+' matches 'a' in 'a', 'abbb' in 'abbb'; # r'ab*+b' does not match anything r'X++' # Largest sequence of 1 or more Xs with no back-tracking; # r'ab++' matches 'ab' in 'ab', 'abbb' in 'abbb', but not 'a'; # r'ab++b' does not match anything r'X{2,}+' # Largest sequence of 2 or more Xs with no back-tracking; # r'ab{2,}+' matches 'abb' in 'abb', 'abbb' in 'abbb', but not 'ab'; # r'ab{2,}+b' does not match anything r'X{,3}+' # Largest sequence of 0 to 3 Xs with no back-tracking; # r'ab{,3}+' matches 'a' in 'a', 'abbb' in 'abbbb'; # r'ab{,3}+b' matches 'abbbb' in 'abbbb', but not 'abbb' r'X{2,3}+' # Largest sequence of 2 to 3 Xs with no back-tracking; # r'ab{2,3}+' matches 'abb' in 'abb', 'abbb' in 'abbbb', but not 'ab'; # r'ab{2,3}+b' matches 'abbbb' in 'abbbb', but not 'abbb'
# Zero-width assertions (consuming no characters) r'^' # Matches start of string (or after each newline in multiline mode); # re.findall(r'^\d', '23\n4') returns ['2']; # re.findall(r'^\d', '23\n4', re.MULTILINE) returns ['2', '4'] r'\A' # Matches start of string (regardless of multiline mode); # re.findall(r'\A\d', '23\n4') returns ['2']; # re.findall(r'\A\d', '23\n4', re.MULTILINE) returns ['2'] r'$' # Matches end of string or before newline at end of string (or before each # newline in multiline mode); # both r'\n$' and r'$\n' match '\n'; # re.findall(r'$', '\n') returns ['', '']; # re.findall(r'\d$', '2\n34') returns ['4']; # re.findall(r'\d$', '2\n34', re.MULTILINE) returns ['2', '4'] r'\Z' # Matches end of string (regardless of multiline mode); # r'\n\Z' matches '\n', whereas r'\Z\n' matches nothing; # re.findall(r'\Z', '\n') returns ['']; # re.findall(r'\d\Z', '2\n34') returns ['4']; # re.findall(r'\d\Z', '2\n34', re.MULTILINE) returns ['4'] r'\b' # Matches start or end of word (\w characters); # r'\b.+?\b' matches 'a2_Ω' in 'a2_Ω,a'; # re.findall(r'\b.+?\b', 'A2! _Ω') returns ['A2', '! ', '_Ω'] r'\B' # Matches anywhere but start or end of word (\w characters); # r'.\B.+?\B' matches 'a2,_ Ω' in 'a2,_ Ωa'; # re.findall(r'\B.+?\B', 'A2! _Ω') returns ['2!', ' _'] r'(?=X)' # Positive lookahead assertion matching current position if a match for X # starts at this position; # r'ab(?=cd)' matches 'ab' in 'abcd', but not 'abdc' r'(?!X)' # Negative lookahead assertion matching current position if no match for X # starts at this position; # r'ab(?!cd)' matches 'ab' in 'abdc', but not 'abcd' r'(?<=X)' # Positive lookbehind assertion matching current position if a match for X # ends at this position; # re.findall(r'(?<=a).', 'aabacd') returns ['a', 'b', 'c'] r'(?<!X)' # Negative lookbehind assertion matching current position if no match for X # ends at this position; # re.findall(r'(?<!a).', 'aabacd') returns ['a', 'a', 'd']
# Groups r'(X)' # Capturing group matching X; # r'(.*?)(\d)' matches 'ab2' in 'ab23' and captures 'ab' and '2', i.e.: # re.match(r'(.*?)(\d)', 'ab23').groups() returns ('ab', '2'); # groups may be nested and capture in the order of their '(', e.g.: # re.match(r'((a(b))c)d', 'abcd').groups() returns ('abc', 'ab', 'b'); # if a group doesn't contribute to the match, it captures None, e.g.: # re.match(r'a(.)*|(.)', 'a').groups() returns (None, None); # if a group is repeated, only the last match is captured, e.g.: # re.match(r'(\s)*(\d)*(.)*', '2ab').groups() returns (None, '2', 'b'); # note: putting the quantifier inside the group is entirely different: # re.match(r'(\s*)(\d*)(.*)', '2ab').groups() returns ('', '2', 'ab') r'(?P<name>X)' # Named capturing group matching X; # r'(?P<first>\d*)' captures '23' in '23a' and names it 'first', i.e.: # re.match(r'(?P<first>\d*)', '23a')['first'] returns '23'; # re.match(r'(?P<first>\d*)', '23a')[1] returns '23' r'(?:X)' # (Non-capturing) group matching X; # r'(?:aa)+' matches 'aaaa' in 'aaaaa' and captures nothing, i.e.: # re.match(r'(?:aa)+', 'aaaaa')[0] returns 'aaaa'; # re.match(r'(?:aa)+', 'aaaaa').groups() returns () r'(?auLimsx-imsx:X)' # Flagged group matching X; each letter corresponds to a flag to be # enabled (or disabled if preceded by '-'), e.g. (?i:X) enables # re.I for expression X, and (?-i:X) disables re.I for X; # r'(?ims:.*?^b.c$)' matches 'a\nB\nc' in 'a\nB\nc\nd'; # r'(?x: a b c)' matches 'abc' in 'abcd'; # r'(?a:\w+)' matches 'a2' in 'a2Ω'; # re.match(r'(?u:\w+)', 'a2Ω', re.A)[0] returns 'a2Ω' r'(?>X)' # Atomic group matching X (no back-tracking to points inside X once X is # matched); # r'ab?b' matches both 'ab' and 'abb', but - like r'ab?+b' - r'a(?>b?)b' # matches 'abb' but not 'ab'; r'(?#X)' # Comment group matching nothing (X is the comment); # r'a(?#That was an a, now comes a b)b' matches 'ab'
# Backreferences (referencing results of previous capturing groups) r'\1' # Matches what 1st capturing group matched; # r'(.)(.)\2\1' matches 'abba', but not 'abab' r'(?P=name)' # Matches what named capturing group matched; # r'(?P<first>\d+)(?P=first)' matches '123123', but not '123456' r'(?(1)X)' # Matches X if 1st capturing group matched something, otherwise matches # current position; # r'(\()?(\[)?a(?(2)\])(?(1)\))' matches '([a])', '(a)', '[a]', 'a', but # not '([a]', '([a)', '([a)]' r'(?(1)X|Y)' # Matches X if 1st capturing group matched something, otherwise matches Y; # r'(?>(1)|\d+) (?(1)mouse|mice)' matches '1 mouse' and '3 mice', but not # '1 mice' or '3 mouse' r'(?(name)X)' # Matches X if named capturing group matched something, otherwise matches # current position; # r'(?P<round>\()?(?P<square>\[)?a(?(square)\])(?(round)\))' behaves like # r'(\()?(\[)?a(?(2)\])(?(1)\))' above r'(?(name)X|Y)' # Matches X if named capturing group matched something, otherwise matches Y; # r'(?>(?P<n>1)|\d+) (?(n)mouse|mice)' behaves like # r'(?>(1)|\d+) (?(1)mouse|mice)' above
# Other r'(?auLimsx)X' # Flags to be applied to entire regular expression X (must be placed at # start of expression); each letter corresponds to a flag to be enabled, # e.g. (?i)X enables re.I for expression X; # r'(?ims).*?^b.c$' matches 'a\nB\nc' in 'a\nB\nc\nd'; # r'(?x) a b c' matches 'abc' in 'abcd'; # r'(?a)\w+' matches 'a2' in 'a2Ω'; # (?a), (?u), (?L) are not allowed to override re.A, re.U, re.L given as # argument to e.g. re.match
# List comprehension [x * y for x in [1, -1] for y in range(4) if y > x] # Returns list [2, 3, 0, -1, -2, -3] # Dictionary comprehension {x: y for x, y in ((0, 3), (1, 4), (2, 3))} # Returns dict {0: 3, 1: 4, 2: 3} # Set comprehension {x**2 for x in range(4)} # Returns set {0, 1, 4, 9} # Tuple comprehension - has no dedicated syntax, # but a generator expression can be passed to tuple() tuple(chr(x) for x in range(65, 67)) # Returns tuple ('A', 'B')
g = (x for x in 'hello' if x < 'm') # Assigns a generator object prepared to produce # the sequence 'h', 'e', 'l', 'l'; # generator objects are also iterators type(g) # Returns class types.GeneratorType with name # 'generator' next(g) # Returns 'h', i.e. next (first) item next(g) # Returns 'e', i.e. next item list(g) # Returns ['l', 'l'], i.e. all remaining items; # g is useless now and can't be restarted list(g) # Returns []; no more items next(g) # Raises StopIteration exception; no more items g = (x**2 for x in range(5)) # Assigns a new generator object for i in g: # Assigns each generated item to i in turn if i == 9: # If item is 9, then... try: i = next(g) # ... skip to next item if any except StopIteration: i = 'end' # If no more, set i to 'end' (will not happen) print(i) # Prints '0', '1', '4', '16', one by oneSee also generator functions.
f = lambda x, y: x + y # Assigns a lambda expression (anonymous function) that takes 2 # arguments and returns the sum of these; this is basically the # same as 'def f(x, y): return x + y', except a lambda doesn't # need to be bound to a name, and is limited to one expression type(f) # Returns class types.FunctionType with name 'function' f(3,5) # Calls lambda expression bound to name f; returns 8 map(lambda x: x**2, range(4)) # Applies lambda to range and returns iterator for sequence # 0, 1, 4, 9 g = lambda x: lambda y: x + y # Binds g to a function which returns a function g(3) # Returns a function which adds 3 to stuff g(3)(4) # Returns 7
# Function definitions def f1(x, y=0): # Arguments may have default values (calculated only once when # 'def' is executed, so beware if using mutable values, e.g. y=[]) # Here's a docstring for this function: """This docstring is accessible via f1.__doc__ """ print(x, y) if x > y: return # Exits function with return value None if x < y: return y, x # Exits function with expression list as return value # Return value is None if function ends without 'return' def f2(x, *args, **keyargs): # Special * and ** syntax explained in function body: print(x, end=' ') # Prints first argument (and a space, no newline) print(args, end=' ')# Prints a tuple of all remaining unnamed arguments print(keyargs) # Prints a dict of all remaining named arguments def f3(x, /, y, *, z): # Special / and * syntax: parameters before / are positional only # (f3(5,...) is OK, but f3(x=5,...) is not); parameters between / # and * may be positional or named (f(5,6,...) and f(5,y=6,...) are # both OK); parameters after * are named only (f(5,6,z=7) is OK, # but f(5,6,7) is not) print(x, y, z) # Prints the 3 arguments def f4(x): def g(y): # Defines an inner function (function object) inside f4 return x + y # Function g uses object referenced by f4's local x, so keeps that # object in existence after f4 returns return g # Function f4 returns the created function object (in this case a # 'closure' because it keeps data (x) in an outer scope (f4) alive # even though that scope has ended when the function is later # called) type(f1) # Returns class types.FunctionType with name 'function' (functions # are also objects) # Function calls f1(3) # Calls f1 which prints '3 0' and returns None print(f1(3,5)) # Calls f1 which prints '3 5', then prints result '(5, 3)' f1(y=5, x=3) # Passes named arguments to f1 which prints '3 5' a1 = [3, 5] f1(a1, [4]) # Passes a1 and [4] to f1 which prints '[3, 5] [4]' and returns # ([4], [3, 5]) because [3,5] is lexicographically smaller than [4] f1(*a1) # Passes each item of a1 as an argument to f1 which prints '3 5' # and returns (5, 3) d = {'y':5, 'x':3} # Creates a dictionary f1(**d) # Passes values of d as named arguments to f1 which prints '3 5' f1(*[3], **{'y':5}) # Passes items of list and values of dictionary as arguments # to f1 which prints '3 5' f2(3) # Prints '3 () {}' f2(3, 4, 5, a=6) # Prints "3 (4, 5) {'a': 6}" f3(5, 6, z=7) # Prints "5 6 7" f3(5, z=7, y=6) # Prints "5 6 7" f3(5, 6, 7) # Raises TypeError (parameter z requires a named argument) f3(x=5, y=6, z=7) # Raises TypeError (parameter x requires an unnamed argument) add10 = f4(10) # Calls f4 which returns new function that adds 10 to its argument print(add10(9)) # Calls function add10 and prints return value '19' f2 = f1 # (Re-)binds name f2 to same function object as f1 f2(3) # Calls f2 (now same as f1) which prints '3 0' and returns NoneNotes:
# Using 'yield' def f1(m): # The yield statement in the body of this function causes it to # return a generator object, which produces a new item whenever # the yield statement is reached; # generator objects are also iterators print('1st') # Prints '1st' when first item is requested from generator (first # call to next()) end = 5 * m + 1 for x in range(m, end, m): yield x # Returns x to caller of next() on generator object, and # stops execution until next() is called again, whereafter # execution continues after yield statement # When the loop ends and f1 returns, the generator object raises a # StopIteration exception g = f1(3) # Assigns the generator object returned from function f1 type(g) # Returns class types.GeneratorType with name 'generator' next(g) # Prints '1st' and returns 3, i.e. next (first) item of generator next(g) # Returns 6, i.e. next item list(g) # Returns [9, 12, 15], i.e. all remaining items; # g is useless now and can't be restarted (but f1 can be called # again to get a new generator object) list(g) # Returns []; no more items next(g) # Raises StopIteration exception; no more items
# Using 'yield from' def f2(m): # Identical to f1 above, but uses 'yield from' instead of 'yield' print('1st') end = 5 * m + 1 yield from range(m, end, m) # Returns items one by one from iterable to caller of # next(), stopping execution each time g = f2(3) # Assigns the generator object returned from function f2 next(g) # Prints '1st' and returns 3, i.e. next (first) item of generator list(g) # Returns [6, 9, 12, 15], i.e. all remaining items
# Using 'send' and return value from 'yield', # and assigning a value to StopIteration exception def f3(m): # Identical to f1 above, but allows values to be sent into the # generator at any time (and returned from yield) to modify its # behavior while iterating over it; also sets a StopIteration value print('1st') end = 5 * m + 1 x = m while x < end: y = yield x # Returns x to caller of next() or send(), stops execution until # next() or send() is called again, then sets y to None (for # next()) or to argument of send(), and resumes execution here x = x + m if y is None else y # Updates next item or uses value provided by send() return x # Puts next x (which would have been generated if iteration hadn't # ended) into value attribute of StopIteration exception g = f3(3) # Assigns the generator object returned from function f3 g.send(4) # Raises TypeError exception; can't send non-None value to # just-started generator (no yield to receive it) next(g) # Prints '1st' and returns 3, i.e. next (first) item of generator next(g) # Returns 6, i.e. next item g.send(4) # Sends 4 into generator, which sets next item to 4 and returns 4 next(g) # Returns 7, i.e. next item next(g) # Returns 10, i.e. next item next(g) # Returns 13, i.e. next item try: next(g) # Raises StopIteration exception due to no more items except StopIteration as e: # Catches StopIteration exception and assigns it to e print(e.value) # Prints '16' (StopIteration value returned at end of f3) print(e.args) # Prints '(16,)' (more general way to get arguments used to # create an exception; e.value is specific to StopIteration) try: next(g) # Raises StopIteration exception again (still no more items) except StopIteration as e: print(e.value) # Prints 'None' (StopIteration value returned by f3 can only be # obtained once)
# Using 'send' and return value from 'yield from'; # see above for definition of f3() def f4(m): print('first-', end='') y = yield from f3(m) # Returns items one by one from f3 generator to caller of # next() or send(); sets y to f3's StopIteration value when f3 # is exhausted; argument to send() is passed to f3's send(); yield from (y, y * 2) # Provide 2 more items (one by one) after f3 is exhausted g = f4(3) # Assigns the generator object returned from function f4 g.send(4) # Raises TypeError exception; can't send non-None value to # just-started generator (no yield to receive it) next(g) # Prints 'first-1st' and returns 3 (first item from f4, which got # it from f3) next(g) # Returns 6 (next item from f4, which got it from f3) g.send(4) # Sends 4 into f4, which forwards it to f3, which sets next item to # 4 and returns 4 to f4, which returns 4 here next(g) # Returns 7 (next item from f4, which got it from f3) list(g) # Returns [10, 13, 16, 32] (all remaining items from f4, which got # the first 2 items from f3, then produced 2 more items based on # f3's StopIteration value (16))
# Using 'throw', and 'close' def f5(): try: yield from (1,2,3) # Returns items one by one from a tuple to the caller (f6), and # raises any exception passed in via throw() except ValueError as e: # Catches a ValueError exception and yield from e.args # returns its arguments one by one to the caller (f6) def f6(): yield from f5() # Returns items one by one from f5 to caller of next(), send(), # or throw() (after forwarding those calls to f5) g = f6(3) # Assigns the generator object returned from function f6 next(g) # Returns 1 (first item from f6, which got it from f5) next(g) # Returns 2 (next item from f6, which got it from f5) g.throw(ValueError, (7,8,9)) # Passes the exception ValueError(7,8,9) to f6 (via f6's # 'yield'), which passes it to f5 (via f5's first 'yield'), # which can't pass it any further so raises the exception at # that 'yield', catches the exception in the 'except', then # starts returning items one by one from the tuple of # arguments passed to ValueError, i.e. (7,8,9); thus f5 # returns 7 to f6, which returns 7 here next(g) # Returns 8 (next item from f6, which got it from f5) g.close() # Closes/exhausts the generator early (so skips the last item 9) next(g) # Raises StopIteration exception due to no more items (closed) g.close() # Closing an already exhausted generator has no effectSee also generator expressions.
from functools import wraps # Imports the 'wraps' decorator factory which copies attributes # (__name__, __doc__, __module__) from the wrapped function to # the wrapper function to make the wrapping more transparent def deco1(f): # Defines a function deco1 which takes another function and # returns a modified version of it; any function that takes a # function or class as its sole argument can be used as a # decorator regardless of what it returns (see how to use deco1 # below) @wraps(f) # Applies the 'wraps' decorator factory to f_wrapper1 def f_wrapper1(*args): # Defines the wrapper function which return 2 * f(args[0] * 10, *args[1:]) # calls the wrapped/decorated function return f_wrapper1 # Returns the wrapper function which will later be called # instead of the wrapped/decorated function def deco_factory(inscale, outscale): # Defines a function deco_factory which uses its # arguments to produce a function that can be used as a # decorator; any function that does this can be used as a # decorator factory (see how to use deco_factory below) def deco2(f): # Defines a function deco2 similar to deco1 above, but this # one is customized based on the arguments to deco_factory @wraps(f) def f_wrapper2(*args): return outscale * f(args[0] * inscale, *args[1:]) return f_wrapper2 return deco2 # Returns the deco2 function # The following line decorates the myadd1 function with the deco1 function; # same as doing myadd1 = deco1(myadd1) after defining myadd1, # so the name myadd1 will end up referring to f_wrapper1 which, when called, # will call the original myadd1 defined below @deco1 def myadd1(x, y): return x + y # The following line calls deco_factory and uses the returned function (deco2) # as a decorator for the myadd2 function; # same as doing myadd2 = deco_factory(100, 10)(myadd2) after defining myadd2, # so the name myadd2 will end up referring to f_wrapper2 which, when called, # will call the original myadd2 defined below @deco_factory(100, 10) def myadd2(x, y): return x + y # Any number of decorators can be applied to the same function; they are applied # in reverse order; # the following is the same as doing myadd3 = deco_factory(100, 10)(deco1(myadd3)) # after defining myadd3 @deco_factory(100, 10) @deco1 def myadd3(x, y): return x + y myadd1(3, 4) # Calls f_wrapper1 which calls the original myadd1 and returns # 68 (the result of 2 * (3 * 10 + 4)) myadd2(3, 4) # Calls f_wrapper2 which calls the original myadd2 and returns # 3040 (the result of 10 * (3 * 100 + 4)) myadd3(3, 4) # Calls f_wrapper2 which calls f_wrapper1 which calls the # original myadd3; returns 60080 (10 * (2 * (3 * 100 * 10 + 4)))Examples of standard functions often used as decorators are: classmethod(), staticmethod(), property(), functools.total_ordering(). Most decorators take a function and return a function, but property() takes a function and returns a property object, and functools.total_ordering() takes a class and returns a class. Examples of standard functions often used as decorator factories are: functools.wraps().
__build_class__(f,'C') # TBD __import__('sys') # TBD abs(-3+4j) # Returns 5.0 aiter(o) # Returns an asynchronous iterator for asynchronous iterable o; # (asynchronous version of iter()) all([True,4,'0']) # Returns True (Are all items True after conversion to bool? Yes); # all([]) returns True (not False!) anext(i,d) # Returns an awaitable which, when awaited, returns next item from # asynchronous iterator i, or, if no more items, returns d or # raises StopIteration if no d; (asynchronous version of next()) any([False,0,'']) # Returns False (Are any items True after conversion to bool? No); # any([]) returns False ascii(o) # Same as repr(o) but uses escapes for non-ascii characters bin(12) # Returns '0b1100' (binary representation of 12) bool() # Returns a new bool breakpoint() # Calls sys.breakpointhook() which by default pauses execution and # enters the Python debugger bytearray() # Returns a new bytearray bytes() # Returns a new bytes object callable(f) # Returns True if f appears to be callable (i.e. a function, class, # or other object with a __call__ method); note that some objects # may appear to be callable but will fail when called chr(65) # Returns 'A' (character with Unicode code 65) classmethod(f) # Returns a class method for function f (usually used as decorator # @classmethod, see classes) compile('x=3\nprint(x)', '', 'exec') # Returns a code object which can be executed by exec() compile('2+5', '', 'eval') # Returns a code object which can be evaluated by eval() complex() # Returns a new complex delattr(o, 'x') # Deletes object o's attribute x; same as del o.x dict() # Returns a new dict dir(o) # Returns a list of o's attributes, or a list of names in the # current local scope if no argument is given divmod(-5, 3) # Returns (-2, 1); same as (-5 // 3, -5 % 3) enumerate(x) # Returns a new enumerate; see iterator types eval('2+3') # Returns 5; evaluates any Python expression (or compile() object) exec('if True:\n print("hi")') # Prints 'hi'; executes code in a string or compile() object exit() # Exits interactive interpreter; see script termination filter(f, 'hello') # Returns iterator for sequence 'h','l','l','o' assuming f is: # def f(x): return x > 'g' filter(None, [3,0,'']) # Returns iterator for sequence 3 (all True items in list) float() # Returns a new float format(4, '<03') # Returns '400' (same as '{:<03}'.format(4), see string operations) frozenset() # Returns a new frozenset getattr(o, 'x', d) # Returns the value of object o's attribute x (same as o.x), or, if # o.x doesn't exist, returns d or raises AttributeError if no d globals() # Returns a dict representing the current global symbol table; # globals()['x'] = 3 is equivalent to global x; x = 3 hasattr(o, 'x') # Returns True if object o has attribute x hash(o) # Returns a hash value (integer) of immutable object o help(o) # Prints documentation on object o - or topic o if o is a string hex(254) # Returns '0xfe' (hexadecimal respresentation of 254) id(o) # Returns the unique id (integer) of object o input('Input: ') # Prints 'Input: ', reads a line from stdin, and returns the line # (excluding the final newline) int() # Returns a new int isinstance(o, c) # Returns True if o is an instance of class c or of a subclass of c # - or of any item in c if c is a tuple of classes isinstance(o, c1 | c2) # Same as isinstance(o, (c1,c2)) issubclass(c1, c2) # Returns True if c1 is a subclass of, or identical to, c2 - or any # item in c2 if c2 is a tuple of classes; c1 must be a class issubclass(c1, c2 | c3) # Same as issubclass(c1, (c2,c3)) iter(o) # Returns an iterator for iterable o; see iterator types iter(f,x) # Returns an iterator that calls f with no args until return value # equals x; see iterator types len([6,7,8]) # Returns 3 (number of items in list/tuple/set/...) list() # Returns a new list locals() # Returns a dict representing the current local symbol table (this # dict should not be modified) map(f, [5,2,6], (3,4)) # Returns iterator for sequence '53','24' assuming f is: # def f(x,y): return str(x)+str(y); # the number of sequences must match the number of arguments to f max(3,5,2) # Returns 5 max([3,5,2]) # Returns 5 max([], default=4) # Returns 4 max([3,5,2], key=lambda x:-x) # Returns 2 max([], key=lambda x:-x, default=4) # Returns 4 memoryview(o) # Returns a new memoryview min(3,5,2) # Returns 2 min([3,5,2]) # Returns 2 min([], default=4) # Returns 4 min([3,5,2], key=lambda x:-x) # Returns 5 min([], key=lambda x:-x, default=4) # Returns 4 next(i,d) # Returns next item from iterator i, or, if no more items, returns # d or raises StopIteration if no d; see iterator types object() # Returns a new object oct(8) # Returns '0o10' (octal representation of 8) open('file', 'w') # Opens 'file' for writing and returns a file object; see files ord('A') # Returns 65 (Unicode code of character 'A') pow(3.0, 2.0) # Returns 9.0; same as 3.0**2.0; pow(3, 2, 4) # Returns 1; same as 3**2 % 4 but more efficient; all args must be # integers print() # Prints its arguments; see standard in/out/error quit() # Exits interactive interpreter; see script termination property() # Returns a new property range(10) # Returns a new range repr(o) # Returns a formal string representation of o, preferably one that # can be executed by eval() to recreate o reversed([1,2,3]) # Returns an iterator for the sequence 3,2,1 reversed('hello') # Returns an iterator for the sequence 'o','l','l','e','h' round(25.16) # Returns 25 (25.16 rounded to 0 digits after the point) round(25.16, 0) # Returns 25.0 (25.16 rounded to 0 digits after the point) round(25.16, 1) # Returns 25.2 (25.16 rounded to 1 digit after the point) round(25.16, -1) # Returns 30.0 (25.16 rounded to 1 digit before the point) set() # Returns a new set setattr(o, 'x', 3) # Sets object o's attribute x to 3; same as o.x = 3 slice(10) # Returns a new slice sorted([10,-1,9]) # Returns [-1, 9, 10] (takes the same additional args as o.sort()) staticmethod(f) # Returns a static method for function f (usually used as decorator # @staticmethod, see classes) str() # Returns a new str sum([1,2,3,4]) # Returns 10 (the sum of all items) sum([2,3,4], 1) # Returns 10 (the sum of all items and the 2nd argument) super(C, self).m() # Calls method m of class C's parent/sibling class (the next class # after C in the method resolution order of self's class) and # passes object self to that method; see class inheritance; # arguments C and self are optional inside class definitions tuple() # Returns a new tuple type(10) # Returns int (i.e. a type object that is the type of the argument) type('MyT', (), {}) # Returns a new type; see class creation and instantiation vars(o) # Returns o.__dict__, or same as locals() if no o zip([5,2,6], (3,4)) # Returns an iterator for sequence (5, 3), (2, 4); # any number of collections are supported; # zip can also unzip, because if x is a list of same-length tuples, # and y = list(zip(*x)), then x == list(zip(*y)) zip([5,2,6], (3,4), strict=True) # Returns same iterator as above, except this iterator # raises ValueError instead of StopIteration when done, because # arguments don't have same length
Types and classes are two terms for the same thing: objects representing categories of other objects. The UML diagram below illustrates the relationships between various types of objects ("int:type" means object int is an instance/object of class type, and arrows point from subclasses to the superclasses/bases they inherit from).
The following code creates the objects shown in the diagram above and checks their relationships.
# Create the non-standard classes shown in the diagram above class Myclass: pass class Mysubclass(Myclass): pass class Mymetaclass(type): pass class Myclass2(metaclass=Mymetaclass): pass # Create the non-standard "other objects" shown in the diagram above x = Myclass() y = Mysubclass() z = Myclass2() o = object() # Create the object groups shown in the diagram above classes = {object, type, Mymetaclass, Myclass, Mysubclass, int, Myclass2} metaclasses = {type, Mymetaclass} other_objs = {42, x, y, z, o} objects = classes | other_objs # All objects (even 'object' itself) are instances of 'object' (possibly instances of a # subclass of 'object') all(isinstance(obj, object) for obj in objects) # Returns True # All objects have a __class__ attribute referencing the class of which the object is a # direct instance; this is also the class returned when the object is passed to the type() # function all(obj.__class__ is type(obj) for obj in objects) # Returns True # All metaclasses are subclasses of 'type', except in the hypothetical and probably not # very useful case where they are NOT subclasses of 'type' but nevertheless behave like # 'type' (no known examples of this); note that Python considers any class to be a subclass # of itself all(issubclass(cls, type) for cls in metaclasses) # Returns True # All classes (even 'type' itself) are instances of 'type' (possibly instances of a # subclass of 'type' in the case of metaclass instances), except if they are instances of # one of the unlikely metaclasses mentioned above all(isinstance(cls, type) for cls in classes) # Returns True all(type(cls) is type for cls in (classes - {Myclass2})) # Returns True # Myclass2 is an example of an instance of a subclass of 'type' type(Myclass2) is Mymetaclass # Returns True issubclass(Mymetaclass, type) # Returns True # Other objects than classes are not instances of 'type' (not even instances of a subclass # of 'type'), they are instances of some other class any(isinstance(obj, type) for obj in other_objs) # Returns False any(issubclass(type(obj), type) for obj in other_objs) # Returns False expected_class_for_obj = {42:int, x:Myclass, y:Mysubclass, z:Myclass2, o:object} all(isinstance(obj, expected_class_for_obj[obj]) for obj in other_objs) # Returns True all(type(obj) is expected_class_for_obj[obj] for obj in other_objs) # Returns True # All classes are subclasses of 'object' (possibly subclasses of a subclass of 'object'; # note that Python considers any class to be a subclass of itself) all(issubclass(cls, object) for cls in classes) # Returns True # Mymetaclass and Mysubclass are examples of subclasses of subclasses of 'object' issubclass(Mymetaclass, type) # Returns True issubclass(type, object) # Returns True issubclass(Mysubclass, Myclass) # Returns True issubclass(Myclass, object) # Returns True # All classes have a (possibly empty) __bases__ tuple containing their superclasses all(hasattr(cls, '__bases__') for cls in classes) # Returns True all(cls.__bases__ == (object,) for cls in classes - {object, Mymetaclass, Mysubclass}) # Returns True object.__bases__ # Returns () Mymetaclass.__bases__ # Returns (type,) Mysubclass.__bases__ # Returns (Myclass,) # Other objects than classes obviously cannot be subclasses (issubclass will raise # TypeError) and do not have the __bases__ tuple any(hasattr(obj, '__bases__') for obj in other_objs) # Returns False
# Class definitions class Myclass: # Defines a class (which inherits only from 'object') # Here's a docstring for this class: """This docstring is accessible via Myclass.__doc__ or e.g. o1.__doc__ where o1 is an instance of Myclass. """ n = 3 # Defines a class variable, shared by all instances of this class @staticmethod # Decorator that defines a static method to be invoked on the class def setn(n): # itself (or optionally on instances of the class) Myclass.n = n # Updates the class variable @classmethod # Decorator that defines a class method to be invoked on the class def setn2(cls, n): # itself (or optionally on instances of the class); first arg is # the class, conventionally named 'cls' cls.n = n # Updates the class variable def __init__(self, x): # Defines the instance constructor; first arg is the instance, # conventionally named 'self' (like 'this' in C++) self.x = x # Creates an instance variable belonging to the given instance def add(self, y): # Defines an instance method to be invoked on a given instance self.x += (y * # Updates the previously created instance variable self.n) # Class variables may be read (not written!) via 'self' # (if written, a new instance variable is created hiding the # class variable!) def __str__(self): # Defines informal nicely printable string representation of # an instance of this class return str(self.x) # Returns instance variable converted to string def __repr__(self): # Defines formal string representation of an instance of this # class, preferably executable by eval() to recreate instance return 'Myclass(%d)' % self.x def __getitem__(self, item): # Defines special method for getting indexed item print('get', item) t = item if isinstance(item, tuple) else (item,) # Make item a tuple if not already for i in t: # Step through tuple if isinstance(i, slice): # Handle slice by converting it to print(range(*i.indices(self.n)), end=' ')# range (self.n sets upper limit) elif i == Ellipsis: # Handle Ellipsis object by just print('...', end=' ') # printing 3 dots else: print(i, end=' ') print() return self.x def __setitem__(self, key, val): # Defines special method for setting indexed item print('set', key, val) def __add__(self, other): # Defines special method overriding '+' operator return self.x + other def __radd__(self, other): # Defines special method overriding '+' operator if # this object is 2nd arg and other.__add__() # returned NotImplemented return self.x + other + self.n type(Myclass) # Returns class type; same as Myclass.__class__ isinstance(Myclass, type) # Returns True issubclass(Myclass, object) # Returns True Myclass.__name__ # Returns 'Myclass' Myclass.__bases__ # Returns (object,) (tuple of base classes) Myclass.mro() # Returns [Myclass, object] # (method resolution order: order in which classes are searched # for a method definition) Myclass.n # Returns 3 type(Myclass.setn) # Returns class types.FunctionType with name 'function' type(Myclass.setn2) # Returns class types.MethodType with name 'method' type(Myclass.add) # Returns class types.FunctionType with name 'function' # (unbound method) # Dynamically defined classes type('Mydynclass', (Myclass,), {'n': 4}) # Creates and returns a class with __name__ == # 'Mydynclass' but not bound to any name in current namespace; # this class inherits from Myclass and sets class variable n to 4; # the class will be lost unless a reference to it is saved, e.g. # Someclass = type('Mydynclass', ...) # Instantiation o1 = Myclass(10) # Creates an object as an instance of class 'Myclass' and runs # the __init__ constructor with parameter x = 10 type(o1) # Returns class Myclass; same as o1.__class__ type(o1.add) # Returns class types.MethodType with name 'method'; more about # method objects below o2 = Myclass(20) # Creates a second instance of the same class using x = 20 o1.x # Returns 10 o2.x # Returns 20 o1.n # Returns 3 (the value of class variable n) str(o1) # Returns '10' (return value from o1.__str__()) repr(o1) # Returns 'Myclass(10)' (return value from o1.__repr__()) dir(o1) # Returns list of all o1's attributes: [..., '__doc__', ..., # '__init__', ..., 'add', 'n', 'setn', 'setn2', 'x'] o1[4] # Calls o1.__getitem__(4) o1[::-1] = 2 # Calls o1.__setitem__(slice(None,None,-1),2); see slice o1[2,:3,...] # Calls o1.__getitem__((2,slice(None,3,None),Ellipsis)) # (note: this extended syntax is not supported by built-in types # such as list and tuple) o1 + 4 # Calls o1.__add__(4) which returns 14 5 + o1 # Calls o1.__radd__(5) (when (5).__add__(o1) returns NotImplemented) # which returns 18 o1.add(2) # Passes 2 to the 'add' method of o1 which updates o1's x; # equivalent to Myclass.add(o1, 2) o1.x # Returns 16 (2 * 3 was added to the previous value 10) o2.x # Returns 20 (no change) Myclass.setn(5) # Changes the class variable n value to 5 Myclass.setn2(5) # Same effect as above (Myclass is automatically passed as 1st arg # to setn2, and 5 becomes 2nd arg) Myclass.n = 5 # Same effect as above o1.setn(5) # Same effect as above (o1 is only used to access Myclass) o1.setn2(5) # Same effect as above (o1 is only used to access Myclass) # (don't do o1.n = 5, it hides the class variable from o1) o1.n # Returns 5 o2.n # Returns 5 (same class var n is accessed from any instance) o2.add(-1) o1.x # Returns 16 o2.x # Returns 15 (-1 * 5 was added to 20) o1.s = 'hi' # Creates a new instance variable on o1 only Myclass.k = 100 # Creates a new class variable (visible in all existing and new # instances) # Bound method objects o1a = o1.add # Assigns a bound method object referencing o1's 'add' method (a # new object is created every time a user defined method is # accessed like this, so (o1.add is o1.add) evaluates to False!) type(o1a) # Returns class types.MethodType with name 'method' o1a.__self__ # Returns o1 o1a.__func__ # Returns Myclass.add o1a(4) # Passes 4 to o1's 'add' method, which updates o1's x o1.x # Returns 36 (4 * 5 was added to 16) # Built-in function/method objects ss = 'abba'.strip # Assigns a bound method object referencing the built-in strip # method of string 'abba' type(ss) # Returns class types.BuiltinFunctionType with name # 'builtin_function_or_method' ss.__self__ # Returns 'abba'ss.__func__# ILLEGAL! Raises AttributeError exception because built-in methods # don't have the __func__ attribute ss('a') # Returns 'bb' (same as 'abba'.strip('a')) type(len) # Returns class types.BuiltinFunctionType with name # 'builtin_function_or_method'; built-in functions are actually # bound methods of the 'builtins' module object (which is hidden # unless explicitly imported) len.__self__ # Returns builtins module object
class A(list): # Defines a class A which inherits from list (which # inherits from object) def __str__(self): # Overrides list's __str__ method in order to... return ('A:' + # ... prepend 'A:' to... super().__str__()) # ... whatever is returned from __str__() of the # next classes in the method resolution order (i.e. # the previous classes in the inheritance order); # the next class is list when self is an instance # of A, but B when self is an instance of C! class B(list): # Defines a class B just like A, except... def __str__(self): return ('B:' + # ... prepend 'B:' to... super().__str__()) # ... whatever is returned from __str__() of the # next classes in the method resolution order; the # next class is list when self is an instance of # either B or C class C(A, B): # Defines a class C which inherits primarily from A # and secondarily from B def __str__(self): # Overrides the __str__ method in order to... return ('C:' + # ... prepend 'C:' to... super().__str__()) # ... whatever is returned from __str__() of the # next classes in the method resolution order; the # next class is A when self is an instance of C C.__bases__ # Returns (A, B) # Method resolution order (MRO) for classes A, B, and C (see notes) A.mro() # Returns [A, list, object]; this means that A().__str__() will first look for # an __str__ method in class A, then in class list, then in class object, until # a class is found which has the method B.mro() # Returns [B, list, object] C.mro() # Returns [C, A, B, list, object] a = A([0]) # Assigns an instance of class A initialized by calling a.__init__([0]) which # resolves to list.__init__(a,[0]) (list is 1st class with __init__ in A.mro()) # which sets the initial value to [0] b = B([1]) # Assigns an instance of class B initialized to [1] by list.__init__(b,[1]) c = C([2]) # Assigns an instance of class C initialized to [2] by list.__init__(c,[2]) print(a) # Prints 'A:[0]', because print calls a.__str__() # which resolves to A.__str__(a) (A is a's class and has __str__) # which calls super(A, a).__str__() # which resolves to list.__str__(a) (list follows A in A.mro() and has __str__) # which returns '[0]' to A.__str__ # which returns 'A:[0]' to print print(b) # Prints 'B:[1]', because print calls b.__str__() # which resolves to B.__str__(b) (B is b's class and has __str__) # which calls super(B, b).__str__() # which resolves to list.__str__(b) (list follows B in B.mro() and has __str__) # which returns '[1]' to B.__str__ # which returns 'B:[1]' to print print(c) # Prints 'C:A:B:[2]', because print calls c.__str__() # which resolves to C.__str__(c) (C is c's class and has __str__) # which calls super(C, c).__str__() # which resolves to A.__str__(c) (A follows C in C.mro() and has __str__) # which calls super(A, c).__str__() # which resolves to B.__str__(c) (B follows A in C.mro() and has __str__) # which calls super(B, c).__str__() # which resolves to list.__str__(c) (list follows B in C.mro() and has __str__) # which returns '[2]' to B.__str__ # which returns 'B:[2]' to A.__str__ # which returns 'A:B:[2]' to C.__str__ # which returns 'C:A:B:[2]' to printNotes on method resolution order (MRO):
A descriptor is an object attribute which calls special methods to override the default behavior when getting, setting, and/or deleting the attribute. In order to do so, a descriptor must be an instance of a class which defines the special methods __get__, __set__, and/or __delete__.
A descriptor that defines both the __get__ and __set__ methods is a data descriptor and looks like an instance/class variable. It overrides any instance/class variable with the same name. A property attribute is an example of a data descriptor.
A descriptor that defines only the __get__ method is a non-data descriptor and looks like a normal method. It is overridden by any normal method with the same name.
class MyDescriptor: # Defines a descriptor class def __set_name__(self, ownerclass, name): # Special method called when descriptor # instance is assigned to name in class self.hidden_var_name = '_' + name # Stores name '_x' in descriptor instance # (assuming descriptor was assigned to name # x in ownerclass definition) def __get__(self, instance, ownerclass): # Special method called when descriptor is # accessed as instance.x or ownerclass.x if instance is None: # If accessed as ownerclass.x, then return ownerclass.__name__ # return name of ownerclass return getattr(instance, # Otherwise return instance._x self.hidden_var_name) def __set__(self, instance, value): # Special method called when descriptor is # set to value, i.e. instance.x = value setattr(instance, # Sets instance._x = float(value) self.hidden_var_name, float(value)) def __delete__(self, instance): # Special method called when descriptor is # deleted, i.e. del instance.x delattr(instance, # Deletes instance._x (the descriptor self.hidden_var_name) # instance.x itself remains intact) class MyOwnerClass: # Defines class MyOwnerClass; will contain our descriptor def __init__(self, v): # Defines the instance constructor self.x = v # Calls MyDescriptor.__set__(x, self, v) which sets self._x = # float(v) x = MyDescriptor() # Defines descriptor x as instance of class MyDescriptor MyOwnerClass.x, # Returns 'MyOwnerClass' (calls MyDescriptor.__get__(x, None, # MyOwnerClass)) o = MyOwnerClass(1) # Assigns a new instance of MyOwnerClass with o._x set to 1.0 o._x, # Returns 1.0 o.x, # Returns 1.0 (calls MyDescriptor.__get__(x, o, MyOwnerClass)) o.x = 2, # Sets o._x to 2.0 (calls MyDescriptor.__set__(x, o, 2)) o.x, # Returns 2.0 (calls MyDescriptor.__get__(x, o, MyOwnerClass)) del o.x # Deletes o._x (calls MyDescriptor.__delete__(x, o)) o.x, # Raises AttributeError exception because o._x doesn't exist o.x = 3, # Sets o._x to 3.0 (calls MyDescriptor.__set__(x, o, 3)) o.x, # Returns 3.0 (calls MyDescriptor.__get__(x, o, MyOwnerClass))
class C: def __init__(self, v): # Defines the instance constructor self._x = float(v) # Initializes an instance variable which is not supposed to be # accessed directly from outside class C @property # Creates a property attribute x whose __get__() method is def x(self): # this function (function name becomes property name) return self._x @x.setter # Sets the __set__() method of property x to def x(self, v): # this function (use same name for function and property) self._x = float(v) @x.deleter # Sets the __delete__() method of property x to def x(self): # this function (use same name for function and property) del self._x type(C.x) # Returns class property isinstance(C.x, property) # Returns True c = C(1) # Assigns a new instance of class C with c._x set to 1.0 c._x # Returns 1.0 (but we're not supposed to access _x directly) c.x # Returns 1.0 (calls our getter C.x.__get__(c)) c.x = 2 # Sets c._x to 2.0 (calls our setter C.x.__set__(c, 2)) c.x # Returns 2.0 (calls our getter C.x.__get__(c)) del c.x # Deletes c._x (calls our deleter C.x.__delete__(c)) c.x # Raises AttributeError exception because c._x doesn't exist c.x = 3 # Sets c._x to 3.0 (calls our setter C.x.__set__(c, 3)) c.x # Returns 3.0 (calls our getter C.x.__get__(c))
Certain special methods - if defined in the class hierarchy of an object (not on the object itself) - are automatically called when certain built-in Python functions, statements, or syntax are applied to that object, and such calls are never redirected to the __getattr__ or __getattribute__ methods even if those exist. Built-in Python classes themselves have many of these special methods.
In the list below, all methods are instance methods unless specifically stated to be static methods or class methods (the @staticmethod and @classmethod decorators are unnecessary for special methods defined within the body of a class definition - the methods are automatically converted to the correct type), and the methods are assumed to have been defined in a class C of which object o is in instance, like this:
class C: def __new__(cls, *args, **kwargs): pass def __init__(self, *args, **kwargs): pass # ...etc... o = C()List of special methods along with examples of code that causes them to be called:
# Object creation/deletion o = C.__new__(C) # Static method; called by o = C() to create object of class C o.__init__() # Called by o = C() to initialize object created by __new__() o.__del__() # Called by del o; gc.collect(), i.e. when o is garbage # collected after last reference to it has been removed # Class creation C.__init_subclass__(C2) # Class method; called by class C2(C): pass, i.e. when a # subclass of class C is defined C2 = C.__class_getitem__(t) # Class method; called by C2 = C[t] to create specialized class # C2 from generic class C using type t # Object attribute access y = o.__getattr__('x') # Called by y = o.x if o.x and o.__getattribute__ don't exist, # or if o.__getattribute__('x') raises AttributeError y = o.__getattribute__('x') # Called by y = o.x o.__setattr__('x', 5) # Called by o.x = 5 o.__delattr__('x') # Called by del o.x y = o.__dir__() # Called by y = dir(o) # Container object item access y = o.__len__() # Called by y = len(o) y = o.__getitem__(3) # Called by y = o[3] o.__setitem__(3, 9) # Called by o[3] = 9 o.__delitem__(3) # Called by del o[3] y = o.__contains__(3) # Called by y = 3 in o y = o.__iter__() # Called by y = iter(o) y = o.__reversed__() # Called by y = reversed(o) y = o.__missing__(3) # Called by y = o[3] (from within dict.__getitem__(3)) if o's # class inherits from dict and o[3] doesn't exist # Iterator object item access y = o.__next__() # Called by y = next(o) # Object conversion y = o.__repr__() # Called by y = repr(o) y = o.__str__() # Called by y = str(o) y = o.__format__('^6') # Called by y = format(o, '^6') or y = '{:^6}'.format(o) y = o.__bytes__() # Called by y = bytes(o) y = o.__bool__() # Called by y = bool(o) y = o.__hash__() # Called by y = hash(o) y = o.__int__() # Called by y = int(o) y = o.__float__() # Called by y = float(o) y = o.__complex__() # Called by y = complex(o) # Object calling y = o.__call__() # Called by y = o() # Context management y = o.__enter__() # Called when entering with o as y: pass o.__exit__(None, None, None)# Called when exiting with o: pass (if no exceptions) y = o.__exit__(excp_type, excp_val, traceback) # Called when exiting with o: raise excp; # if bool(y) == True, the exception is suppressed # Object comparison y = o.__lt__(o2) # Called by y = o < o2, or by y = o2 > o if o's type is # subclass of o2's type or if o2.__gt__(o) returns # NotImplemented y = o.__le__(o2) # Called by y = o <= o2, or by y = o2 >= o if o's type is # subclass of o2's type or if o2.__ge__(o) returns # NotImplemented y = o.__eq__(o2) # Called by y = o == o2, or by y = o2 == o if o's type is # subclass of o2's type or if o2.__eq__(o) returns # NotImplemented y = o.__ne__(o2) # Called by y = o != o2, or by y = o2 != o if o's type is # subclass of o2's type or if o2.__ne__(o) returns # NotImplemented y = o.__gt__(o2) # Called by y = o > o2, or by y = o2 < o if o's type is # subclass of o2's type or if o2.__lt__(o) returns # NotImplemented y = o.__ge__(o2) # Called by y = o >= o2, or by y = o2 <= o if o's type is # subclass of o2's type or if o2.__le__(o) returns # NotImplemented # Unary arithmetic operations y = o.__neg__() # Called by y = -o y = o.__pos__() # Called by y = +o y = o.__abs__() # Called by y = abs(o) y = o.__invert__() # Called by y = ~o y = o.__round__() # Called by y = round(o) y = o.__round__(2) # Called by y = round(o, 2) y = o.__trunc__() # Called by y = math.trunc(o) y = o.__floor__() # Called by y = math.floor(o) y = o.__ceil__() # Called by y = math.ceil(o) y = o.__index__() # Called by y = operator.index(o) or 'hello'[:o] (returns # 'hello'[:y]) or hex(o) (returns hex(y)) or wherever an exact # integer is needed # Binary arithmetic operations y = o.__add__(o2) # Called by y = o + o2 (but see __radd__) y = o.__sub__(o2) # Called by y = o - o2 (but see __rsub__) y = o.__mul__(o2) # Called by y = o * o2 (but see __rmul__) y = o.__matmul__(o2) # Called by y = o @ o2 (but see __rmatmul__) y = o.__truediv__(o2) # Called by y = o / o2 (but see __rtruediv__) y = o.__floordiv__(o2) # Called by y = o // o2 (but see __rfloordiv__) y = o.__mod__(o2) # Called by y = o % o2 (but see __rmod__) y = o.__divmod__(o2) # Called by y = divmod(o, o2) (but see __rdivmod__) y = o.__pow__(o2) # Called by y = o ** o2 or y = pow(o, o2) (but see __rpow__) y = o.__pow__(o2, 5) # Called by y = pow(o, o2, 5) (no __rpow__ variant) y = o.__lshift__(o2) # Called by y = o << o2 (but see __rlshift__) y = o.__rshift__(o2) # Called by y = o >> o2 (but see __rrshift__) y = o.__and__(o2) # Called by y = o & o2 (but see __rand__) y = o.__or__(o2) # Called by y = o | o2 (but see __ror__) y = o.__xor__(o2) # Called by y = o ^ o2 (but see __rxor__) # Reverse binary arithmetic operations y = o.__radd__(o2) # Called by y = o2 + o if o's type is subclass of o2's # type or if o2.__add__(o) returns NotImplemented y = o.__rsub__(o2) # Called by y = o2 - o if o's type is subclass of o2's # type or if o2.__sub__(o) returns NotImplemented y = o.__rmul__(o2) # Called by y = o2 * o if o's type is subclass of o2's # type or if o2.__mul__(o) returns NotImplemented y = o.__rmatmul__(o2) # Called by y = o2 @ o if o's type is subclass of o2's # type or if o2.__matmul__(o) returns NotImplemented y = o.__rtruediv__(o2) # Called by y = o2 / o if o's type is subclass of o2's # type or if o2.__truediv__(o) returns NotImplemented y = o.__rfloordiv__(o2) # Called by y = o2 // o if o's type is subclass of o2's # type or if o2.__floordiv__(o) returns NotImplemented y = o.__rmod__(o2) # Called by y = o2 % o if o's type is subclass of o2's # type or if o2.__mod__(o) returns NotImplemented y = o.__rdivmod__(o2) # Called by y = divmod(o2, o) if o's type is subclass of o2's # type or if o2.__divmod__(o) returns NotImplemented y = o.__rpow__(o2) # Called by y = o2 ** o or y = pow(o2, o) if o's type is # subclass of o2's type or if o2.__pow__(o) returns # NotImplemented # pow(o2, o, 5) always calls o2.__pow__(o, 5), never __rpow__ y = o.__rlshift__(o2) # Called by y = o2 << o if o's type is subclass of o2's # type or if o2.__lshift__(o) returns NotImplemented y = o.__rrshift__(o2) # Called by y = o2 >> o if o's type is subclass of o2's # type or if o2.__rshift__(o) returns NotImplemented y = o.__rand__(o2) # Called by y = o2 & o if o's type is subclass of o2's # type or if o2.__and__(o) returns NotImplemented y = o.__ror__(o2) # Called by y = o2 | o if o's type is subclass of o2's # type or if o2.__or__(o) returns NotImplemented y = o.__rxor__(o2) # Called by y = o2 ^ o if o's type is subclass of o2's # type or if o2.__xor__(o) returns NotImplemented # Augmented arithmetic assignment o = o.__iadd__(o2) # Called by o += o2 (falls back to o = o + o2 if no __iadd__) o = o.__isub__(o2) # Called by o -= o2 o = o.__imul__(o2) # Called by o *= o2 o = o.__imatmul__(o2) # Called by o @= o2 o = o.__itruediv__(o2) # Called by o /= o2 o = o.__ifloordiv__(o2) # Called by o //= o2 o = o.__imod__(o2) # Called by o %= o2 o = o.__ipow__(o2) # Called by o **= o2 o = o.__ilshift__(o2) # Called by o <<= o2 o = o.__irshift__(o2) # Called by o >>= o2 o = o.__iand__(o2) # Called by o &= o2 o = o.__ior__(o2) # Called by o |= o2 o = o.__ixor__(o2) # Called by o ^= o2 # Coroutine methods y = o.__await__() # Called by z = await o (inside a coroutine), where y is a # coroutine wrapper (handled by await) and z is the coroutine's # return value when it completes y = o.__aiter__() # Called by y = aiter(o) or async for z in o: pass (inside a # coroutine) to get an asynchronous iterator y for the # asynchronous iterable o; asynchronous variant of __iter__ y = o.__anext__() # Called by y = anext(o) or repeatedly by # async for z in o: pass (inside a coroutine) to get an # awaitable y which - when it completes - produces the next # item in o (to be assigned to z); asynchronous variant __next__ y = o.__aenter__() # Called when entering async with o as z: pass (inside a # coroutine); y is an awaitable which - when it completes - # produces the value to be assigned to z; asynchronous variant # of __enter__ y = o.__aexit__(None, None, None) # Called when exiting async with o: pass (inside a # coroutine) (if no exceptions); y is an awaitable which # completes when the __aexit__ coroutine completes; # asynchronous variant of __exit__ y = o.__aexit__(excp_type, excp_val, traceback) # Called when exiting # async with o: raise excp (inside a coroutine); y is an # awaitable which - when it completes - produces a value, and # if bool(value) == True, the exception is suppressed; # asynchronous variant of __exit__See also special methods for descriptors.
# Here's a docstring for this module: """Any Python file can be imported as a module, or run as a top level script. """ def f(x): return x * 2 if __name__ == '__main__': # If this file is run as a script, its module name is # '__main__', print(f(10)) # in which case call f and print the result '20' else: # Otherwise, this file is imported as a module, print('Module:', __name__) # so print 'Module: mymodule'Some other Python file:
import mymodule # Runs mymodule.py; puts all its names into namespace 'mymodule'; # the module prints 'Module: mymodule' import os, re # Imports multiple modules in same statement type(mymodule) # Returns class types.ModuleType with name 'module' print(mymodule.f(8)) # Calls mymodule's function f, and prints result '16' print(mymodule.__doc__) # Prints mymodule's docstring: 'Any Python file can be ...' print(__doc__) # Prints 'None' (this module has no docstring)Some other Python file:
import mymodule as m # Runs mymodule.py; puts all its names into namespace 'm'; # the module prints 'Module: mymodule' import os as o, re as r # Imports multiple modules in same statement print(m.f(8)) # Calls mymodule's function f, and prints result '16'Some other Python file:
from mymodule import f # Runs mymodule.py; puts its name f into our namespace; # the module prints 'Module: mymodule' from re import sub, subn # Imports multiple names from module in same statement print(f(8)) # Calls function f (defined in mymodule), and prints result '16'Some other Python file:
from mymodule import f as g # Runs mymodule.py; binds name g in our namespace to whatever # mymodule's name f is bound to; # the module prints 'Module: mymodule' from re import (sub as s, # Imports multiple names from module in same statement; subn as sn) # parentheses can be used to avoid backslash line-continuation print(g(8)) # Calls function g (f in mymodule), and prints result '16'Some other Python file:
from mymodule import * # Runs mymodule.py; puts all its names into our namespace; # the module prints 'Module: mymodule' print(f(8)) # Calls function f (defined in mymodule), and prints result '16'
import sys # System functionality, e.g. argv, exit(), stdin, stdout, stderr, # path, version_info import os # Operating system functions, e.g. getcwd(), chdir(), mkdir(), # makedirs(), rmdir(), remove(), rename(), walk() import os.path # Path manipulation, e.g. exists(), join(), abspath(), dirname(), # basename() import subprocess # Running external programs, e.g. run('echo hi', shell=True, \ # capture_output=True).stdout import logging # Logging, e.g. info(), warning(), error(), debug() import atexit # Exit handler registration, e.g. register() import math # Math constants and functions, e.g. pi, e, sqrt(), sin() import cmath # Complex math functions, e.g. sqrt(-1) import decimal # Class for precise representation of decimal numbers, e.g. # Decimal('0.1'); see float type import random # Random number generation, e.g. random(), randint(), randbytes() import functools # Function manipulation, e.g. partial(), reduce(), @wraps import itertools # Iterator construction, e.g. cycle(), chain(), permutations() import re # See regular expressions import collections # See container types import copy # Shallow/deep object copying functions, e.g. copy(), deepcopy() import time # Time functions, e.g. sleep(), monotonic() import datetime # Date/time objects, e.g. datetime.now(timezone.utc).astimezone() \ # .strftime('%Y-%m-%d %H:%M:%S.%f UTC%z')
import numpy # Numerical vector/matrix math import scipy # Scientific/engineering math (uses numpy) import matplotlib # Graphing (uses numpy)
Before they can be imported by Python scripts, non-standard modules/packages need to be installed on top of the relevant Python installation. This may be done by running (on the command line) the 'pip' command belonging to the Python installation. If you have multiple Python installations, e.g. for different Python versions, make sure to use the 'pip' command belonging to the one you wish to install modules for.
Example of a Linux command (not Python code) to install numpy:
/path/to/correct/pip install numpyor
/path/to/correct/python -m pip install numpyOn Windows the Python launcher py.exe may be used in the following way (where 'X.Y' is the Python version to install the module for):
py -X.Y -m pip install numpy
A package is a collection of modules and may also contain nested subpackages. In its simplest form a package is a directory (on a disk) containing an __init__.py file along with the module files and subpackage directories belonging to the package.
When a package is imported, the resulting package object has the same type as a module object, but has an additional __path__ attribute pointing to the package directory.
Let's assume we have the following directory structure and files:
The file contents are as follows:
File mymodule1.py:# Importing using relative package references from . import mymodule1b # Imports mymodule1b from same subpackage from ..subpkg3 import mymodule3 # Imports mymodule3 from sibling subpackage subpkg3 from .. import mymodule0 # Imports mymodule0 from parent package mypackage # Importing using absolute package reference (from top level package) from mypackage.subpkg3 import mymodule3b # Imports mymodule3b def who(): print(__name__) # Prints the module name (incl. parent package names) def who1b(): mymodule1b.who() # Calls the who function in mymodule1b def who3(): mymodule3.who() # Calls the who function in mymodule3 def who3b(): mymodule3b.who() # Calls the who function in mymodule3b def who0(): mymodule0.who() # Calls the who function in mymodule0All other mymodule*.py files:
def who(): print(__name__) # Prints the module name (incl. parent package names)File __init__.py under subpkg1:
__all__ = ['mymodule1'] # Specifies which modules to import from subpkg1 if '*' is usedAll other __init__.py files are empty:
The package can now be used by some other Python file:
import mypackage.subpkg1.mymodule1 # Imports mymodule1 from the package type(mypackage) # Returns class types.ModuleType with name 'module' # (a package is also a module) mypackage.subpkg1.mymodule1.who() # Prints 'mypackage.subpkg1.mymodule1'Some other Python file:
from mypackage.subpkg1 import mymodule1 # Imports mymodule1 from the package mymodule1.who() # Prints 'mypackage.subpkg1.mymodule1' mymodule1.who1b() # Prints 'mypackage.subpkg1.mymodule1b' mymodule1.who3() # Prints 'mypackage.subpkg3.mymodule3' mymodule1.who3b() # Prints 'mypackage.subpkg3.mymodule3b' mymodule1.who0() # Prints 'mypackage.mymodule0'Some other Python file:
from mypackage.subpkg1 import * # Imports all modules mentioned in the __all__ list in # subpkg1's __init__.py (i.e. mymodule1) from mypackage.subpkg2 import * # Imports nothing because subpkg2's __init__.py doesn't # define an __all__ list mymodule1.who() # Prints 'mypackage.subpkg1.mymodule1'mymodule2# Raises NameError because mymodule2 was never imported
x = 10 # (Creates and) binds name x to (immutable) object 10 y = x # Binds name y to same object as name x del x # Forgets name x; its previously bound object is still bound to y z = 10 # Binds name z to (immutable) object 10 (possibly same object 10 # that y is bound to; mutable objects will never be reused like # this) y = 11 # Binds name y to a different object than before p = q = ['hi'] # Binds names p and q to same (mutable) object (first p, then q!) p, z = z, p # Swaps objects bound to names p and z (so p = 10, z = ['hi']) def f1(b): # Binds name f1 to function object defined here, and binds name b # to object passed as argument to f1 b = 3 # Binds name b to different object than was passed to f1 (this # does not alter the passed object nor rebind whatever name was # used to refer to that object when calling f1) return b # Returns object bound to name b (and name b goes out of scope) x = f1(y) # Calls function object bound to name f1, and passes object bound # to name y as argument (name y itself is not passed and can't be # rebound from within f1); # also binds name x to object returned by function call class C1: # Binds name C1 to class object defined here pass c = C1() # Binds name c to new instance object created from class object # bound to name C1 y = (x := 2 + 3) + x # Adds 2 and 3 and binds name x to resulting object 5, then adds # that and object bound to name x (i.e. 5) and binds name y to # resulting object 10; x is still bound to 5 after this statement
def f1(x, y): global d, e # Allows this function to modify module level names d & e print(b, x) # Prints '1 1'; no need for 'global' to read external b, but # this will fail if b is assigned anywhere within f1 # (UnboundLocalError exception if assigned after this line) # (except if we cheat and assign to globals()['b']) c, d, e = 10, 11, 12 # Creates local c hiding external c (because no 'global') # (but we can still cheat and read/write globals()['c']); # also creates e at module level (did not exist before this!) f[0] = 13 # f refers to mutable object which can be changed even though # f is not listed as 'global' (but without 'global' external f # can't be changed to reference another object) x = 14 # x was same as external b, but is now 14, while b is still 1 y[0] = 15 # y refers to same object as external g, and that object can # be changed via y (but g can't be changed to refer to another # object) f2() # Calls external function f2 if x == 14: # Function definitions may be conditional like any statement # (the 'if' body does not have its own local name scope) def f3(d): # f3 is defined within f1, so can read f1's locals print(d) # Prints local parameter d which is hiding external d global c # Gives f3 access to module level c - not local c in f1 c = x # Assigns f1's local x to module level c (now c = 14) nonlocal h # Gives f3 access to outer scope h (excl. module level) h = 'C' # Changes f1's h from 'B' to 'C' h = 'B' # Creates local h hiding external h f3(30) # Calls inner function f3 which prints '30' print(b,c,d,e,f,g,h) # Prints '1 10 11 12 [13] [15] C'; f3 changed h def f2(): # f2 is not defined within f1, so can't read f1's locals pass b, c, d = 1, 2, 3 # Names read by function f1 must be defined before call to f1 f, g, h = [4], [5], 'A' # but not necessarily before definition of f1 f1(b, g) # Calls function f1 and sets parameters x = b and y = g print(b,c,d,e,f,g,h) # Prints '1 14 11 12 [13] [15] A'; f1 changed d, e, and objects # referenced by f & g (but didn't change f & g themselves); # f3 changed cNotes:
import asyncio # Support for multiple concurrent tasks/coroutines import threading # Support for multiple threads import multiprocessing # Support for multiple processes import concurrent.futures # Used here for thread and process pool executors import contextlib # Used here to suppress exceptions in a with statement import os # Used here to get process id import datetime # Used here to get/format current time and do time calculations # A class to handle thread-safe and process-safe logging class Logger: def __init__(self, job_id, lock): self.job_id = job_id self.lock = lock # The __call__() method is called automatically when an instance of this class is # called as a function def __call__(self, msg): # Grab the global lock before printing to avoid simultaneous printing from # different threads/processes with self.lock: print('{}, job {} (proc {:5}, thread {:5}): {}'.format( datetime.datetime.now().strftime('%H:%M:%S.%f'), self.job_id, os.getpid(), threading.get_ident(), msg), flush=True) # A coroutine function identified by the async keyword; returns a coroutine object # representing the code in the function body, which is capable of cooperatively sharing # the same thread with other coroutines by occasionally allowing itself to be suspended # (e.g. by doing an await); execution of and switching between coroutines is handled by an # event loop async def concurrent_job(job_id, s, lock): log = Logger(job_id, lock) log(f'Started! Input={s!r}') for c in s: # Suspend this task for 1 second await asyncio.sleep(1) log(c) log(f'Done! Result={c!r}') return c # Another coroutine function whose coroutine object runs forever until cancelled async def endless_concurrent_job(job_id, lock, reraise=False): log = Logger(job_id, lock) loop = asyncio.get_running_loop() time = loop.time() log(f'Started! Time={time:.3f}') try: while True: # Suspend this task for 1.7 second await asyncio.sleep(1.7) delta = loop.time() - time time += delta log(f'Time delta={delta:.3f}') except asyncio.CancelledError as e: # An exception was raised to cancel this job log(f'Cancelled, msg={e.args[0]!r}') if reraise: raise time = loop.time() log(f'Time={time:.3f}') log(f'Result={time!r}') return time # A coroutine generator function identified by the async and yield keywords; returns a # generator object which - when iterated - runs each iteration in a coroutine and returns # the next yielded item as the result of the coroutine async def concurrent_generator_func(job_id, n, lock): log = Logger(job_id, lock) log(f'Started! Input={n}') for i in range(n): # Suspend this task for 1 second await asyncio.sleep(1) log(f'Sending {i}') # Produce the iteration item yield i log('Done!') # A normal non-cooperative function which blocks the whole thread until it returns, so it # should be run on a separate thread or in a separate process if the main thread needs to # do other things in parallel def blocking_job(job_id, step, lock): log = Logger(job_id, lock) start_time = datetime.datetime.now() end_time = start_time + datetime.timedelta(seconds=5) log(f'Started! Input={step}') i = 0 while datetime.datetime.now() < end_time: i += step log(f'Done! Result={i!r}') return i async def main(): # Create a single global lock to be passed to all threads and processes to ensure that # they don't corrupt each other's output (the simpler threading.RLock() can be used for # single-process programs, and no lock is needed for single-thread programs) lock = multiprocessing.Manager().RLock() log = Logger('Main', lock) log('Started!') # Get running event loop for our thread loop = asyncio.get_running_loop() # Start 3 concurrent jobs/coroutines in new separate tasks (in same thread as ours; # return value is a task which is a subclass of a 'future' which represents the job's # promised result when it completes; in addition to being a 'future' a task handles the # execution of the wrapped coroutine object) con1_task = asyncio.create_task(concurrent_job('Con1', 'ABCDEF', lock)) con2_task = asyncio.create_task(endless_concurrent_job('Con2', lock)) con3_task = asyncio.create_task(endless_concurrent_job('Con3', lock, reraise=True)) # Start parallel blocking job in new separate thread (in same process as ours; 1st arg # None selects default executor concurrent.futures.ThreadPoolExecutor, next arg # blocking_job is function to call in new thread, and remaining args are passed to that # function; return value is a 'future' object representing this job's promised result # when it completes) blk_t_future = loop.run_in_executor(None, blocking_job, 'BlkT', 1, lock) # Start parallel blocking job in new separate process (a separate Python interpreter is # started in that process, loads this module, and calls function blocking_job with # specified args; WARNING: make sure all this code is not run again when this module is # loaded in that new process, or else it will again spawn a new process etc.) process_executor = concurrent.futures.ProcessPoolExecutor() blk_p_future = loop.run_in_executor(process_executor, blocking_job, 'BlkP', -1, lock) # While the above jobs are running, iterate through a generator by running each # iteration in a separate coroutine (in same thread as ours) con_g_generator = concurrent_generator_func('ConG', 3, lock) async for i in con_g_generator: log(f'Received {i} from ConG') # Wait until all jobs (except the endless ones) complete, then get their results as a # list (list items have same order as args to gather()) results = await asyncio.gather(con1_task, blk_t_future, blk_p_future) # Convert the results into a dict with the job names as keys results = dict(zip(('Con1', 'BlkT', 'BlkP'), results)) # Stop endless job Con2 (by raising a CancelledError exception inside it) con2_task.cancel('Bye') # Wait until the job actually stops and get its result (the CancelledError exception is # caught and suppressed in Con2, so no exception is seen here) results['Con2'] = await con2_task # Stop endless job Con3 (by raising a CancelledError exception inside it) con3_task.cancel('Ciao') # Wait until the job actually stops (the CancelledError exception is not suppressed in # Con3, so suppress it here; the exception prevents 'await' from returning a result) with contextlib.suppress(asyncio.CancelledError): results['Con3'] = await con3_task for job in results: log(f'{job} result: {results[job]!r}') return 'Done!' # Make sure that main() only gets called when this module is run as a script, not when it # is imported as a module (as it will be inside the subprocess spawned by main() - see # WARNING above) if __name__ == '__main__': # Run coroutine main() and print its return value when it completes print(asyncio.run(main()))Example of output from above code:
16:59:17.484854, job Main (proc 14204, thread 12668): Started! 16:59:17.487854, job BlkT (proc 14204, thread 12036): Started! Input=1 16:59:18.100757, job ConG (proc 14204, thread 12668): Started! Input=3 16:59:18.148753, job BlkP (proc 10388, thread 11804): Started! Input=-1 16:59:18.244754, job Con1 (proc 14204, thread 12668): Started! Input='ABCDEF' 16:59:18.324757, job Con2 (proc 14204, thread 12668): Started! Time=1171.968 16:59:18.388756, job Con3 (proc 14204, thread 12668): Started! Time=1172.062 16:59:19.226306, job ConG (proc 14204, thread 12668): Sending 0 16:59:19.257555, job Main (proc 14204, thread 12668): Received 0 from ConG 16:59:19.319856, job Con1 (proc 14204, thread 12668): A 16:59:20.120442, job Con2 (proc 14204, thread 12668): Time delta=1.828 16:59:20.179725, job Con3 (proc 14204, thread 12668): Time delta=1.797 16:59:20.336100, job ConG (proc 14204, thread 12668): Sending 1 16:59:20.380381, job Main (proc 14204, thread 12668): Received 1 from ConG 16:59:20.412978, job Con1 (proc 14204, thread 12668): B 16:59:21.462500, job ConG (proc 14204, thread 12668): Sending 2 16:59:21.493750, job Main (proc 14204, thread 12668): Received 2 from ConG 16:59:21.524633, job ConG (proc 14204, thread 12668): Done! 16:59:21.565310, job Con1 (proc 14204, thread 12668): C 16:59:21.913473, job Con2 (proc 14204, thread 12668): Time delta=1.797 16:59:21.977473, job Con3 (proc 14204, thread 12668): Time delta=1.797 16:59:22.487480, job BlkT (proc 14204, thread 12036): Done! Result=10168136 16:59:22.612480, job Con1 (proc 14204, thread 12668): D 16:59:23.140135, job BlkP (proc 10388, thread 11804): Done! Result=-10056909 16:59:23.612465, job Con1 (proc 14204, thread 12668): E 16:59:23.674968, job Con2 (proc 14204, thread 12668): Time delta=1.766 16:59:23.721724, job Con3 (proc 14204, thread 12668): Time delta=1.750 16:59:24.623154, job Con1 (proc 14204, thread 12668): F 16:59:24.623154, job Con1 (proc 14204, thread 12668): Done! Result='F' 16:59:24.623154, job Con2 (proc 14204, thread 12668): Cancelled, msg='Bye' 16:59:24.624165, job Con2 (proc 14204, thread 12668): Time=1178.312 16:59:24.624165, job Con2 (proc 14204, thread 12668): Result=1178.312 16:59:24.624165, job Con3 (proc 14204, thread 12668): Cancelled, msg='Ciao' 16:59:24.625165, job Main (proc 14204, thread 12668): Con1 result: 'F' 16:59:24.625165, job Main (proc 14204, thread 12668): BlkT result: 10168136 16:59:24.625165, job Main (proc 14204, thread 12668): BlkP result: -10056909 16:59:24.625165, job Main (proc 14204, thread 12668): Con2 result: 1178.312 Done!
import subprocess # Imports subprocess module command_line = 'findstr /b [A-P] | sort' # Prepares a shell command line to execute some_input = 'Lisa\nBart\nR2D2\n' # Prepares some command input completed_proc = subprocess.run(command_line, # Executes the command line shell=True, # in a shell text=True, # with text (not bytes) input/output, input=some_input, # some_input as stdin, capture_output=True) # stdout/stderr captured, # and waits for command completion completed_proc.returncode # Returns 0 (command succeeded) completed_proc.stdout # Returns 'Bart\nLisa\n' (command output) completed_proc.stderr # Returns '' (no error output from command)
import subprocess # Imports subprocess module # The following Windows command line will wait for 1 second (because the waitfor command # fails to receive the signal 'nothing' within the specified 1-second deadline), then # prints DONE; the final exit code is 0 because the echo command succeeds command_line = 'waitfor nothing /t 1 2>NUL || echo DONE' # An instance of the Popen class can be used as a context manager in a 'with' statement # to ensure that pipes are closed and the subprocess is waited for when the 'with' block # exits (but here the proc.wait() below explicitly waits for the subprocess) with subprocess.Popen(command_line, # Executes the command line shell=True, # in a shell text=True, # with text (not bytes) input/output, stdout=subprocess.PIPE) as proc: # stdout captured, # and doesn't wait for command completion print('Command launched') assert proc.poll() is None # proc.poll() returns None while the # subprocess is still running proc.wait() # Waits for the subprocess to complete # (WARNING: if the subprocess produces more # output than will fit in the pipe buffer, # it will stall and never end because # proc.wait does not read from proc.stdout # while waiting) assert proc.poll() == proc.returncode # After completion, proc.poll() returns the # command exit code print('Finished with code %d and output: %s' % (proc.returncode, proc.stdout.read()))
Command launched Finished with code 0 and output: DONEHere's another example showing how the external program output can be obtained line by line as each line becomes available:
import subprocess # Imports subprocess module # The following Windows command line prints the numbers 1 through 5 and waits for 1 second # after each number; the final exit code is 1 because the waitfor command fails to receive # the signal 'nothing' command_line = 'for /L %i in (1,1,5) do @(echo %i && waitfor nothing /t 1 2>NUL)' with subprocess.Popen(command_line, # Executes the command line shell=True, # in a shell text=True, # with text (not bytes) input/output, bufsize=1, # line buffering, stdout=subprocess.PIPE) as proc: # stdout captured, # and doesn't wait for command completion for line in proc.stdout: # Gets 1 line at a time from the subprocess assert proc.poll() is None # proc.poll() returns None while the # subprocess is still running print(line, end='') # Prints the received line of text # When the above 'with' statement exits, it waits for the subprocess to complete assert proc.poll() == proc.returncode # After completion, proc.poll() returns the # command exit code print('Finished with code %d' % proc.returncode)
1 2 3 4 5 Finished with code 1