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

ukb6xsjkyjzkexotnq1jqjdfmw0.jpeg


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

← Previous publications

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 sill can be escaped by \, but \ is preserved in the string:

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


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 ifs and fors 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)
]


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']


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 []


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

© Habrahabr.ru