Python tricks summary - a1k89/Blog GitHub Wiki
-
solid- Single responsibility, open/closed, Liskov substitution, Interface segregation, Dependency inversion (единственная работа для объекта. Открыт для расширения и закрыт для изменения. Подкласс должен соответствовать родительскому классу. Строить тонкие клиенты. Зависимость от абстракций, а не от реализаций) -
Patterns
- Creational: Fabric (abstract), singleton, builder
- Structural: adapter, decorator, facade
- Behavioral: iterator, mediator, template, strategy
- Use
assertonly for debug -
assertmay simple switch on/off
bad:
names = ['Alex', 'Bob', 'Anna']
good:
names = ['Alex',
'Bob',
'Anna',] # comma also importantUse with instead try...finally. With automatically close the file
with open('file.txt', 'w') as file:
file.write('hello')class FileManager:
def __init__(self, filename):
self.filename = filename
def __enter__(self)
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
And now may use as:
with FileManager("hello.txt") as file:
file.write("some text")Also, you may to use:
from contextlib import contextmanager
@contextmanager
def managed_file(name):
try:
f = open(name, 'w')
yield f
finally:
f.close()
with managed_file('hello.txt', 'w') as file:
file.write('some text')You may to use with for any class. Only use interface.
class Simple:
def __init__(self, name):
self.name = name
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass-
_some: only as hint (pep8) that variable internal use. Not use in python interpretator -
some_: to avoid naming conflict only (pep8( -
__some: to protect the variable.
class Some:
def __init__(self):
self.name = 'hello'
self._name = 'hello'
self__name = 'hello'
a = Some()
a.name # hello
a._name # hello
a.__name # error. Now we has: _Some.__name-
__some__: use for magic method. Please don't name your variable like this -
_: only convention. You may to use this for unpack:
car = ('red', 'auto', 12)
color, _, _ = car- 'Hello %s' % name - old style ('Hello %(name)s')
- (python3) 'Hello {}'.format(name)
- (python3.6) f'Hello {name}'
from string import Template
t = Template('Hey, $name!')
t.substitute(name='hello') # Hey, helloimport this
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!- Functions in python is a object also (like everything in python)
- Functions can be stored in Data Structures
- Functions can be passed as argument (decorators. Higher-order functions)
def bark(name):
return name.upper()
test_array = ['hello',
'world',]
some = list(map(bark, test_array)) # return ['HELLO', 'WORLD']- Nested functions
def some(name):
def inner(value):
return f'hello {value}'
return inner(name)
s = some('Andrei') # hello Andrei- Functions can capture local state
- Objects can behave like functions (callable)
class Sync:
def __init__(self, name):
self.name = name
def sync(self):
print('object {} was function'.format(self.name))
def __call__(self):
return self.sync()- s = lambda x, y: x + y
- Lambdas you can use:
tuples = [(1, 'c'), (2, 'b'), (3, 'a')]
sorted_tuples = sorted(tuples, key=lambda x: x[1])- Use lambda only with sorting
- Decorators can modify behavior
- In multiple decorators: bottom-to-top
def strong(func):
def wrp():
return "<strong>" + func() + "</strong>"
return wrp
def em(func):
def wrp():
return "<em>" + func() + "</em>"
return em
@strong
@em
def hello(name):
return 'Hello {}'.format(name)
s = hello('Andrei') # <strong><em>Hello Andrei</em></strong>- Decorators with arguments
def proxy(func):
def wrp(*args, **kwargs):
print('make something before')
return func(*args, **kwargs)
@proxy
def some(name, age):
return f'hello {name} with {age}'
test = some('Andrei', 32)
1. You pass function (only function)
2. You pass args/kwargs. Wrp got it and then run func with unwrap args/kwargs- Use
functools
def trace(func):
@functools.wraps(func) # this save all metadata for func!
def wrp(*args, **kwargs):
return func(*args, **kwargs)
return wrp- *args is a tuple
- **kwargs - is a dictionary
def some(required, *args, **kwargs):
my_args = args # collect all no positional params and create one tuple
my_kw = kwargs # collect all positional params and create dictionary
print(my_args)
print(my_kw)
some('hello', 1,2,3,4,5, ['hello'], key1='12', key2=14)
# [1,2,3,4,5,['hello']]
# {'key1': 12, 'key2': 14}Simple: when you see * and ** in params:
- You pass no/pos arguments
- Function get it and wrapped in tuple and dictionary
- use * to unpack tuple (list) and ** to unpack dictionary to params
def show_vector(x, y, z):
print(x,y,z)
point = (1,10,10)
show_vector(*point)- Any function return
Noneif not assign another value
a = [1,2,3]
b = a
a == b # True
a is b # True (points to one array)
c = list(a) # Another array
a == c # True
a is c # False (another array, another point)- Add to each class repr (for more information about class when printed to console)
- str simple calls repr
class NameToShortError(ValueError):
pass
...raise NameToShortError('hello')- Create copy for simple:
new_list = list(some_list)
new_dict = dict(some_dict)
new_set = set(some_set)- Making Shallow copies
>>> xs = [[1,2,3],[3,4,5]]
>>> ys = list(xs)
>>> xs
[[1, 2, 3], [3, 4, 5]]
>>> ys.append('hei!')
>>> ys
[[1, 2, 3], [3, 4, 5], 'hei!']
>>> xs
[[1, 2, 3], [3, 4, 5]]
>>> xs[0][1] = 'hei!hei!'
>>> xs
[[1, 'hei!hei!', 3], [3, 4, 5]]
>>> ys
[[1, 'hei!hei!', 3], [3, 4, 5], 'hui']
>>> - Making Deep copies
>>> import copy
>>> xs = [[1,2,3],[4,5]]
>>> ys = copy.deepcopy(xs)
>>> xs
[[1, 2, 3], [4, 5]]
>>> ys
[[1, 2, 3], [4, 5]]
>>> ys[0][1]='hello'
>>> ys
[[1, 'hello', 3], [4, 5]]
>>> xs
[[1, 2, 3], [4, 5]]- Copying arbitrary objects
Please use
copybuilt-in module to copy and deepcopy of object
import copy
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point {self.x!r}, {self.y!r}'
class Rect:
def __init__(self, topleft, bottomright):
self.topleft = topleft
self.bottomright = bottomright
def __repr__(self):
return f'React {self.topleft}, {self.bottomright}'
rect = Rect(Point(0,1), Point(4,4))
srect = copy.copy(rect)
rect.topleft.x = -1000000
drect = copy.deepcopy(srect)
drect.topleft.x = -999999
React Point -999999, 1, Point 4, 4
React Point -1000000, 1, Point 4, 4
React Point -1000000, 1, Point 4, 4- Use
ABCMeta and abstractmethod
from abc import ABCMeta, abstractmethod
class Base(metaclass=ABCMeta):
@abstractmethod
def foo(self):
passUse ABCMeta instead standard way raise NotImplementedError() is a best practice
- The some of
extensionof standard tuple - Principle: write once - read many
from collections import namedtuple
Car = namedtuple('Car', 'color engine style')
mazda = Car('Black', 'gazoline', 'sedan')
color, engine, type_of = mazda- You may extend namedtuple
>>> from collections import namedtuple
>>> Car = namedtuple('Car','color milleage')
>>> class MyCar(Car):
... def hex(self):
... if self.color == 'red':
... return 'hello'
... else:
... return 'hai'
...
>>> c = MyCar('red', 1234)
>>> c.hex()
'hello'- Also,
namedtuplehas properties:_fields,_as_dict(),_replace(),_make()*** so far so good
class Girl:
bufer_size = 10
def __init__(self, name):
self.name = name
def __repr__(self):
return f'Girl {self.name} {self.bufer_size} size'
anna = Girl('Anna')
olga = Girl('Olga')
roxana = Girl('Roxana')
print('Girl model', Girl.bufer_size)
print(anna)
anna.bufer_size = 6
print(anna)
print('Girl model', Girl.bufer_size)
Girl model 10
Girl Anna 10 size
Girl Anna 6 size
Girl model 10- When you assign variable inside class - this is a
class variableand available throughClass.variable. - But if you want to get this variable inside instance - you create this variable inside instance and it make a copy from class variable.
- Instance method: method for each instance (very simple)
- Class method: link to class, not any instance
- Static method: no any instance or class
- If you need unique method - use
instance method. If you want to create specific fabric - useclass method. Finally, for non-class and non-instance methods - usestatic method
-
dict. The most usefull data structure -
collections:
from collections import OrderedDict
od = OrderedDict()
od['hello'] = 1
od['welcome'] = 2- collections.defaultdict - create default key
- collections.chainMap
- `list' - can add/remove
-
tuple- only create and read -
array.array:
ar = array('f)
ar.append(23.0)
ar.append(2.0)
ar.append('hello') # error!-
str- immutable strings (can't insert)
-
dict- simple data objects -
tuple- immutable groups of objects collections.namedtuplestruct.Struct
- Unordered collection of elements (Mutable)
- Be careful: when create set:
new_set = {1,2,3,4,'10','20','a',12}
type(new_set) # return set
s = {} # wrong! dict return
s1 = set() # good! now is set-
frozenset- immutable set -
collections.Counter- multiset
“>>> from collections import Counter
>>> inventory = Counter()
>>> loot = {'sword': 1, 'bread': 3}
>>> inventory.update(loot)
>>> inventory
Counter({'bread': 3, 'sword': 1})
>>> more_loot = {'sword': 1, 'apple': 1}
>>> inventory.update(more_loot)
>>> inventory
Counter({'bread': 3, 'sword': 2, 'apple': 1})”-
Lifo - last in first out
-
FIFO - first in first out
-
lists- simple, built-in stacks -
collections.deque- fast stack -
queue.LifoQueue
- for i in some_arr...
- (to get index):
arr = [1,2,3,4]
for index, value in enumerate(arr):
...range(start_value, to_end_value, step(optional))
some_list = [x * x for x in range (1,5) # [1,4,9,16]
some_dict = {x: x * x for x in range (1,5) if x != 3} # {1: 1, 2: 4, 4: 16 }
some_set = {x * x for x in range(1,5} # {1,4,9,16} # unordered set!!!lst = [1,2,3,4,5,6]
ss = lst[1:4] # [2,3,4,] not included last
ss = lst[1:4:2] # [2,4]
ss = ls[::-1] # [6,5,4,3,2,1] reverse!!!
del lst[::] # [] this delete all items but save cursor to lst- Python
iteratorprotocol -
Iterator: structure who consist of:__iter__and__next__
class SomeRepeater:
def __init__(self, value, limit):
self.value = value
self.count = 0
self.limit = limit
def __iter__(self)
return self
def __next__(self):
if self.count >= limit:
raise StopIteration()
sef.count += 1
return self.value- Magic
yieldkeyword
def generator(value):
while True:
yield value- Some syntax sugar for iterators
- Generator Expressions vs List Comprehensions:
lst = [x for x in range(100)] # return list. And list all in memory
gen = (x for x in range(100)) # return generator objects. And values not located in a memory!- You may filtering values in generator:
gen = (x for x in range(100) if x % 2 == 0)