@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
for
and 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
for
and if
can 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
if
and for
in 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
sorted
allows you to specify custom sorting methods. This is done using an argument key
that 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 a
is the value to compare (priority), and the b
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 the definition and the function generator is the presence of a keyword
yield
in 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 from
supports 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
==
. is
and in
:>>> [] is not 3 in [1, 2, 3]
True
Each operator applies to two adjacent operands.
a OP1 b OP2 c
strictly equivalent (a OP1 b) AND (b OP2 c)
. Comparison a
and c
not 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