From Scratch¶
The main goal of this cheat sheet is to collect some common and basic semantics or snippets. The cheat sheet includes some syntax, which we have already known but still ambiguous in our mind, or some snippets, which we google them again and again. In addition, because the end Of life date for Python 2 is coming. Most of the snippets are mainly based on Python 3’s syntax.
Hello world!¶
When we start to learn a new language, we usually learn from printing
Hello world!. In Python, we can use another way to print the message by
importing __hello__
module. The source code can be found on
frozen.c.
>>> print("Hello world!")
Hello world!
>>> import __hello__
Hello world!
>>> import __phello__
Hello world!
>>> import __phello__.spam
Hello world!
Python Version¶
It is important for a programmer to know current Python version because
not every syntax will work in the current version. In this case, we can get the
Python version by python -V
or using the module, sys
.
>>> import sys
>>> print(sys.version)
3.7.1 (default, Nov 6 2018, 18:46:03)
[Clang 10.0.0 (clang-1000.11.45.5)]
We can also use platform.python_version
to get Python version.
>>> import platform
>>> platform.python_version()
'3.7.1'
Sometimes, checking the current Python version is important because we may want
to enable some features in some specific versions. sys.version_info
provides more
detail information about the interpreter. We can use it to compare with the
version we want.
>>> import sys
>>> sys.version_info >= (3, 6)
True
>>> sys.version_info >= (3, 7)
False
Ellipsis¶
Ellipsis is a
built-in constant. After Python 3.0, we case use ...
as Ellipsis
. It
may be the most enigmatic constant in Python. Based on the official document,
we can use it to extend slicing syntax. Nevertheless, there are some other
conventions in type hinting, stub files, or function expressions.
>>> ...
Ellipsis
>>> ... == Ellipsis
True
>>> type(...)
<class 'ellipsis'>
The following snippet shows that we can use the ellipsis to represent a function or a class which has not implemented yet.
>>> class Foo: ...
...
>>> def foo(): ...
...
if … elif … else¶
The if statements are used to control the code flow. Instead of using
switch
or case
statements control the logic of the code, Python uses
if ... elif ... else
sequence. Although someone proposes we can use
dict
to achieve switch
statements, this solution may introduce
unnecessary overhead such as creating disposable dictionaries and undermine
a readable code. Thus, the solution is not recommended.
>>> import random
>>> num = random.randint(0, 10)
>>> if num < 3:
... print("less than 3")
... elif num < 5:
... print("less than 5")
... else:
... print(num)
...
less than 3
for Loop¶
In Python, we can access iterable object’s items directly through the
for statement. If we need to get indexes and items of an iterable object
such as list or tuple at the same time, using enumerate
is better than
range(len(iterable))
. Further information can be found on
Looping Techniques.
>>> for val in ["foo", "bar"]:
... print(val)
...
foo
bar
>>> for idx, val in enumerate(["foo", "bar", "baz"]):
... print(idx, val)
...
(0, 'foo')
(1, 'bar')
(2, 'baz')
for … else …¶
It may be a little weird when we see the else
belongs to a for
loop at
the first time. The else
clause can assist us to avoid using flag
variables in loops. A loop’s else
clause runs when no break occurs.
>>> for _ in range(5):
... pass
... else:
... print("no break")
...
no break
The following snippet shows the difference between using a flag variable and
the else
clause to control the loop. We can see that the else
does not
run when the break
occurs in the loop.
>>> is_break = False
>>> for x in range(5):
... if x % 2 == 0:
... is_break = True
... break
...
>>> if is_break:
... print("break")
...
break
>>> for x in range(5):
... if x % 2 == 0:
... print("break")
... break
... else:
... print("no break")
...
break
Using range
¶
The problem of range
in Python 2 is that range
may take up a lot of
memory if we need to iterate a loop many times. Consequently, using xrange
is recommended in Python 2.
>>> import platform
>>> import sys
>>> platform.python_version()
'2.7.15'
>>> sys.getsizeof(range(100000000))
800000072
>>> sys.getsizeof(xrange(100000000))
40
In Python 3, the built-in function range
returns an iterable range object
instead of a list. The behavior of range
is the same as the xrange
in
Python 2. Therefore, using range
do not take up huge memory anymore if we
want to run a code block many times within a loop. Further information can be
found on PEP 3100.
>>> import platform
>>> import sys
>>> platform.python_version()
'3.7.1'
>>> sys.getsizeof(range(100000000))
48
while … else …¶
The else
clause belongs to a while loop serves the same purpose as the
else
clause in a for loop. We can observe that the else
does not run
when the break
occurs in the while loop.
>>> n = 0
>>> while n < 5:
... if n == 3:
... break
... n += 1
... else:
... print("no break")
...
The do while
Statement¶
There are many programming languages such as C/C++, Ruby, or Javascript,
provide the do while
statement. In Python, there is no do while
statement. However, we can place the condition and the break
at the end of
a while
loop to achieve the same thing.
>>> n = 0
>>> while True:
... n += 1
... if n == 5:
... break
...
>>> n
5
try … except … else …¶
Most of the time, we handle errors in except
clause and clean up resources
in finally
clause. Interestingly, the try
statement also provides an
else
clause for us to avoid catching an exception which was raised by the
code that should not be protected by try ... except
. The else
clause
runs when no exception occurs between try
and except
.
>>> try:
... print("No exception")
... except:
... pass
... else:
... print("Success")
...
No exception
Success
String¶
Unlike other programming languages, Python does not support string’s item assignment directly. Therefore, if it is necessary to manipulate string’s items, e.g., swap items, we have to convert a string to a list and do a join operation after a series item assignments finish.
>>> a = "Hello Python"
>>> l = list(a)
>>> l[0], l[6] = 'h', 'p'
>>> ''.join(l)
'hello python'
List¶
Lists are versatile containers. Python provides a lot of ways such as negative index, slicing statement, or list comprehension to manipulate lists. The following snippet shows some common operations of lists.
>>> a = [1, 2, 3, 4, 5]
>>> a[-1] # negative index
5
>>> a[1:] # slicing
[2, 3, 4, 5]
>>> a[1:-1]
[2, 3, 4]
>>> a[1:-1:2]
[2, 4]
>>> a[::-1] # reverse
[5, 4, 3, 2, 1]
>>> a[0] = 0 # set an item
>>> a
[0, 2, 3, 4, 5]
>>> a.append(6) # append an item
>>> a
[0, 2, 3, 4, 5, 6]
>>> del a[-1] # del an item
>>> a
[0, 2, 3, 4, 5]
>>> b = [x for x in range(3)] # list comprehension
>>> b
[0, 1, 2]
>>> a + b # add two lists
[0, 2, 3, 4, 5, 0, 1, 2]
Dict¶
Dictionaries are key-value pairs containers. Like lists, Python supports many ways such as dict comprehensions to manipulate dictionaries. After Python 3.6, dictionaries preserve the insertion order of keys. The Following snippet shows some common operations of dictionaries.
>>> d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
>>> d
{'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
>>> d['timmy'] = "yellow" # set data
>>> d
{'timmy': 'yellow', 'barry': 'green', 'guido': 'blue'}
>>> del d['guido'] # del data
>>> d
>>> 'guido' in d # contain data
False
{'timmy': 'yellow', 'barry': 'green'}
>>> {k: v for k ,v in d.items()} # dict comprehension
{'timmy': 'yellow', 'barry': 'green'}
>>> d.keys() # list all keys
dict_keys(['timmy', 'barry'])
>>> d.values() # list all values
dict_values(['yellow', 'green'])
Function¶
Defining a function in Python is flexible. We can define a function with function documents, default values, arbitrary arguments, keyword arguments, keyword-only arguments, and so on. The Following snippet shows some common expressions to define functions.
def foo_with_doc():
"""Documentation String."""
def foo_with_arg(arg): ...
def foo_with_args(*arg): ...
def foo_with_kwarg(a, b="foo"): ...
def foo_with_args_kwargs(*args, **kwargs): ...
def foo_with_kwonly(a, b, *, k): ... # python3
def foo_with_annotations(a: int) -> int: ... # python3
Function Annotations¶
Instead of writing string documents in functions to hint the type of parameters and return values, we can denote types by function annotations. Function annotations which the details can be found on PEP 3017 and PEP 484 were introduced in Python 3.0. They are an optional feature in Python 3. Using function annotations will lose compatibility in Python 2. We can solve this issue by stub files. In addition, we can do static type checking through mypy.
>>> def fib(n: int) -> int:
... a, b = 0, 1
... for _ in range(n):
... b, a = a + b, b
... return a
...
>>> fib(10)
55
Generators¶
Python uses the yield
statement to define a generator function. In
other words, when we call a generator function, the generator function will
return a generator instead of return values for creating an iterator.
>>> def fib(n):
... a, b = 0, 1
... for _ in range(n):
... yield a
... b, a = a + b, b
...
>>> g = fib(10)
>>> g
<generator object fib at 0x10b240c78>
>>> for f in fib(5):
... print(f)
...
0
1
1
2
3
Generator Delegation¶
Python 3.3 introduced yield from
expression. It allows a generator to
delegate parts of operations to another generator. In other words, we can
yield a sequence from other generators in the current generator function.
Further information can be found on PEP 380.
>>> def fib(n):
... a, b = 0, 1
... for _ in range(n):
... yield a
... b, a = a + b, b
...
>>> def fibonacci(n):
... yield from fib(n)
...
>>> [f for f in fibonacci(5)]
[0, 1, 1, 2, 3]
Class¶
Python supports many common features such as class documents, multiple inheritance, class variables, instance variables, static method, class method, and so on. Furthermore, Python provides some special methods for programmers to implement iterators, context manager, etc. The following snippet displays common definition of a class.
class A: ...
class B: ...
class Foo(A, B):
"""A class document."""
foo = "class variable"
def __init__(self, v):
self.attr = v
self.__private = "private var"
@staticmethod
def bar_static_method(): ...
@classmethod
def bar_class_method(cls): ...
def bar(self):
"""A method document."""
def bar_with_arg(self, arg): ...
def bar_with_args(self, *args): ...
def bar_with_kwarg(self, kwarg="bar"): ...
def bar_with_args_kwargs(self, *args, **kwargs): ...
def bar_with_kwonly(self, *, k): ...
def bar_with_annotations(self, a: int): ...
async
/ await
¶
async
and await
syntax was introduced from Python 3.5. They were
designed to be used with an event loop. Some other features such as the
asynchronous generator were implemented in later versions.
A coroutine function
(async def
) are used to create a coroutine for an event loop. Python
provides a built-in module, asyncio, to write a concurrent code through
async
/await
syntax. The following snippet shows a simple example of
using asyncio. The code must be run on Python 3.7 or above.
import asyncio
async def http_ok(r, w):
head = b"HTTP/1.1 200 OK\r\n"
head += b"Content-Type: text/html\r\n"
head += b"\r\n"
body = b"<html>"
body += b"<body><h1>Hello world!</h1></body>"
body += b"</html>"
_ = await r.read(1024)
w.write(head + body)
await w.drain()
w.close()
async def main():
server = await asyncio.start_server(
http_ok, "127.0.0.1", 8888
)
async with server:
await server.serve_forever()
asyncio.run(main())
Avoid exec
and eval
¶
The following snippet shows how to use the built-in function exec
. Yet,
using exec
and eval
are not recommended because of some security issues
and unreadable code for a human. Further reading can be found on
Be careful with exec and eval in Python
and Eval really is dangerous
>>> py = '''
... def fib(n):
... a, b = 0, 1
... for _ in range(n):
... b, a = b + a, b
... return a
... print(fib(10))
... '''
>>> exec(py, globals(), locals())
55