├── with_statements.md ├── compile.py ├── toc.md ├── variables.md ├── first.md ├── intro.md ├── file_io.md ├── type_hinting.md ├── exceptions.md ├── command_line.md ├── loops.md ├── comprehensions.md ├── classes.md ├── functions.md ├── string_formatting.md ├── syntax.md ├── c_interop.md ├── data_structures.md └── README.md /with_statements.md: -------------------------------------------------------------------------------- 1 | # With statements 2 | 3 | `With` statements enable files (and other objects) to automatically be closed when they are no longer needed. 4 | 5 | ```python 6 | with open('file.txt') as f: 7 | print(f.read()) 8 | ``` 9 | 10 | The file `f` will automatically be closed at the end of the `with` statement's code block 11 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | chapter_re = re.compile(r'\[(.*)\]\((.*)\)') 4 | 5 | with open('README.md', 'w') as all: 6 | with open('toc.md') as toc: 7 | files = chapter_re.findall(toc.read()) 8 | 9 | all.writelines( 10 | ['# Table of Contents\n', '\n'] 11 | + [f'* [{t}](#{"-".join(t.lower().replace("/", "").split())})\n' for t, f in files] 12 | + ['\n'] 13 | ) 14 | 15 | body = '\n'.join(open(f).read() for _, f in files) 16 | all.write(re.sub(r'\(.*\.md(#.*)\)', r'(\1)', body)) 17 | -------------------------------------------------------------------------------- /toc.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | * [Intro](intro.md) 4 | * [Your first program](first.md) 5 | * [Syntax](syntax.md) 6 | * [Variables](variables.md) 7 | * [Loops](loops.md) 8 | * [Functions](functions.md) 9 | * [Data structures](data_structures.md) 10 | * [Classes](classes.md) 11 | * [Exceptions](exceptions.md) 12 | * [String formatting](string_formatting.md) 13 | * [File I/O](file_io.md) 14 | * [With statements](with_statements.md) 15 | * [Command line arguments](command_line.md) 16 | * [Comprehensions](comprehensions.md) 17 | * [Type hinting](type_hinting.md) 18 | * [C Interop](c_interop.md) 19 | -------------------------------------------------------------------------------- /variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | In Python variables can hold any type of value; it does not even have to be the same across a variable's lifetime. 4 | Variables in Python do not need to be defined prior to being assigned to. 5 | 6 | ```python 7 | foo = 'bar' 8 | ``` 9 | 10 | This snippet makes a new string variable with the value `'bar'`. 11 | 12 | Just about anything in Python can be assigned to a variable and passed around as such. 13 | 14 | ```python 15 | foo = print 16 | foo('Hello world') 17 | ``` 18 | 19 | This program will print `Hello world` just as if the print function had been called directly. 20 | -------------------------------------------------------------------------------- /first.md: -------------------------------------------------------------------------------- 1 | # Your first program 2 | 3 | The simplest Python program is just an empty text file. 4 | Python doesn't require any special functions or objects to be created unlike C or Java. 5 | The Python iterpreter will simply execute each line in the source file starting at the top. 6 | 7 | As is tradition, your first Python program will simply print `Hello world`. 8 | 9 | ```python 10 | print('Hello world') 11 | ``` 12 | 13 | Save this file as `hello.py` and run it on the command line with `python hello.py`. 14 | If you installed Python correctly then you should see `Hello world` printed and be returned to your prompt. 15 | -------------------------------------------------------------------------------- /intro.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | This guide is written for an audience who is already familiar 4 | with languages such as C and Java. 5 | 6 | ## Installing 7 | 8 | Install the latest version of Python 3 from [python.org](https://www.python.org/downloads/). 9 | Be sure to install Python 3 and not Python 2. 10 | Python 2 is deprecated and will not be maintained past January 1 2020. 11 | No new code should be written in Python 2. 12 | 13 | When installing be sure to add Python to the PATH so you can run scripts 14 | from the command line. 15 | 16 | Open up a command prompt and run `python` to ensure that Python is installed correctly. 17 | You should be greeted with a welcome message which includes the version of Python running. 18 | Ensure that this version number matches the one you installed. 19 | Type `exit()` to leave the interactive prompt. 20 | 21 | You can open an interactive prompt at any time to test out small snippets of code without having to make a new script. 22 | -------------------------------------------------------------------------------- /file_io.md: -------------------------------------------------------------------------------- 1 | # File I/O 2 | 3 | File I/O is incredibly easy in Python. 4 | A file object is created by calling the `open()` function. 5 | At its simplest `open()` takes a single argument, the name of the file to open. 6 | 7 | File objects have numerous methods for reading the contents of the file. Additionally, a file object can be iterated over directly, yielding complete lines. 8 | 9 | ```python 10 | f = open('file.txt') 11 | content = f.read() 12 | f.close() 13 | ``` 14 | 15 | ```python 16 | f = open('file.txt') 17 | lines = f.readlines() 18 | f.close() 19 | ``` 20 | 21 | ```python 22 | f = open('file.txt') 23 | for line in f: 24 | ... 25 | f.close() 26 | ``` 27 | 28 | A complete list of file methods can be found in the [official documentation](https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects) 29 | 30 | When opening a file for writing just pass `'w'` as the second argument to `open()`. 31 | 32 | ```python 33 | f = open('file.txt', 'w') 34 | f.write('Test message') 35 | f.close() 36 | ``` 37 | 38 | Be sure to close file objects when you are done using them to prevent resource leaks. 39 | Alternatively use [with statements](with_statements.md#with-statements) to handle it automatically. 40 | -------------------------------------------------------------------------------- /type_hinting.md: -------------------------------------------------------------------------------- 1 | # Type hinting 2 | 3 | While Python is a dynamically typed language and doesn't have explicit types, it does have type hinting. 4 | When used with tools such as [mypy] it is possible to combine the flexibility of dynamic typing with the safety of static typing. 5 | 6 | ```python 7 | def foo(bar: str) -> int: 8 | return len(bar) 9 | ``` 10 | 11 | This is a function that takes a string and returns an integer. 12 | Type hints for function parameters are placed after a colon after the parameter name. 13 | Type hints for function return values are placed after an arrow after the parameter list, but before the ending colon. 14 | 15 | Python itself does nothing with the type hints. 16 | It would be perfectly valid to pass a list to the `foo` function defined above. 17 | Type hints are purely decorative unless used with an external tools such as [mypy]. 18 | 19 | It is also possible to access the type hints from within Python code. 20 | The `__annotations__` attribute of the function contains all the type hinting information. 21 | 22 | ```python 23 | def foo(bar: str) -> int: 24 | return len(bar) 25 | 26 | print(foo.__annotations__) # prints "{'bar': , 'return': }" 27 | ``` 28 | 29 | [mypy]: http://mypy-lang.org/ 30 | -------------------------------------------------------------------------------- /exceptions.md: -------------------------------------------------------------------------------- 1 | # Exceptions 2 | 3 | Exceptions in Python are handled using `try`/`except` blocks. 4 | 5 | ```python 6 | try: 7 | # Code that could throw an exception 8 | except: 9 | # Handle exception 10 | ``` 11 | 12 | This code will catch any error that is thrown inside the `try` block. 13 | It is possible to narrow down the exceptions that the `except` block will catch. 14 | 15 | ```python 16 | try: 17 | # Code that could throw an exception 18 | except IndexError: 19 | # Handle only an IndexError 20 | ``` 21 | 22 | If you need the exception object it is also possible to give it a name so it can be referenced in the `except` block. 23 | 24 | ```python 25 | try: 26 | # Code that could throw an exception 27 | except IndexError as e: 28 | # Handle only an IndexError 29 | ``` 30 | 31 | `Try`/`except` statements also support `else` and `finally` clauses. 32 | An `else` clause after `try`/`except` will execute only if the `except` clause does not run. 33 | A `finally` clause will always run, regardless of whether an exception is thrown or not. 34 | 35 | ```python 36 | try: 37 | # Code that could throw an exception 38 | except: 39 | # Handle exception 40 | else: 41 | # Only runs if except clause doesn't 42 | finally: 43 | # Always runs 44 | ``` 45 | 46 | `Finally` clauses are usually used to clean up resources used in a `try` block. 47 | -------------------------------------------------------------------------------- /command_line.md: -------------------------------------------------------------------------------- 1 | # Command line arguments 2 | 3 | Python has multiple ways to use command line arguments passed to the script. 4 | 5 | ## sys.argv 6 | 7 | The simplest (and best for scripts that don't really take options) is to just read the raw strings passed to the python process. 8 | 9 | ```python 10 | import sys 11 | 12 | print(sys.argv[0]) # prints name of script 13 | print(sys.argv[1]) # prints 1st argument 14 | ... 15 | ``` 16 | 17 | All arguments passed to Python are collected in `sys.argv`. 18 | The first item is the path of the script that was passed to python. 19 | All subsequent items are the remaining arguments passed. 20 | Since `sys.argv` is just a list it is important to check the size before accessing elements, or surround accesses in `try`/`except` blocks in case the wrong number of arguments were passed. 21 | 22 | ## argparse 23 | 24 | The more complicated, but much more flexible, way of handling command line arguments is to use Python's built in module `argparse`. 25 | 26 | ```python 27 | import argparse 28 | 29 | parser = argparse.ArgumentParser() 30 | parser.add_argument('--foo') 31 | args = parser.parse_args() 32 | 33 | print(f'foo = {args.foo}') 34 | ``` 35 | 36 | When run with `python args.py --foo bar` this program will print out `"foo = bar"` 37 | 38 | This is only a very simple example of `argparse`. 39 | It is much more powerful, and a full tutorial can be found in the [official documentations](https://docs.python.org/3/howto/argparse.html) 40 | -------------------------------------------------------------------------------- /loops.md: -------------------------------------------------------------------------------- 1 | # Loops 2 | 3 | Python supports `for` and `while` loops. 4 | 5 | ## For loops 6 | 7 | `For` loops operate a little differently than what you might be used to in C or Java. 8 | They are similar to Java's "enhanced" `for` loops in that they traverse over an iterator. 9 | 10 | ```python 11 | for i in range(10): 12 | print(i) 13 | ``` 14 | 15 | This code will print every number between `0` (inclusive) and `10` (exclusive). 16 | The `range` function returns an iterator that goes from `0` to `9` and the `for` loop will run once for every value in the iterator. 17 | 18 | `range` can accept a start, end, and step value. `range(5, 13, 2)` will loop over `[5, 7, 9, 11]`. 19 | 20 | A `for` loop can traverse any iterable, such as a [list](data_structures.md#lists) or generator. 21 | 22 | ```python 23 | l = [8, 9, 2, 'hi'] 24 | for item in l: 25 | print(item) 26 | ``` 27 | 28 | This code will print every item in the list on its own line 29 | 30 | ## While loops 31 | 32 | `While` loops are far more conventional in Python. 33 | They simply take a boolean expression and continue to execute while it's true. 34 | 35 | ```python 36 | i = 0 37 | while i < 10: 38 | print(i) 39 | i += 1 40 | ``` 41 | 42 | ## Else clause 43 | 44 | Both `for` and `while` loops support an optional `else` clause. 45 | The code in the `else` clause is only executed if the loop ended normally, i.e. `break` was not called. 46 | 47 | ```python 48 | for name in names: 49 | if name == 'Jebediah': 50 | print('Found Jeb!') 51 | break 52 | else: 53 | print("Jeb wasn't in the list of names") 54 | ``` 55 | -------------------------------------------------------------------------------- /comprehensions.md: -------------------------------------------------------------------------------- 1 | # Comprehensions 2 | 3 | Some of Python's coolest features are list, dictionary, and generator comprehensions. 4 | These allow you to create complex lists and dictionaries using a concise syntax. 5 | 6 | ## List Comprehensions 7 | 8 | List comprehensions are created as such: 9 | 10 | ```python 11 | new_list = [ for in ] 12 | ``` 13 | 14 | The following two lists are identical 15 | 16 | ```python 17 | list1 = [] 18 | for i in range(10): 19 | list1.append(i*2) 20 | 21 | list2 = [i*2 for i in range(10)] 22 | ``` 23 | 24 | List comprehensions also allow you to filter the data before it is passed to the expression. 25 | 26 | ```python 27 | new_list = [i*2 for i in range(10) if i%2 == 0] 28 | ``` 29 | 30 | This lets the list comprehension replace both `map` and `filter` in a functional programming style. 31 | 32 | ## Dictionary Comprehensions 33 | 34 | Dictionary comprehensions are nearly identical to list comprehensions, except they create a dictionary instead of a list. 35 | 36 | ```python 37 | new_dict = {f'key #{i}': i for i in range(10)} 38 | ``` 39 | 40 | Dictionary comprehensions also support filtering 41 | 42 | ## Generator Expressions 43 | 44 | Generator expressions are exactly the same as list comprehensions except they return a generator and are created with parentheses instead of brackets. 45 | 46 | ```python 47 | new_gen = (i*2 for i in range(10)) 48 | ``` 49 | 50 | One shortcut with generator expressions is you can omit the parentheses if the generator is the only argument to a function. 51 | 52 | ```python 53 | print('\n'.join(str(i*2) for i in range(10))) 54 | ``` 55 | -------------------------------------------------------------------------------- /classes.md: -------------------------------------------------------------------------------- 1 | # Classes 2 | 3 | Python is an object oriented programming language. 4 | Accordingly, it is possible to create custom classes and define methods on them. 5 | New classes are defined using the `class` keyword. 6 | 7 | ```python 8 | class Adder: 9 | def __init__(self, initial_total): 10 | self.total = initial_total 11 | 12 | def add(self, n): 13 | self.total += n 14 | 15 | foo = Adder(5) 16 | print(foo.total) # prints "5" 17 | foo.add(10) 18 | print(foo.total) # prints "15" 19 | ``` 20 | 21 | This code creates a simple class with two methods defined. 22 | `__init__` is a special ["dunder" method](#dunder-methods) which acts as the class's initializer. 23 | This initializer takes two arguments: `self`, which is a reference to the new instance of the class, and `initial_total`, used to initialize `self.total`. 24 | All methods must have at least one parameter, conventionally called `self`, so they can reference the class they are part of. 25 | 26 | Python has no concept of public vs. private members of a class. 27 | Conventionally "private" members of a class are prefixed with an underscore, but this is purely convention and they are still accessible from outside the class. 28 | 29 | ## Dunder methods 30 | 31 | Python doesn't support overloading operators to provide advanced functionality to classes, but instead uses special double underscore, or dunder, methods. 32 | 33 | Some of the most common are `__init__`, `__str__`, `__lt__`, `__gt__`, and `__getitem__`. 34 | 35 | A full list of dunder methods and their usage can be found in the [official documentation](https://docs.python.org/3/reference/datamodel.html#special-method-names) 36 | -------------------------------------------------------------------------------- /functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Functions in Python are defined using the `def` keyword. 4 | 5 | ```python 6 | def foo(bar, baz): 7 | return bar + baz 8 | ``` 9 | 10 | This defines a function named `foo` that takes two arguments and returns their sum. 11 | 12 | Default arguments can be added with an equals sign as such: 13 | 14 | ```python 15 | def foo(bar, baz=42): 16 | return bar + baz 17 | ``` 18 | 19 | This function can be called with either one or two arguments. 20 | 21 | Python also supports variable argument lists by prepending a `*` to the last non-keyword argument 22 | 23 | ```python 24 | def foo(*bar): 25 | s = 0 26 | for v in bar: 27 | s += v 28 | 29 | return s 30 | ``` 31 | 32 | This function will take any number of arguments and return the sum of all of them. The arguments are collected in `bar` as a list. 33 | 34 | When calling functions it is possible to provide arguments out of order or skip some. 35 | 36 | ```python 37 | def greet(first_name, last_name='The Nameless', greeting='Hello'): 38 | print(greeting, first_name, last_name) 39 | ``` 40 | 41 | This function can be called with 1, 2, or 3 arguments, using defaults if not enough are supplied. 42 | If you wanted to supply a first name and a greeting with no last name you can use keyword arguments. 43 | 44 | ```python 45 | greet('Noah', greeting='Salutations') # prints "Salutations Noah The Nameless" 46 | ``` 47 | 48 | You can also collect a variable number of keyword arguments in a function with two `*`s. 49 | 50 | ```python 51 | def var(**kwargs): 52 | print(kwargs) 53 | ``` 54 | 55 | Keyword arguments will be passed into the function as a [dictionary](data_structures.md#dictionaries). 56 | 57 | ```python 58 | var(first='foo', second='bar') # prints "{'first': 'foo', 'second': 'bar'}" 59 | ``` 60 | -------------------------------------------------------------------------------- /string_formatting.md: -------------------------------------------------------------------------------- 1 | # String formatting 2 | 3 | Python supports 3 different ways to format strings. 4 | The first method is similar to C-style printf formatting, while the second two are different. 5 | 6 | ## % formatting 7 | 8 | Python supports C-style printf formatting using the `%` operator. 9 | 10 | ```python 11 | print('%s - %d' % ('foo', 42)) # prints "foo - 42" 12 | ``` 13 | 14 | Use of this style of formatting is discourage in favor of the two newer methods. 15 | 16 | ## .format 17 | 18 | In addition to C-style formatting, Python also has a new style for formatting strings. 19 | 20 | ```python 21 | print('{} - {}'.format('foo', 42)) # prints "foo - 42" 22 | ``` 23 | 24 | Each `{}` in the source string is a placeholder that is filled by an argument passed to `.format()`. 25 | The placeholders can also be named and filled in using keyword arguments passed to `.format()`. 26 | 27 | ```python 28 | print('{name} is {age} years old'.format(age=21, name='Noah')) # prints "Noah is 21 years old" 29 | ``` 30 | 31 | ## f-strings 32 | 33 | Python's third main string formatting tool is f-strings. 34 | By prepending a `f` to a string literal you can make it an f-string, which allows variable names to be inserted directly into the string. 35 | 36 | ```python 37 | name = 'Noah' 38 | age = 21 39 | print(f'{name} is {age} years old') # prints "Noah is 21 years old" 40 | ``` 41 | 42 | ## Advanced formatting 43 | 44 | Both `.format()` and f-strings support advanced formatting of their arguments. 45 | The general syntax is as such: 46 | 47 | ``` 48 | {[name or variable]:[options]} 49 | ``` 50 | 51 | The most useful is controlling how many digits are printed after a decimal point. 52 | 53 | ```python 54 | long_number = 42.5838294585 55 | print(f'{long_number:.3f}') # prints "42.584" 56 | ``` 57 | 58 | ## Further reading 59 | 60 | Much more information about formatting can be found at https://pyformat.info/ 61 | -------------------------------------------------------------------------------- /syntax.md: -------------------------------------------------------------------------------- 1 | # Syntax 2 | 3 | Python syntax shares many similarities with languages such as C and Java. 4 | 5 | ## Indentation 6 | The most confusing difference for newcomers to the language is significant whitespace. 7 | Unlike most languages, Python uses indentation to separate blocks instead of curly braces or other identifiers. 8 | 9 | ```python 10 | def foo(): 11 | print('bar') 12 | print('baz') 13 | ``` 14 | 15 | In this example `foo()` is a function that will print out `bar` when it is run. 16 | The line that prints `"baz"` is not a part of the function, because it is not indented to the same level. 17 | Many editors will display guides in the gutter to assist with keeping track of what block a particular line belongs to. 18 | 19 | Indentations should always be made using spaces; indenting with tabs is highly discouraged unless it is done to remain consistent with existing code. 20 | 21 | ## Literals 22 | 23 | ### Strings 24 | 25 | String literals are written Python with either single or double quote characters. `foo = 'bar'` and `foo = "bar"` are identical in function. 26 | Convention is to use single quotes unless a single quote appears in the string itself. 27 | When a quote character appears in a string it is easiest to use the other type of quote when making the string. It is also possible to just escape the contained quote. 28 | Both `foo = 'bar\'baz'` and `foo = "bar'baz"` create the same string. 29 | 30 | Python also supports raw strings which cannot have escape sequences in them. This is useful when a string would have many backslashes in it, such as a regex pattern. 31 | ```python 32 | patt = r'\d+(\.\d\d)?' 33 | ``` 34 | Notice the `r` prepended to the string which indicates a raw string. 35 | Without using a raw string this same pattern would have to look like this: 36 | ```python 37 | patt = '\\d+(\\.\\d\\d)?' 38 | ``` 39 | This obscures the original meaning of the string when being read. 40 | 41 | Python also supports a number of other prefixes, such as `f` for [formatting](formatting.md#f-strings). 42 | 43 | ### Numbers 44 | 45 | Python supports most common formats for expressing numeric literals. `0b`, `0o`, and `0x` prefixes will create binary, octal, and hex literals respectively. 46 | Float literals can be written in scientific notation using either `E` or `e`. 47 | Any number literal can be written with underscores between digits that do not affect the value at all. 48 | Underscores are used solely to make reading the number easier. 49 | 50 | All of these variables are equal to each other: 51 | ```python 52 | a = 0x2A 53 | b = 0o52 54 | c = 0b10_1010 55 | d = 4.2e1 56 | e = 4_2 57 | ``` 58 | -------------------------------------------------------------------------------- /c_interop.md: -------------------------------------------------------------------------------- 1 | # C Interop 2 | 3 | Python has numerous ways to interoperate with C code. 4 | `ctypes` is a built in module, but becomes messy for anything other than trivial usage. 5 | `cffi` is a third party module that makes C interop much simpler and streamlined. 6 | The third option is to write a python module directly in C. This method doesn't offer much benefit over using `cffi` and is far more involved to set up. 7 | 8 | ## CFFI 9 | 10 | `cffi` has two modes of operation. 11 | `ABI` mode and `API` mode. 12 | `ABI` mode is very easy to use, but can more easily fail at runtime if types are not defined correctly. 13 | `API` mode works by creating a python module in C and compiling it along with whatever library or sources you are trying to access through python. 14 | The disadvantage to this method is the requirement for a C compiler and an extra step to compile the module. 15 | It is marginally faster when making calls to C code however. 16 | It should be used when you anticipate making many time sensitive calls to C code. 17 | Otherwise `ABI` mode is probably sufficient. 18 | 19 | The following data were taken by running a fibonacci function implemented in both C and Python 1,000,000 times. 20 | Across multiple runs `ctypes` and `cffi (ABI)` times were nearly identical, with `cffi (API)` being faster, and the pure Python implementation being the slowest. 21 | When calculating a large fibonacci number once, all three C implementations were comparable. 22 | 23 | ``` 24 | ctypes: 0.5116191999995863 25 | cffi (ABI): 0.49985519999972894 26 | cffi (API): 0.2513962000002721 27 | python: 10.211897299999691 28 | ``` 29 | 30 | ### ABI mode 31 | 32 | The following code loads a shared library called `lib.so` and calls a function `foo` inside it. 33 | The most important part of this code is the call to `ffi.cdef()`. This tells `cffi` what functions are defined in the library and what their signatures are. 34 | The string passed is straight C code that can be pulled out of a header file. 35 | Being straight C code, the ending semicolon is important. 36 | 37 | ```python 38 | from cffi import FFI 39 | 40 | ffi = FFI() 41 | lib = ffi.dlopen('./lib.so') 42 | ffi.cdef('int foo(int bar);') 43 | 44 | print(lib.foo(7)) # call foo() from the C library 45 | ``` 46 | 47 | ### API mode 48 | 49 | Using the API mode of `cffi` is more involved. 50 | First you must create a separate script that will create and compile the Python module. 51 | The following script is an example for a hypothetical library in `foo.c`. 52 | Running `foo_extension_build.py` will create `_foo.c`, `_foo.o` and `_foo.so`. 53 | The last file is the actual library that can be imported by Python. 54 | The second snippet shows how to import and use this shared library. 55 | 56 | `foo_extension_build.py`: 57 | ```python 58 | from cffi import FFI 59 | 60 | ffi = FFI() 61 | ffi.cdef('int foo(int bar);') 62 | ffi.set_source('_foo', '#include "foo.h"', sources=['foo.c'], libraries=[]) 63 | 64 | if __name__ == '__main__': 65 | ffi.compile(verbose=True) 66 | ``` 67 | 68 | ```python 69 | from _foo.lib import foo 70 | 71 | print(foo(7)) # call foo() from the C library 72 | ``` 73 | -------------------------------------------------------------------------------- /data_structures.md: -------------------------------------------------------------------------------- 1 | # Data Structures 2 | 3 | Python has some very powerful data structures 4 | 5 | ## Lists 6 | 7 | Python's simplest data structure is a list. 8 | New lists can be created with square brackets. 9 | Items in a list are not constrained to be of the same type; a list can hold absolutely anything. 10 | 11 | ```python 12 | l = [5, 2, 3, ['foo', 'bar', 42, 5, 5], 3] 13 | ``` 14 | 15 | In this example `l` is a list with 5 elements. 16 | 17 | The length of a list is accessed with the `len` function. 18 | 19 | ```python 20 | print(len([4, 5, 6])) # prints "3" 21 | ``` 22 | 23 | Items from a list are accessed with the conventional bracket syntax. 24 | 25 | ```python 26 | def second(l): 27 | return l[1] 28 | ``` 29 | 30 | This function will return the second item in the list (and throw an error if it's shorter than that). 31 | 32 | ### Slices 33 | 34 | Python also has a slice syntax that allows more advanced selections from lists. 35 | 36 | ```python 37 | list[start:stop:step] 38 | ``` 39 | 40 | The `start`, `stop`, and `step` arguments are the same as in the `range` function. 41 | Any omitted parts use default values: 0 for the start, length for the stop, and `1` for the step. 42 | The new list that is returned is a shallow copy of the original. 43 | 44 | Slicing allows for some neat tricks: 45 | 46 | ```python 47 | copy = original[:] # Creates a shallow copy of original list 48 | reverse = original[::-1] # Creates a copy that is reversed 49 | ``` 50 | 51 | It is even possible to change chunks of a list by assigning with slices 52 | 53 | ```python 54 | original[::2] = [7]*int(len(original)/2) # replace every other value with 7 55 | ``` 56 | 57 | ## Tuples 58 | 59 | A tuple in Python is basically an immutable list. Tuples are created using parentheses. 60 | 61 | ```python 62 | t = (3, 4) 63 | ``` 64 | 65 | Like lists they can store any values and be heterogeneous. 66 | They also support all the same features as lists aside from changing elements. 67 | 68 | Tuples are typically used when returning multiple values from a function. 69 | 70 | ```python 71 | def foo(a, b): 72 | return a/b, a%b 73 | ``` 74 | 75 | The function `foo` in the block above returns a tuple with two values in it. 76 | Notice the parentheses are not necessary in this context. 77 | 78 | ## Dictionaries 79 | 80 | One of Python's most powerful data structure is the dictionary. 81 | Python's dictionary is equivalent to Java's HashMap. 82 | Dictionaries are created using curly braces. 83 | 84 | ```python 85 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 86 | ``` 87 | 88 | Accessing items from dictionaries looks just like list access. 89 | 90 | ```python 91 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 92 | print(d['key 2']) # prints "value 2" 93 | ``` 94 | 95 | Iterating over a dictionary will iterate over its keys 96 | 97 | ```python 98 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 99 | for key in d: 100 | print(key, d[key]) # prints every key and value in d 101 | ``` 102 | 103 | It is possible to iterate over both keys and values at the same time using the `.items()` method. 104 | 105 | ```python 106 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 107 | for key, value in d.items(): 108 | print(key, value) # prints every key and value in d 109 | ``` 110 | 111 | Again as with lists and tuples dicts can be heterogenous. 112 | 113 | ```python 114 | def foo(): 115 | print('foo') 116 | 117 | def bar(): 118 | print('bar') 119 | 120 | d = {'f1': foo, 'f2': bar, 'number': 23} 121 | 122 | d['f1']() # calls the function "foo" defined above 123 | ``` 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | * [Intro](#intro) 4 | * [Your first program](#your-first-program) 5 | * [Syntax](#syntax) 6 | * [Variables](#variables) 7 | * [Loops](#loops) 8 | * [Functions](#functions) 9 | * [Data structures](#data-structures) 10 | * [Classes](#classes) 11 | * [Exceptions](#exceptions) 12 | * [String formatting](#string-formatting) 13 | * [File I/O](#file-io) 14 | * [With statements](#with-statements) 15 | * [Command line arguments](#command-line-arguments) 16 | * [Comprehensions](#comprehensions) 17 | * [Type hinting](#type-hinting) 18 | * [C Interop](#c-interop) 19 | 20 | # Intro 21 | 22 | This guide is written for an audience who is already familiar 23 | with languages such as C and Java. 24 | 25 | ## Installing 26 | 27 | Install the latest version of Python 3 from [python.org](https://www.python.org/downloads/). 28 | Be sure to install Python 3 and not Python 2. 29 | Python 2 is deprecated and will not be maintained past January 1 2020. 30 | No new code should be written in Python 2. 31 | 32 | When installing be sure to add Python to the PATH so you can run scripts 33 | from the command line. 34 | 35 | Open up a command prompt and run `python` to ensure that Python is installed correctly. 36 | You should be greeted with a welcome message which includes the version of Python running. 37 | Ensure that this version number matches the one you installed. 38 | Type `exit()` to leave the interactive prompt. 39 | 40 | You can open an interactive prompt at any time to test out small snippets of code without having to make a new script. 41 | 42 | # Your first program 43 | 44 | The simplest Python program is just an empty text file. 45 | Python doesn't require any special functions or objects to be created unlike C or Java. 46 | The Python iterpreter will simply execute each line in the source file starting at the top. 47 | 48 | As is tradition, your first Python program will simply print `Hello world`. 49 | 50 | ```python 51 | print('Hello world') 52 | ``` 53 | 54 | Save this file as `hello.py` and run it on the command line with `python hello.py`. 55 | If you installed Python correctly then you should see `Hello world` printed and be returned to your prompt. 56 | 57 | # Syntax 58 | 59 | Python syntax shares many similarities with languages such as C and Java. 60 | 61 | ## Indentation 62 | The most confusing difference for newcomers to the language is significant whitespace. 63 | Unlike most languages, Python uses indentation to separate blocks instead of curly braces or other identifiers. 64 | 65 | ```python 66 | def foo(): 67 | print('bar') 68 | print('baz') 69 | ``` 70 | 71 | In this example `foo()` is a function that will print out `bar` when it is run. 72 | The line that prints `"baz"` is not a part of the function, because it is not indented to the same level. 73 | Many editors will display guides in the gutter to assist with keeping track of what block a particular line belongs to. 74 | 75 | Indentations should always be made using spaces; indenting with tabs is highly discouraged unless it is done to remain consistent with existing code. 76 | 77 | ## Literals 78 | 79 | ### Strings 80 | 81 | String literals are written Python with either single or double quote characters. `foo = 'bar'` and `foo = "bar"` are identical in function. 82 | Convention is to use single quotes unless a single quote appears in the string itself. 83 | When a quote character appears in a string it is easiest to use the other type of quote when making the string. It is also possible to just escape the contained quote. 84 | Both `foo = 'bar\'baz'` and `foo = "bar'baz"` create the same string. 85 | 86 | Python also supports raw strings which cannot have escape sequences in them. This is useful when a string would have many backslashes in it, such as a regex pattern. 87 | ```python 88 | patt = r'\d+(\.\d\d)?' 89 | ``` 90 | Notice the `r` prepended to the string which indicates a raw string. 91 | Without using a raw string this same pattern would have to look like this: 92 | ```python 93 | patt = '\\d+(\\.\\d\\d)?' 94 | ``` 95 | This obscures the original meaning of the string when being read. 96 | 97 | Python also supports a number of other prefixes, such as `f` for [formatting](#f-strings). 98 | 99 | ### Numbers 100 | 101 | Python supports most common formats for expressing numeric literals. `0b`, `0o`, and `0x` prefixes will create binary, octal, and hex literals respectively. 102 | Float literals can be written in scientific notation using either `E` or `e`. 103 | Any number literal can be written with underscores between digits that do not affect the value at all. 104 | Underscores are used solely to make reading the number easier. 105 | 106 | All of these variables are equal to each other: 107 | ```python 108 | a = 0x2A 109 | b = 0o52 110 | c = 0b10_1010 111 | d = 4.2e1 112 | e = 4_2 113 | ``` 114 | 115 | # Variables 116 | 117 | In Python variables can hold any type of value; it does not even have to be the same across a variable's lifetime. 118 | Variables in Python do not need to be defined prior to being assigned to. 119 | 120 | ```python 121 | foo = 'bar' 122 | ``` 123 | 124 | This snippet makes a new string variable with the value `'bar'`. 125 | 126 | Just about anything in Python can be assigned to a variable and passed around as such. 127 | 128 | ```python 129 | foo = print 130 | foo('Hello world') 131 | ``` 132 | 133 | This program will print `Hello world` just as if the print function had been called directly. 134 | 135 | # Loops 136 | 137 | Python supports `for` and `while` loops. 138 | 139 | ## For loops 140 | 141 | `For` loops operate a little differently than what you might be used to in C or Java. 142 | They are similar to Java's "enhanced" `for` loops in that they traverse over an iterator. 143 | 144 | ```python 145 | for i in range(10): 146 | print(i) 147 | ``` 148 | 149 | This code will print every number between `0` (inclusive) and `10` (exclusive). 150 | The `range` function returns an iterator that goes from `0` to `9` and the `for` loop will run once for every value in the iterator. 151 | 152 | `range` can accept a start, end, and step value. `range(5, 13, 2)` will loop over `[5, 7, 9, 11]`. 153 | 154 | A `for` loop can traverse any iterable, such as a [list](#lists) or generator. 155 | 156 | ```python 157 | l = [8, 9, 2, 'hi'] 158 | for item in l: 159 | print(item) 160 | ``` 161 | 162 | This code will print every item in the list on its own line 163 | 164 | ## While loops 165 | 166 | `While` loops are far more conventional in Python. 167 | They simply take a boolean expression and continue to execute while it's true. 168 | 169 | ```python 170 | i = 0 171 | while i < 10: 172 | print(i) 173 | i += 1 174 | ``` 175 | 176 | ## Else clause 177 | 178 | Both `for` and `while` loops support an optional `else` clause. 179 | The code in the `else` clause is only executed if the loop ended normally, i.e. `break` was not called. 180 | 181 | ```python 182 | for name in names: 183 | if name == 'Jebediah': 184 | print('Found Jeb!') 185 | break 186 | else: 187 | print("Jeb wasn't in the list of names") 188 | ``` 189 | 190 | # Functions 191 | 192 | Functions in Python are defined using the `def` keyword. 193 | 194 | ```python 195 | def foo(bar, baz): 196 | return bar + baz 197 | ``` 198 | 199 | This defines a function named `foo` that takes two arguments and returns their sum. 200 | 201 | Default arguments can be added with an equals sign as such: 202 | 203 | ```python 204 | def foo(bar, baz=42): 205 | return bar + baz 206 | ``` 207 | 208 | This function can be called with either one or two arguments. 209 | 210 | Python also supports variable argument lists by prepending a `*` to the last non-keyword argument 211 | 212 | ```python 213 | def foo(*bar): 214 | s = 0 215 | for v in bar: 216 | s += v 217 | 218 | return s 219 | ``` 220 | 221 | This function will take any number of arguments and return the sum of all of them. The arguments are collected in `bar` as a list. 222 | 223 | When calling functions it is possible to provide arguments out of order or skip some. 224 | 225 | ```python 226 | def greet(first_name, last_name='The Nameless', greeting='Hello'): 227 | print(greeting, first_name, last_name) 228 | ``` 229 | 230 | This function can be called with 1, 2, or 3 arguments, using defaults if not enough are supplied. 231 | If you wanted to supply a first name and a greeting with no last name you can use keyword arguments. 232 | 233 | ```python 234 | greet('Noah', greeting='Salutations') # prints "Salutations Noah The Nameless" 235 | ``` 236 | 237 | You can also collect a variable number of keyword arguments in a function with two `*`s. 238 | 239 | ```python 240 | def var(**kwargs): 241 | print(kwargs) 242 | ``` 243 | 244 | Keyword arguments will be passed into the function as a [dictionary](#dictionaries). 245 | 246 | ```python 247 | var(first='foo', second='bar') # prints "{'first': 'foo', 'second': 'bar'}" 248 | ``` 249 | 250 | # Data Structures 251 | 252 | Python has some very powerful data structures 253 | 254 | ## Lists 255 | 256 | Python's simplest data structure is a list. 257 | New lists can be created with square brackets. 258 | Items in a list are not constrained to be of the same type; a list can hold absolutely anything. 259 | 260 | ```python 261 | l = [5, 2, 3, ['foo', 'bar', 42, 5, 5], 3] 262 | ``` 263 | 264 | In this example `l` is a list with 5 elements. 265 | 266 | The length of a list is accessed with the `len` function. 267 | 268 | ```python 269 | print(len([4, 5, 6])) # prints "3" 270 | ``` 271 | 272 | Items from a list are accessed with the conventional bracket syntax. 273 | 274 | ```python 275 | def second(l): 276 | return l[1] 277 | ``` 278 | 279 | This function will return the second item in the list (and throw an error if it's shorter than that). 280 | 281 | ### Slices 282 | 283 | Python also has a slice syntax that allows more advanced selections from lists. 284 | 285 | ```python 286 | list[start:stop:step] 287 | ``` 288 | 289 | The `start`, `stop`, and `step` arguments are the same as in the `range` function. 290 | Any omitted parts use default values: 0 for the start, length for the stop, and `1` for the step. 291 | The new list that is returned is a shallow copy of the original. 292 | 293 | Slicing allows for some neat tricks: 294 | 295 | ```python 296 | copy = original[:] # Creates a shallow copy of original list 297 | reverse = original[::-1] # Creates a copy that is reversed 298 | ``` 299 | 300 | It is even possible to change chunks of a list by assigning with slices 301 | 302 | ```python 303 | original[::2] = [7]*int(len(original)/2) # replace every other value with 7 304 | ``` 305 | 306 | ## Tuples 307 | 308 | A tuple in Python is basically an immutable list. Tuples are created using parentheses. 309 | 310 | ```python 311 | t = (3, 4) 312 | ``` 313 | 314 | Like lists they can store any values and be heterogeneous. 315 | They also support all the same features as lists aside from changing elements. 316 | 317 | Tuples are typically used when returning multiple values from a function. 318 | 319 | ```python 320 | def foo(a, b): 321 | return a/b, a%b 322 | ``` 323 | 324 | The function `foo` in the block above returns a tuple with two values in it. 325 | Notice the parentheses are not necessary in this context. 326 | 327 | ## Dictionaries 328 | 329 | One of Python's most powerful data structure is the dictionary. 330 | Python's dictionary is equivalent to Java's HashMap. 331 | Dictionaries are created using curly braces. 332 | 333 | ```python 334 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 335 | ``` 336 | 337 | Accessing items from dictionaries looks just like list access. 338 | 339 | ```python 340 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 341 | print(d['key 2']) # prints "value 2" 342 | ``` 343 | 344 | Iterating over a dictionary will iterate over its keys 345 | 346 | ```python 347 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 348 | for key in d: 349 | print(key, d[key]) # prints every key and value in d 350 | ``` 351 | 352 | It is possible to iterate over both keys and values at the same time using the `.items()` method. 353 | 354 | ```python 355 | d = {'key 1': 'value 1', 'key 2': 'value 2'} 356 | for key, value in d.items(): 357 | print(key, value) # prints every key and value in d 358 | ``` 359 | 360 | Again as with lists and tuples dicts can be heterogenous. 361 | 362 | ```python 363 | def foo(): 364 | print('foo') 365 | 366 | def bar(): 367 | print('bar') 368 | 369 | d = {'f1': foo, 'f2': bar, 'number': 23} 370 | 371 | d['f1']() # calls the function "foo" defined above 372 | ``` 373 | 374 | # Classes 375 | 376 | Python is an object oriented programming language. 377 | Accordingly, it is possible to create custom classes and define methods on them. 378 | New classes are defined using the `class` keyword. 379 | 380 | ```python 381 | class Adder: 382 | def __init__(self, initial_total): 383 | self.total = initial_total 384 | 385 | def add(self, n): 386 | self.total += n 387 | 388 | foo = Adder(5) 389 | print(foo.total) # prints "5" 390 | foo.add(10) 391 | print(foo.total) # prints "15" 392 | ``` 393 | 394 | This code creates a simple class with two methods defined. 395 | `__init__` is a special ["dunder" method](#dunder-methods) which acts as the class's initializer. 396 | This initializer takes two arguments: `self`, which is a reference to the new instance of the class, and `initial_total`, used to initialize `self.total`. 397 | All methods must have at least one parameter, conventionally called `self`, so they can reference the class they are part of. 398 | 399 | Python has no concept of public vs. private members of a class. 400 | Conventionally "private" members of a class are prefixed with an underscore, but this is purely convention and they are still accessible from outside the class. 401 | 402 | ## Dunder methods 403 | 404 | Python doesn't support overloading operators to provide advanced functionality to classes, but instead uses special double underscore, or dunder, methods. 405 | 406 | Some of the most common are `__init__`, `__str__`, `__lt__`, `__gt__`, and `__getitem__`. 407 | 408 | A full list of dunder methods and their usage can be found in the [official documentation](https://docs.python.org/3/reference/datamodel.html#special-method-names) 409 | 410 | # Exceptions 411 | 412 | Exceptions in Python are handled using `try`/`except` blocks. 413 | 414 | ```python 415 | try: 416 | # Code that could throw an exception 417 | except: 418 | # Handle exception 419 | ``` 420 | 421 | This code will catch any error that is thrown inside the `try` block. 422 | It is possible to narrow down the exceptions that the `except` block will catch. 423 | 424 | ```python 425 | try: 426 | # Code that could throw an exception 427 | except IndexError: 428 | # Handle only an IndexError 429 | ``` 430 | 431 | If you need the exception object it is also possible to give it a name so it can be referenced in the `except` block. 432 | 433 | ```python 434 | try: 435 | # Code that could throw an exception 436 | except IndexError as e: 437 | # Handle only an IndexError 438 | ``` 439 | 440 | `Try`/`except` statements also support `else` and `finally` clauses. 441 | An `else` clause after `try`/`except` will execute only if the `except` clause does not run. 442 | A `finally` clause will always run, regardless of whether an exception is thrown or not. 443 | 444 | ```python 445 | try: 446 | # Code that could throw an exception 447 | except: 448 | # Handle exception 449 | else: 450 | # Only runs if except clause doesn't 451 | finally: 452 | # Always runs 453 | ``` 454 | 455 | `Finally` clauses are usually used to clean up resources used in a `try` block. 456 | 457 | # String formatting 458 | 459 | Python supports 3 different ways to format strings. 460 | The first method is similar to C-style printf formatting, while the second two are different. 461 | 462 | ## % formatting 463 | 464 | Python supports C-style printf formatting using the `%` operator. 465 | 466 | ```python 467 | print('%s - %d' % ('foo', 42)) # prints "foo - 42" 468 | ``` 469 | 470 | Use of this style of formatting is discourage in favor of the two newer methods. 471 | 472 | ## .format 473 | 474 | In addition to C-style formatting, Python also has a new style for formatting strings. 475 | 476 | ```python 477 | print('{} - {}'.format('foo', 42)) # prints "foo - 42" 478 | ``` 479 | 480 | Each `{}` in the source string is a placeholder that is filled by an argument passed to `.format()`. 481 | The placeholders can also be named and filled in using keyword arguments passed to `.format()`. 482 | 483 | ```python 484 | print('{name} is {age} years old'.format(age=21, name='Noah')) # prints "Noah is 21 years old" 485 | ``` 486 | 487 | ## f-strings 488 | 489 | Python's third main string formatting tool is f-strings. 490 | By prepending a `f` to a string literal you can make it an f-string, which allows variable names to be inserted directly into the string. 491 | 492 | ```python 493 | name = 'Noah' 494 | age = 21 495 | print(f'{name} is {age} years old') # prints "Noah is 21 years old" 496 | ``` 497 | 498 | ## Advanced formatting 499 | 500 | Both `.format()` and f-strings support advanced formatting of their arguments. 501 | The general syntax is as such: 502 | 503 | ``` 504 | {[name or variable]:[options]} 505 | ``` 506 | 507 | The most useful is controlling how many digits are printed after a decimal point. 508 | 509 | ```python 510 | long_number = 42.5838294585 511 | print(f'{long_number:.3f}') # prints "42.584" 512 | ``` 513 | 514 | ## Further reading 515 | 516 | Much more information about formatting can be found at https://pyformat.info/ 517 | 518 | # File I/O 519 | 520 | File I/O is incredibly easy in Python. 521 | A file object is created by calling the `open()` function. 522 | At its simplest `open()` takes a single argument, the name of the file to open. 523 | 524 | File objects have numerous methods for reading the contents of the file. Additionally, a file object can be iterated over directly, yielding complete lines. 525 | 526 | ```python 527 | f = open('file.txt') 528 | content = f.read() 529 | f.close() 530 | ``` 531 | 532 | ```python 533 | f = open('file.txt') 534 | lines = f.readlines() 535 | f.close() 536 | ``` 537 | 538 | ```python 539 | f = open('file.txt') 540 | for line in f: 541 | ... 542 | f.close() 543 | ``` 544 | 545 | A complete list of file methods can be found in the [official documentation](https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects) 546 | 547 | When opening a file for writing just pass `'w'` as the second argument to `open()`. 548 | 549 | ```python 550 | f = open('file.txt', 'w') 551 | f.write('Test message') 552 | f.close() 553 | ``` 554 | 555 | Be sure to close file objects when you are done using them to prevent resource leaks. 556 | Alternatively use [with statements](#with-statements) to handle it automatically. 557 | 558 | # With statements 559 | 560 | `With` statements enable files (and other objects) to automatically be closed when they are no longer needed. 561 | 562 | ```python 563 | with open('file.txt') as f: 564 | print(f.read()) 565 | ``` 566 | 567 | The file `f` will automatically be closed at the end of the `with` statement's code block 568 | 569 | # Command line arguments 570 | 571 | Python has multiple ways to use command line arguments passed to the script. 572 | 573 | ## sys.argv 574 | 575 | The simplest (and best for scripts that don't really take options) is to just read the raw strings passed to the python process. 576 | 577 | ```python 578 | import sys 579 | 580 | print(sys.argv[0]) # prints name of script 581 | print(sys.argv[1]) # prints 1st argument 582 | ... 583 | ``` 584 | 585 | All arguments passed to Python are collected in `sys.argv`. 586 | The first item is the path of the script that was passed to python. 587 | All subsequent items are the remaining arguments passed. 588 | Since `sys.argv` is just a list it is important to check the size before accessing elements, or surround accesses in `try`/`except` blocks in case the wrong number of arguments were passed. 589 | 590 | ## argparse 591 | 592 | The more complicated, but much more flexible, way of handling command line arguments is to use Python's built in module `argparse`. 593 | 594 | ```python 595 | import argparse 596 | 597 | parser = argparse.ArgumentParser() 598 | parser.add_argument('--foo') 599 | args = parser.parse_args() 600 | 601 | print(f'foo = {args.foo}') 602 | ``` 603 | 604 | When run with `python args.py --foo bar` this program will print out `"foo = bar"` 605 | 606 | This is only a very simple example of `argparse`. 607 | It is much more powerful, and a full tutorial can be found in the [official documentations](https://docs.python.org/3/howto/argparse.html) 608 | 609 | # Comprehensions 610 | 611 | Some of Python's coolest features are list, dictionary, and generator comprehensions. 612 | These allow you to create complex lists and dictionaries using a concise syntax. 613 | 614 | ## List Comprehensions 615 | 616 | List comprehensions are created as such: 617 | 618 | ```python 619 | new_list = [ for in ] 620 | ``` 621 | 622 | The following two lists are identical 623 | 624 | ```python 625 | list1 = [] 626 | for i in range(10): 627 | list1.append(i*2) 628 | 629 | list2 = [i*2 for i in range(10)] 630 | ``` 631 | 632 | List comprehensions also allow you to filter the data before it is passed to the expression. 633 | 634 | ```python 635 | new_list = [i*2 for i in range(10) if i%2 == 0] 636 | ``` 637 | 638 | This lets the list comprehension replace both `map` and `filter` in a functional programming style. 639 | 640 | ## Dictionary Comprehensions 641 | 642 | Dictionary comprehensions are nearly identical to list comprehensions, except they create a dictionary instead of a list. 643 | 644 | ```python 645 | new_dict = {f'key #{i}': i for i in range(10)} 646 | ``` 647 | 648 | Dictionary comprehensions also support filtering 649 | 650 | ## Generator Expressions 651 | 652 | Generator expressions are exactly the same as list comprehensions except they return a generator and are created with parentheses instead of brackets. 653 | 654 | ```python 655 | new_gen = (i*2 for i in range(10)) 656 | ``` 657 | 658 | One shortcut with generator expressions is you can omit the parentheses if the generator is the only argument to a function. 659 | 660 | ```python 661 | print('\n'.join(str(i*2) for i in range(10))) 662 | ``` 663 | 664 | # Type hinting 665 | 666 | While Python is a dynamically typed language and doesn't have explicit types, it does have type hinting. 667 | When used with tools such as [mypy] it is possible to combine the flexibility of dynamic typing with the safety of static typing. 668 | 669 | ```python 670 | def foo(bar: str) -> int: 671 | return len(bar) 672 | ``` 673 | 674 | This is a function that takes a string and returns an integer. 675 | Type hints for function parameters are placed after a colon after the parameter name. 676 | Type hints for function return values are placed after an arrow after the parameter list, but before the ending colon. 677 | 678 | Python itself does nothing with the type hints. 679 | It would be perfectly valid to pass a list to the `foo` function defined above. 680 | Type hints are purely decorative unless used with an external tools such as [mypy]. 681 | 682 | It is also possible to access the type hints from within Python code. 683 | The `__annotations__` attribute of the function contains all the type hinting information. 684 | 685 | ```python 686 | def foo(bar: str) -> int: 687 | return len(bar) 688 | 689 | print(foo.__annotations__) # prints "{'bar': , 'return': }" 690 | ``` 691 | 692 | [mypy]: http://mypy-lang.org/ 693 | 694 | # C Interop 695 | 696 | Python has numerous ways to interoperate with C code. 697 | `ctypes` is a built in module, but becomes messy for anything other than trivial usage. 698 | `cffi` is a third party module that makes C interop much simpler and streamlined. 699 | The third option is to write a python module directly in C. This method doesn't offer much benefit over using `cffi` and is far more involved to set up. 700 | 701 | ## CFFI 702 | 703 | `cffi` has two modes of operation. 704 | `ABI` mode and `API` mode. 705 | `ABI` mode is very easy to use, but can more easily fail at runtime if types are not defined correctly. 706 | `API` mode works by creating a python module in C and compiling it along with whatever library or sources you are trying to access through python. 707 | The disadvantage to this method is the requirement for a C compiler and an extra step to compile the module. 708 | It is marginally faster when making calls to C code however. 709 | It should be used when you anticipate making many time sensitive calls to C code. 710 | Otherwise `ABI` mode is probably sufficient. 711 | 712 | The following data were taken by running a fibonacci function implemented in both C and Python 1,000,000 times. 713 | Across multiple runs `ctypes` and `cffi (ABI)` times were nearly identical, with `cffi (API)` being faster, and the pure Python implementation being the slowest. 714 | When calculating a large fibonacci number once, all three C implementations were comparable. 715 | 716 | ``` 717 | ctypes: 0.5116191999995863 718 | cffi (ABI): 0.49985519999972894 719 | cffi (API): 0.2513962000002721 720 | python: 10.211897299999691 721 | ``` 722 | 723 | ### ABI mode 724 | 725 | The following code loads a shared library called `lib.so` and calls a function `foo` inside it. 726 | The most important part of this code is the call to `ffi.cdef()`. This tells `cffi` what functions are defined in the library and what their signatures are. 727 | The string passed is straight C code that can be pulled out of a header file. 728 | Being straight C code, the ending semicolon is important. 729 | 730 | ```python 731 | from cffi import FFI 732 | 733 | ffi = FFI() 734 | lib = ffi.dlopen('./lib.so') 735 | ffi.cdef('int foo(int bar);') 736 | 737 | print(lib.foo(7)) # call foo() from the C library 738 | ``` 739 | 740 | ### API mode 741 | 742 | Using the API mode of `cffi` is more involved. 743 | First you must create a separate script that will create and compile the Python module. 744 | The following script is an example for a hypothetical library in `foo.c`. 745 | Running `foo_extension_build.py` will create `_foo.c`, `_foo.o` and `_foo.so`. 746 | The last file is the actual library that can be imported by Python. 747 | The second snippet shows how to import and use this shared library. 748 | 749 | `foo_extension_build.py`: 750 | ```python 751 | from cffi import FFI 752 | 753 | ffi = FFI() 754 | ffi.cdef('int foo(int bar);') 755 | ffi.set_source('_foo', '#include "foo.h"', sources=['foo.c'], libraries=[]) 756 | 757 | if __name__ == '__main__': 758 | ffi.compile(verbose=True) 759 | ``` 760 | 761 | ```python 762 | from _foo.lib import foo 763 | 764 | print(foo(7)) # call foo() from the C library 765 | ``` 766 | --------------------------------------------------------------------------------