17 December 2019

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

Mail.ru Group corporate blogPythonProgramming

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
Tags:pythonpythonetc eng
Hubs: Mail.ru Group corporate blog Python Programming
+29
1.8k 2
Comments 1
Top of the last 24 hours