@Pythonetc June 2019


    This is the eleventh selection of Python tips and programming from my @pythonetc feed.

    Previous collections


    A character \in a regular line has special meaning. \t- this is a tab character, \r- line break, and so on.

    To disable this behavior, you can use raw strings. Then it r'\t'will turn into just a backslash and t.

    Obviously, you cannot use it 'internally r'...'. And although this restriction can be circumvented with \, however, the line \will still remain:

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


    List generators can contain more than one pair of expressions forand if:

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

    In addition, any expression is inside forand ifcan use all previously defined variables:

    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 ifand forin its sole discretion:

    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 function sortedallows you to specify custom sorting methods. This is done using an argument keythat describes how to convert the original values ​​for later comparison:

    >>> 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 working with comparison support an argument key. Of those that are rumored, you can mention heapq(partial support) and bisect(no support).

    There are two ways to go in this situation. You can use custom objects that support proper comparison:

    >>> 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 need to create several versions of such classes, because objects can be compared in different ways. This can be inconvenient, so there is a second way.

    Instead of creating custom objects, you can use tuples (a, b)in which ais the value to compare (priority), and the boriginal 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 the definition and the function generator is the presence of a keyword yieldin the function body:

    In : def f():
    ...:     pass
    ...:
    In : def g():
    ...:     yield
    ...:
    In : type(f())
    Out: NoneType
    In : type(g())
    Out: generator

    This means that to create an empty generator you need to do this:

    In : def g():
    ...:     if False:
    ...:             yield
    ...:
    In : list(g())
    Out: []

    But since it yield fromsupports simple iterators, there is a nicer version:

    def g():
        yield from []


    In Python, you can create chains of comparison operators:

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

    Such chains do not have to be mathematically correct, you can mix >and <:

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

    Operators are also supported ==. isand in:

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

    Each operator applies to two adjacent operands. a OP1 b OP2 cstrictly equivalent (a OP1 b) AND (b OP2 c). Comparison aand cnot performed:

    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)

    Result:

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

    Also popular now: