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