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


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

    Previous publications


    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!


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


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


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


    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
    

    Also popular now: