Skip to content

Latest commit

 

History

History
552 lines (436 loc) · 15.4 KB

python-basic.rst

File metadata and controls

552 lines (436 loc) · 15.4 KB

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 weired 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