Python etc
6.12K subscribers
18 photos
194 links
Regular tips about Python and programming in general

Owner — @pushtaev

© CC BY-SA 4.0 — mention if repost
Download Telegram
Python has NaN float value and it's a rule-breaking thing:

import math

sorted([5.0, math.nan, 10.0, 0.0])
# [5.0, nan, 0.0, 10.0]

3 < math.nan
# False
3 > math.nan
# False

min(3, math.nan)
# 3
min(math.nan, 3)
# nan


Be careful. Use math.isnan to check if a value is NaN.
Some functional languages, like Elixir, have in the standard library a huge collection of functions to work with lazy enumerables (in Python, we name them iterators). In Python, this role is on itertools. However, itertools is a relatively small collection of such functions, it contains only most important and basic ones (maybe, a bit of junk, but so). The documentation has Itertools Recipes with some useful functions. A few examples:

def take(n, iterable):
"Return first n items of the iterable as a list"
return list(islice(iterable, n))

def prepend(value, iterator):
"Prepend a single value in front of an iterator"
# prepend(1, [2, 3, 4]) -> 1 2 3 4
return chain([value], iterator)

def tail(n, iterable):
"Return an iterator over the last n items"
# tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n))

def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(islice(iterable, n, None), default)


All these examples and much more can be imported from more-itertools third-party library.
Today we have 2 polls at once! Both with multiple answers.
What are static type checkers you ever tried?
Anonymous Poll
49%
mypy
4%
pyre
5%
pytype
4%
pyright
1%
pyanalyze
47%
None of the above
What are static type checkers you actively use?
Anonymous Poll
39%
mypy
1%
pyre
3%
pytype
2%
pyright
1%
pyanalyze
57%
None of the above
There is a built-in function format that basically just calls __format__ method of the passed argument type with passed spec. It is used in str.format as well.

class A:
def __format__(self, spec):
return spec

format(A(), 'oh hi mark')
# 'oh hi mark'

'{:oh hi mark}'.format(A())
# 'oh hi mark'
Every class is an instance of its metaclass. The default metaclass is type. You can use this knowledge to check if something is a class or is an instance:

class A: pass
isinstance(A, type) # True
isinstance(A(), type) # False


However, class and instance are both an instance of object!

isinstance(A(), object) # True
isinstance(A, object) # True


This is because type an instance of object and subclass of object at the same time, and object is an instance of type and has no parent classes.

isinstance(type, object) # True
issubclass(type, object) # True
type(type) # type
type(object) # type
type.__mro__ # (type, object)
object.__mro__ # (object,)
Everything is an object, including functions, lambdas, and generators:

g = (i for i in [])
def f(): pass

type(g).__mro__ # (generator, object)
type(f).__mro__ # (function, object)
type(lambda:0).__mro__ # (function, object)


Generators have no __dict__ but functions do!

def count(f):
def w():
w.calls += 1
return f()
# let's store an attribute in the function!
w.calls = 0
return w

@count
def f():
return 'hello'

f()
f()
f.calls
# 2
Python caches every imported module is sys.modules:

import sys
import typing

sys.modules['typing']
# <module 'typing' from '/usr/local/lib/python3.7/typing.py'>

len(sys.modules)
# 637


You can reload any module with importlib.reload to force it to be executed again. Be careful, though, since every object from the module will be recreated, you can break all isinstance checks and have hard times with debugging it.

old_list = typing.List
old_list is typing.List
# True

importlib.reload(typing)
old_list is typing.List
# False
Module numbers was introduced by PEP-3141 in Python 2.6. It implements the numbers hierarchy, inspired by Scheme:

Number :> Complex :> Real :> Rational :> Integral


They are ABC classes, so they can be used in isinstance checks:

import numbers
isinstance(1, numbers.Integral)
# True

isinstance(1, numbers.Real)
# True

isinstance(1.1, numbers.Integral)
# False


+ int is Integral.
+ fractions.Fraction is Rational.
+ float is Real.
+ complex is Complex (wow!)
+ decimal.Decimal is Number.

In theory, Decimal should be Real but it's not because Decimal doesn't interoperate with float:

Decimal(1) + 1.1
# TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'


The most fun thing about numbers is that it's not supported by mypy.
types.SimpleNamespace is a way to make a dict with access by attributes:

from types import SimpleNamespace
sn = SimpleNamespace(a=1, b=2)
sn.a
# 1

sn.c
# AttributeError: ...


However, values from SimpleNamespace can't be accessed by getitem anymore because "There should be only one obvious way to do it":

sn['a']
# TypeError: 'types.SimpleNamespace' object is not subscriptable
How many ways you know how to convert int to str? Let's try! Note that complications of the same method don't count.

