# Tips and tricks from my Telegram-channel @pythonetc, June 2019

![](https://habrastorage.org/r/w780q1/webt/uk/b6/xs/ukb6xsjkyjzkexotnq1jqjdfmw0.jpeg)  

It is a new selection of tips and tricks about Python and programming from my Telegram-channel @pythonetc.  

← [Previous publications](https://habr.com/ru/search/?q=%5Bpythonetc%20eng%5D&target_type=posts)  

![](https://habrastorage.org/r/w1560/webt/ix/fw/hm/ixfwhm2i27b6uy36zmz5qlvjqdc.png)  

The `\` symbol in regular string have special meaning. `\t` is tab character, `\r` is carriage return and so on.  

You can use raw-strings to disable this behaviour. `r'\t'` is just backslash and `t`.  

You obviously can’t use `'` inside `r'...'`. However, it still can be escaped by `\`, but `\` is preserved in the string:  

```
>>> print(r'It\'s insane!')
It\'s insane!
```

![](https://habrastorage.org/r/w1560/webt/ix/fw/hm/ixfwhm2i27b6uy36zmz5qlvjqdc.png)  

List comprehensions may contain more than one `for` and `if` clauses:  

```
In : [(x, y) for x in range(3) for y in range(3)]
Out: [
    (0, 0), (0, 1), (0, 2),
    (1, 0), (1, 1), (1, 2),
    (2, 0), (2, 1), (2, 2)
]
In : [
    (x, y)
    for x in range(3)
    for y in range(3)
    if x != 0
    if y != 0
]
Out: [(1, 1), (1, 2), (2, 1), (2, 2)]
```

Also, any expression within `for` and `if` may use all the variables that are defined before:  

```
In : [
    (x, y)
    for x in range(3)
    for y in range(x + 2)
    if x != y
]
Out: [
    (0, 1),
    (1, 0), (1, 2),
    (2, 0), (2, 1), (2, 3)
]
```

You can mix `if`s and `for`s however you want:  

```
In : [
    (x, y)
    for x in range(5)
    if x % 2
    for y in range(x + 2)
    if x != y
]
Out: [
    (1, 0), (1, 2),
    (3, 0), (3, 1), (3, 2), (3, 4)
]
```

![](https://habrastorage.org/r/w1560/webt/ix/fw/hm/ixfwhm2i27b6uy36zmz5qlvjqdc.png)  

The `sorted` function allows you to provide custom method for sorting. It’s done with the `key` argument, which describes how to convert original values to values that are actually compared:  

```
>>> x = [dict(name='Vadim', age=29), dict(name='Alex', age=4)]
>>> sorted(x, key=lambda v: v['age'])
[{'age': 4, 'name': 'Alex'}, {'age': 29, 'name': 'Vadim'}]
```

Alas, not all libraries that work with comparison support something like this `key` argument. Notable examples are `heapq` (partial support) and `bisect` (no support).  

There are two ways to deal with the situation. The first is to use custom objects that do support proper comparsion:  

```
>>> class User:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...     def __lt__(self, other):
...         return self.age < other.age
...
>>> x = [User('Vadim', 29), User('Alex', 4)]
>>> [x.name for x in sorted(x)]
['Alex', 'Vadim']
```

However, you may have to create several versions of such classes since there are more than one way to compare objects. It can be tiresome, but can be easily solved by the second way.  

Instead of creating custom objects you may use tuples `(a, b)` where `a` is the value to compare (a.k.a. prioirty) and `b` is the original value:  

```
>>> users = [dict(name='Vadim', age=29), dict(name='Alex', age=4)]
>>> to_sort = [(u['age'], u) for u in users]
>>> [x[1]['name'] for x in sorted(to_sort)]
['Alex', 'Vadim']
```

![](https://habrastorage.org/r/w1560/webt/ix/fw/hm/ixfwhm2i27b6uy36zmz5qlvjqdc.png)  

The difference between function definition and generator definition is the presence of the `yield` keyword in the function body:  

```
In : def f():
...:     pass
...:

In : def g():
...:     yield
...:

In : type(f())
Out: NoneType

In : type(g())
Out: generator
```

That means that in order to create an empty generator you have to do something like this:  

```
In : def g():
...:     if False:
...:         yield
...:

In : list(g())
Out: []
```

However, since `yield from` supports simple iterators that better looking version would be this:  

```
def g():
    yield from []
```

![](https://habrastorage.org/r/w1560/webt/ix/fw/hm/ixfwhm2i27b6uy36zmz5qlvjqdc.png)  

In Python, you can chain comparison operators:  

```
>>> 0 < 1 < 2
True
>>> 0 < 1 < 0
False
```

Such chains don’t have to be mathematically valid, you can mix `>` and `<`:  

```
>>> 0 < 1 > 2
False
>>> 0 < 1 < 2 > 1 > 0
True
```

Other operators such as `==`, `is` and `in` are also supported:  

```
>>> [] is not 3 in [1, 2, 3]
True
```

Every operator is applied to the two nearest operands. `a OP1 b OP2 c` is strictly equal to `(a OP1 b) AND (b OP2 c)`. No comparison between `a` and `c` is implied:  

```
class Spy:
    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        print(f'{self.x} == {other.x}')
        return self.x == other.x

    def __ne__(self, other):
        print(f'{self.x} != {other.x}')
        return self.x != other.x

    def __lt__(self, other):
        print(f'{self.x} < {other.x}')
        return self.x < other.x

    def __le__(self, other):
        print(f'{self.x} <= {other.x}')
        return self.x <= other.x

    def __gt__(self, other):
        print(f'{self.x} > {other.x}')
        return self.x > other.x

    def __ge__(self, other):
        print(f'{self.x} >= {other.x}')
        return self.x >= other.x

s1 = Spy(1)
s2 = Spy(2)
s3 = Spy(3)

print(s1 is s1 < s2 <= s3 == s3)
```

Output:  

```
1 < 2
2 <= 3
3 == 3
True

```