@Pythonetc compilation, october 2019







A new selection of Python tips and programming from my @pythonetc feed.



← Previous collections









If you want to iterate several iterable objects at once, you can use the zip



function (it has nothing to do with the ZIP file format):



 from datetime import timedelta names = [ 'Eleven. Return and Revert', 'Wilderness', 'The Menagerie Inside', 'Evaporate', ] years = [ 2010, 2013, 2015, 2018, ] durations = [ timedelta(minutes=57, seconds=38), timedelta(minutes=48, seconds=5), timedelta(minutes=46, seconds=34), timedelta(minutes=43, seconds=25), ] print('Midas Fall LPs:') for name, year, duration in zip( names, years, durations ): print(f' * {name} ({year}) β€” {duration}')
      
      





Result:



 Midas Fall LPs: * Eleven. Return and Revert (2010) β€” 0:57:38 * Wilderness (2013) β€” 0:48:05 * The Menagerie Inside (2015) β€” 0:46:34 * Evaporate (2018) β€” 0:43:25
      
      











You can stop the generator by explicitly calling g.close()



, but more often than not, the garbage collector does this for you. After calling close



, at the point where the generating function was paused, GeneratorExit



initiated:



 def gen(): try: yield 1 yield 2 finally: print('END') g = gen() print(next(g)) # prints '1' g.close() # prints 'END'
      
      





Do not forget about three aspects. First, you cannot continue to generate values ​​while processing GeneratorExit



:



 def gen(): try: yield 1 finally: yield 3 g = gen() next(g) g.close() # RuntimeError
      
      





Secondly, if the generator is not already running, then the exception will not be thrown, but the generator will still go into the β€œstopped” state:



 def gen(): try: yield 1 finally: print('END') g = gen() g.close() # nothing print(list(g)) # prints '[]'
      
      





Thirdly, close



does nothing if the generator has already finished working:



 def gen(): try: yield 1 yield 2 finally: print('END') g = gen() print(list(g)) print('Closing now') g.close() # END # [1, 2] # Closing now
      
      











f-lines allow you to specify the width of the displayed value, as well as other formatting specifiers:



 >>> x = 42 >>> f'{x:5}+{x:15f}' ' 42+ 42.000000'
      
      





They can also contain calculated expressions, which is useful when the width is not known in advance:



 def print_table(matrix): cols_width = [ max(len(str(row[col])) for row in matrix) for col in range(len(matrix[0])) ] for row in matrix: for i, cell in enumerate(row): print( f'{cell:{cols_width[i]}} ', end='' ) print() albums = [ ['Eleven. Return and Revert', 2010], ['Wilderness', 2013], ['The Menagerie Inside', 2015], ['Evaporate', 2018], ] print_table(albums)
      
      





Result:



 Eleven. Return and Revert 2010 Wilderness 2013 The Menagerie Inside 2015 Evaporate 2018
      
      











If your class is derived from another, then the metaclass of your class must also be derived from the metaclass of that class:



 from collections import UserDict from abc import ABCMeta # ABCMeta is a metaclass of UserDict class MyDictMeta(ABCMeta): def __new__(cls, name, bases, dct): return super().__new__(cls, name, bases, dct) class MyDict(UserDict, metaclass=MyDictMeta): pass
      
      





It may be advisable to automatically get the metaclass of this other class:



 def create_my_dict_class(parents): class MyDictMeta(*[type(c) for c in parents]): def __new__(cls, name, bases, dct): return super().__new__(cls, name, bases, dct) class MyDict(*parents, metaclass=MyDictMeta): pass MyDict = create_my_dict_class((UserDict,))
      
      











__init__



allows you to modify an object immediately after its creation. If you want to control what you __new__



created, use __new__



:



 from typing import Tuple, Dict from cached_property import cached_property class Numbers: _LOADED: Dict[Tuple[int, ...], 'Numbers'] = {} def __new__(cls, ints: Tuple[int, ...]): if ints not in cls._LOADED: obj = super().__new__(cls) cls._LOADED[ints] = obj return cls._LOADED[ints] def __init__(self, ints: Tuple[int, ...]): self._ints = ints @cached_property def biggest(self): print('calculating...') return max(self._ints) print(Numbers((4, 3, 5)).biggest) print(Numbers((4, 3, 5)).biggest) print(Numbers((4, 3, 6)).biggest)
      
      






All Articles