Tips and tricks from my Telegram-channel @pythonetc, November 2019
Tips and tricks from my Telegram-channel @pythonetc, November 2019
It is a new selection of tips and tricks about Python and programming from my Telegram-channel @pythonetc.
← Previous publications.
PATH
is an environment variable that stores paths where executables are looked for. When you ask your shell to run ls
, the shell looks for the ls
executable file across all paths that are presented in PATH.
$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/v.pushtaev/.local/bin:/home/v.pushtaev/bin
$ which ls
/usr/bin/ls
In the example above paths are separated by :
in PATH. No escaping is possible: a path that contains :
cannot be used inside PATH
.
However, that is not true for all operating systems. In Python you can get the right separator for the local system with os.pathsep
:
Python 3.5.0 [...] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.pathsep
';'
os.pathsep
is not to be mixed up with os.path.sep
which is the separator for file paths:
>>> os.path.sep
'/'
To make regular expressions more readable you may use the re.VERBOSE
flag. It allows you to use extra spaces wherever you want as well as add comments with the #
symbol:
import re
URL_RE = re.compile(r'''
^
(https?)://
(www[.])?
(
(?: [^.]+[.] )+
( [^/]+ ) # TLD
)
(/.*)
$
''', re.VERBOSE)
m = URL_RE.match('https://www.pythonetc.com/about/')
schema, www, domain, tld, path = m.groups()
has_www: bool = bool(www)
print(f'schema={schema}, has_www={has_www}')
print(f'domain={domain}, tld={tld}')
print(f'path={path}')
re.X
is an alias for re.VERBOSE
.
complex
is the Python built-in type for complex numbers:
>>> complex(1, 2).real
1.0
>>> abs(complex(3, 4))
5.0
>>> complex(1, 2) == complex(1, -2).conjugate()
True
>>> str(complex(2, -3))
'(2-3j)'
There is not need to use it directly though since Python has literals for complex numbers:
>>> (3 + 4j).imag
4.0
>>> not (3 + 4j)
False
>>> (-3 - 4j) + (2 - 2j)
(-1-6j)
a : b : c
notation can be used to define slice(a, b, c)
only within brackets:
>>> [1, 2, 3, 4, 5][0:4:2]
[1, 3]
>>> [1, 2, 3, 4, 5][slice(0, 4, 2)]
[1, 3]
If you want to pass the slice object as an argument to a function, you have to define it explicitly:
def multislice(slc, *iterables):
return [i[slc] for i in iterables]
print(multislice(
slice(2, 6, 2),
[1, 2, 3, 4, 5, 6, 7],
[2, 4, 2, 4, 2, 4, 2],
))
Here is how you can convert such a function to an object that supports [a : b : c]
:
from functools import partial
class SliceArgDecorator:
def __init__(self, f):
self._f = f
def __getitem__(self, slc):
return partial(self._f, slc)
slice_arg = SliceArgDecorator
@slice_arg
def multislice(slc, *iterables):
return [i[slc] for i in iterables]
print(multislice[2:6:2](
[1, 2, 3, 4, 5, 6, 7],
[2, 4, 2, 4, 2, 4, 2],
))
__getattribute__
is a powerful tool that allows you to easily use delegation pattern when it«s appropriate. Here is how you add an ability to be comparable to a non-comparable object:
class CustomEq:
def __init__(self, orig, *, key):
self._orig = orig
self._key = key
def __lt__(self, other):
return self._key(self) < self._key(other)
def __getattribute__(self, name):
if name in {'_key', '_orig', '__lt__'}:
return super().__getattribute__(name)
return getattr(self._orig, name)
class User:
def __init__(self, user_id):
self._user_id = user_id
def get_user_id(self):
return self._user_id
def comparable(obj, *, key):
return CustomEq(obj, key=key)
user1 = comparable(User(1), key=lambda u: u.get_user_id())
user2 = comparable(User(2), key=lambda u: u.get_user_id())
print(user2 > user1) # True
print(user2 < user1) # False
print(user2.get_user_id()) # 2