n = 13

n.__str__() # 1
str(n) # 2
'{}'.format(n) # 3
'%i' % n # 4
f'{n}' # 5
format(n, 'd') # 6

from string import Template
Template('$n').substitute(n=n) # 7

# similar to other methods:
n.__repr__()
repr(n)
str.format('{}', n)
n.__format__('d')


Can you beat it?
The point of the post above was that for some simple tasks there are many ways to do it (and some ways are good only in some cases). Also, the number of possible solutions grows as the language evolves. Another good example is concatenation. You can join 2 strings with f-strings, str.format, str.join, +, and so on. Thinking about these ways, even not suitable for daily usage, helps better language understanding.

Our amazing subscribers decided to take the challenge. Below are some not suitable for work but fan solutions how to convert int to str.

@dedefer:

import io
with io.StringIO() as f:
print(n, end='', file=f)
n_str = f.getvalue()


Evgeny:

import ctypes
ctypes.cdll.LoadLibrary('libc.so.6').printf(b'%d', n)


If you're going to use solutions below on the production, keep in mind that it doesn't work with negative numbers.

@oayunin:

from math import log10
''.join(['0123456789'[(n // 10 ** i) % 10] for i in range(int(log10(n)), -1, -1)])


A similar solution from @apatrushev:

''.join(chr(t + 48) for t in (n // 10**x % 10 for x in reversed(range(int(math.log(n,10)) + 1))) if t)


A similar solution with lambdas from @antonboom:

(lambda n:
''.join(
chr(ord('0') + (n // (10**i)) % 10)
for i in range(math.ceil(math.log(n, 10)) - 1, -1, -1)
)
)(n)


One more from @oayunin:

from subprocess import check_output
with open('/tmp/tmp.txt', 'w') as f:
for x in range(n):
f.write(' ')
check_output(['wc', '-c', '/tmp/tmp.txt']).decode().split()[0]


@orsinium:

import sys
import subprocess
cmd = [sys.executable, '-c', 'print(len("' + '*' * n + '"))']
subprocess.run(cmd, capture_output=True).stdout.strip().decode()


@maxvyaznikov:

chars = []
while n > 0:
digit = n % 10
n = int(n / 10)
chars.append(chr(ord('0') + digit))
print(''.join(chars[::-1]))
Context manager contextlib.nullcontext is helpful when a block of code not always should be executed in a context.

A good example is a function that works with a database. If a session is passed, the function will use it. Otherwise, it creates a new session, and does it in a context to guarantee fallback logic to be executed:

from contextlib import nullcontext

def get_user(id, session=None):
if session:
context = nullcontext(session)
else:
context = create_session()
with context as session:
...


Another example is optional suppressing errors:

from contextlib import suppress

def do_something(silent=False):
if silent:
context = suppress(FileNotFoundError)
else:
context = nullcontext()
with context:
...


It was added in Python 3.7. For earlier Python versions DIY:

from contextlib import contextmanager

@contextmanager
def nullcontext(value=None):
yield value


Another option is to use ExitStack.
The module enum provides a way to build an enumerable class. It is a class with a predefined list of instances, and every instance is bound to a unique constant value.

from colorsys import rgb_to_hls
from enum import Enum

class Color(Enum):
RED = (1, 0, 0)
GREEN = (0, 1, 0)
BLUE = (0, 0, 1)

@property
def hls(self):
return rgb_to_hls(*self.value)

Color
# <enum 'Color'>

Color.RED
# <Color.RED: (1, 0, 0)>

Color.RED.name
# 'RED'

Color.RED.value
# (1, 0, 0)

Color.RED.hls
# (0.0, 0.5, 1.0)

type(Color.RED) is Color
# True
types.DynamicClassAttribute is a decorator that allows having a @property that behaves differently when it's called from the class and when from the instance.

from types import DynamicClassAttribute

class Meta(type):
@property
def hello(cls):
return f'hello from Meta ({cls})'

class C(metaclass=Meta):
@DynamicClassAttribute
def hello(self):
return f'hello from C ({self})'

C.hello
# "hello from Meta (<class '__main__.C'>)"

C().hello
# 'hello from C (<__main__.C object ...)'


Practically, it is used only in enum to provide name and value properties for instances while still allowing to have name and value class members:

import enum

class E(enum.Enum):
value = 1

E.value
# <E.value: 1>

E.value.value
# 1
The module zipapp can pack a python module into a zip archive that can be executed directly by a Python interpreter. It is a good way to ship CLI tools:

$ mkdir example
$ echo 'print("hello, @pythonetc!")' > example/__main__.py
$ python3 -m zipapp example
$ python3 example.pyz
hello, @pythonetc!