-
Notifications
You must be signed in to change notification settings - Fork 0
Python
python2 -m pip install virtualenv
virtualenv --python=/usr/bin/python2.6 venv
source venv/bin/activate
pip install -r requirements.txt
pip freeze
Установка Устанавливаем последнюю версию pip в систему, можно делать разными способами, рекомендую также использовать virtualenv:
apt-get install python-pip
pip install -U pip
Для последних версий MySQL-python требуется версия distribute >= 0.6.28, поэтому обновляем его тоже:
pip install -U distribute>=0.6.28
Затем, устанавливаем требуемые dev-пакеты:
apt-get install python-dev libmysqlclient-dev
Устанавливаем MySQL-python:
pip install MySQL-python
Название web-сервера можно узнать из заголовка Server HTTP ответа.
>>> from requests import get
>>> get('https://stepic.org/favicon.ico').headers['Server']
'nginx/1.10.3'
wget -r -k -l 7 -p -E -nc https://stepik.org/explore
python -m pytest -v tests/test_generator.py
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
''.join('{}{}'.format(key, val) for key, val in adict.items())
string = ast.literal_eval(string)
assert type(new_dict) is dict
def main():
a, b = map(int, input().split())
res = a + b
print(res)
if __name__ == "__main__":
main()
import numpy as np
np.show_config()
class Song:
def __init__(self, artist, song):
self.artist = artist
self.song = song
self.tags = []
def add_tags(self, *args):
self.tags.extend(args)
song1 = Song("artist_name1", "song_name2")
song1.add_tags("tag1", "tag2")
song2 = Song("artist_name3", "song_name4")
song2.add_tags("tag3", "tag4")
song2.tags
class MoneyBox:
def __init__(self, capacity):
self.capacity = capacity
self.value = 0
def can_add(self, v):
if (v <= (self.capacity - self.value)):
return True
else:
return False
def add(self, v):
self.value = self.value + v
isinstance(x,y)
type()
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# Проверим, что s.split не работает, если разделитель - не строка
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
# return использовать не обязательно, функция вернёт None
# Для документации функции используются строковые литералы
>>> def foo():
... """I return 42."""
... return 42
...
# После объявлении функции документация доступна через определённый атрибут
>>> foo.__doc__
'I return 42.'
# В интерпретаторе удобнее пользоваться встроенной функцией
>>> help(foo) # или foo? в IPython.
>>> def min(x, y): # __o
... return x if x < y else y # _`\<,_
... # (_)/ (_)
# 1. Находить минимум произвольного количества аргументов
>>> min(-5, 12, 13)
-5
# 2. Использовать функцию min для кортежей, списков, множеств и других последовательностей
>>> xs = {-5, 12, 13}
>>> min(???)
-5
# 3. Ограничить минимум произвольным отрезком [lo, hi]
>>> bounded_min(-5, 12, 13, lo=0, hi=255)
12
# 4. По заданным lo и hi строить функцию bounded_min (фабрика функций)
>>> bounded_min = make_min(lo=0, hi=255)
>>> bounded_min(-5, 12, 13)
12
# 1.a
def min(*args): # type(args) == tuple
res = float("inf") # инициализируем бесконечностью
for arg in args:
if arg < res:
res = arg
return res
# >>> min(-5, 12, 13)
# -5
# >>> min()
# inf
1.b
def min(first, *args):
res = first
# ...
# min()
# TypeError: min() missing 1 required [...] argument: 'first'
# 2.a
# Синтаксис будет работать с любым объектом, поддерживающим протокол итератора
# Итератор - что-то, что можно обойти
>> xs = {-5, 12, 13} # но вообще set лучше не юзать
>>> min(*xs) # здесь он не критичен, но вообще, распаковывать set - странная идея
-5
>>> min(*[-5, 12, 13])
-5
>>> min(*(-5, 12, 13))
-5
...
# 3.a
def bounded_min(first, *args, lo=float("-inf"),
hi=float("inf")):
res = hi
# итерируемся по первому элементу + всё остальное
for arg in (first, ) + args: # args - это кортеж,
# соответственно fisrt тоже надо превратить к кортеж :)
if arg < res and lo < arg < hi:
res = arg
return max(res, lo)
# >>> bounded_min(-5, 12, 13, lo=0, hi=255)
# 12
# Нужно было написать функцию, которая принимает что-то,
# по чему можно итерироваться и возвращает список уникальных элементов
def unique(iterable, seen=set()):
acc = []
for item in iterable:
if item not in seen:
seen.add(item)
acc.append(item)
return acc
# >>> xs = [1, 1, 2, 3]
# >>> unique(xs)
# [1, 2, 3]
# >>> unique(xs)
# []
# Так происходит, потому что значение по умолчанию инициализируется ровно один раз -
# - в момент компиляции в байт-код
# Т.е. все вызовы unique() будут различать значения атрибута seen
# Вывод: никогда не использовать изменяемые значения в качестве значений по умолчанию
# (никакие списки, никакие словари)
# Атрибут __defaults__ - кортеж с инициализированными значениями элементов по умолчанию
# >>> unique.__defaults__
# ({1, 2, 3},)
def unique(iterable, seen=None): # None не изменяемый, потому что он Singleton
seen = set(seen or []) # None --- falsy.
# Поэтому если аргумент=None, то он станет пустым списком
# OR подразумевает операции над булевыми переменными,
# поэтому, если они не булевы,
# то он попытается их привести к таковым
# А если первая штука truesy, то мы берём её значение
acc = []
for item in iterable:
if item not in seen:
seen.add(item)
acc.append(item)
return acc
# >>> xs = [1, 1, 2, 3]
# >>> unique(xs)
# [1, 2, 3]
# >>> unique(xs)
# [1, 2, 3]
None
0
0,00
""
[] () {}
set()
# Если функция имеет фиксированную арность,
# то ключевые агрументы можно передавать без явного указания имени:
>>> def flatten(xs, depth=None):
... pass
...
>>> flatten([1, [2], 3], depth=1)
>>> flatten([1, [2], 3], 1)
# Можно явно потребовать, чтобы часть аргументов всегда передавалась как ключевые:
>>> def flatten(xs, *, depth=None): # звёздочка всё кушает и не связывает это с именем
# Всё, что идёт после звёздочки - ключевые аргументы
... pass
...
>>> flatten([1, [2], 3], 2)
TypeError: flatten() takes 1 positional argument [...]
def runner(cmd, **kwargs):
if kwargs.get("verbose", True):
print("Logging enabled")
>>> runner("mysqld", limit=42)
Logging enabled
>>> runner("mysqld", **{"verbose": False})
>>> options = {"verbose": False}
>>> runner("mysqld", **options)
# Присваивание
acc = []
seen = set()
(acc, seen) = ([], set())
# В качестве правого объекта можно использовать любой объект,
# поддерживающий протокол итератора
x, y, z = [1, 2, 3]
x, y, z = {1, 2, 3} # unordered
x, y, z = "xyz"
x, y = y, x # по сути справа - tuple (y, x)
# Скобки обычно опускают, но иногда они бывают полезны
rectangle = (0, 0), (4, 4)
(x1, y1), (x2, y2) = rectangle
# В Python 3.0 можно юзать * (что-то мы распакуем, а что-то обратно запакуем)
# rest всегда приводится к списку и копирует то, что справа
>>> first, *rest = range(1, 5)
>>> first, rest
(1, [2, 3, 4])
# * можно использовать в любом месте приложения
>>> first, *rest, last = range(1, 5)
>>> last
4
>>> first, *rest, last = [42]
ValueError: need more than 1 values to unpack
# Работает рекурсивно тоже
>>> *_, (first, *rest) = [range(1, 5)] * 5
>>> fisrt
1
https://www.python.org/dev/peps/pep-3132/
# Синтаксис распаковки работает и в цикле for, например:
for a, *b in [range(4), range(2)]:
print(b)
# [1, 2, 3]
# [1]
>>> import dis
>>> dis.dis("first, *rest, last = ('a', 'b', 'c')")
0 LOAD_CONST 4 (('a', 'b', 'c'))
3 UNPACK_EX 257
6 STORE_NAME 0 (first)
9 STORE_NAME 1 (rest)
12 STORE_NAME 2 (last)
15 LOAD_CONST 3 (None)
18 RETURN_VALUE
# Мораль: присваивание в Python работает слева-направо
>>> x, (x, y) = 1, (2, 3) # не надо так писать :)
>>> x
2
>>> dis.dis("first, *rest, last = ['a', 'b', 'c']")
0 LOAD_CONST 0 (1)
3 LOAD_CONST 1 (2)
6 LOAD_CONST 2 (3)
9 BUILD_LIST 3
12 UNPACK_EX 257
15 STORE_NAME 0 (first)
18 STORE_NAME 1 (rest)
21 STORE_NAME 2 (last)
24 LOAD_CONST 3 (None)
27 RETURN_VALUE
# Мораль: Синтаксически схожие конструкции могут
# иметь различную семантику времени исполнения
# Функции в Python могут принимать произвольное количество
# позиционных и ключевых аргументов
# Для объявления таких функций используется синтаксис упаковки,
# а для вызова синтаксис распаковки
>>> def f(*args, **kwargs):
... pass
...
>>> f(1, 2, 3, **{"foo":42})
# Синтаксис распаковки также можно использовать при присваивании
# нескольких аргументов в цикле for:
>>> first, *rest = range(4)
>>> for first, *rest in [range(4), range(2)]:
... pass
...
# В Python 3.5 возможности распаковки были в очередной раз расширены
# Теперь можем распаковать произвольное количество объектов
# https://www.python.org/dev/peps/pep-0448/
# Изменения затронули распаковку при вызове функции:
>>> def f(*args, **kwargs):
... print(args, kwargs)
...
>>> f(1, 2, *[3, 4], *[5],
... foo="bar", **{"baz": 42}, boo=24)
# (1, 2, 3, 4, 5) {'baz': 42, 'boo': 24, 'foo': 'bar'}
# и при инициализации контейнеров
>>> defaults = {"host": "0.0.0.0", "port": 8080}
>>> {**defaults, "port": 80} # замещает значения по ключу
{'host': '0.0.0.0', 'port': 80}
>>> [*range(5), 6] # аналогично для множества и кортежа
[0, 1, 2, 3, 4, 5, 6]
- В отличие от Java (< 8), C/C++ (< 11) в Python функции - объекты первого класса. То есть с ними можно делать то же самое, что и с другими значениями. Можно функцию передать в другую функцию, можно функцию вернуть из функции.
- Например, можно объявлять функции внутри других функций
>>> def wrapper():
... def identity(x):
... return x
... return identity
...
>>> f = wrapper()
>>> f(42)
42
def make_min(*, lo, hi):
def inner(first, *args):
res = hi
for arg in (first,) + args:
if arg < res and lo < arg < hi:
res = arg
return max(res, lo)
return inner
# >>> bounded_min = make_min(lo=0, hi=255)
# >>> bounded_min(-5, 12, 13)
# 0
>>> min # builtin
<built-in function min>
>>> min = 42 # global
>>> def f(*args):
... min = 2
... def g(): # enclosing
... min = 4 # local
... print(min)
...
# Правило LEGB
# Поиск имени ведётся не более чем в четырёх областях видимости:
# локальной, затем в объемлющей функции (если такая имеется), затем
# в глобальной и, наконец, во встроенной.
>>> min = 42 # = globals()['min'] = 42
>>> globals() # изменяемый словарь глобальных переменных
{..., 'min': 42}
>>> def f():
... min = 2 # = locals()['min'] = 2
... print(locals())
...
>>> f()
{'min': 2}
- Функции в Python могут использовать переменные, определённые во внешних областях видимости
- Важно помнить, что поиск переменных осуществляется во время исполнения функции, а не во время объявления
>>> def f():
... print(i)
...
>>> for i in range(4):
... f()
...
0
1
2
3
- Для присваивания правило LEGB не работает. Присваивание абсолютно всегда создаёт имя в локальной области видимости
>>> min = 42
>>> def f():
... min += 1
... return min
...
>>> f()
UnboundLocalError: local variable 'min' referenced [...]
- По умолчанию операция присваивания создаёт локальную переменную
- Изменить это поведение можно с помощью операторов local и nonlocal
- Позволяет модифицировать значение переменной из глобальной области видимости
>>> min = 42
>>> def f():
... global min
... min += 1
... return min
...
>>> f()
43
>>> f()
44
- Использование global порочно, почти всегда лучше заменить global на thread-local объект
- Позволяет модифицировать значение переменной из объемлющей области видимости
# Мы хотим сделать "изменяемую ячейку памяти" value,
# при этом мы хотим инкапсулировать её значение (никаким
# другим способом пользователь не сможет получить её значение, кроме вызвав get(),
# потому что переменная value локальная
# Переменная get просто использует value (мы пойдём наверх и
# найдём в объемлющей области видимости value)
# С set другая история, там нужен nonlocal чтобы мы не создали локальную переменную,
# а изменили внешнюю
def cell(value=None):
def get():
return value
def set(update):
nonlocal value
value = update
return get, set
# >>> get, set = cell()
# >>> set(42)
# >>> get()
# 42
Почитать мысли разработчиков на эту тему (PEP-3104)
- В Python четыре области видимости: встроенная, глобальная, объемлющая и локальная
- Правило LEGB: поиск имени осуществляется от локальной к встроенной
- При использовании операции присваивания имя считается локальным. Это поведение можно изменить с помощью операторов global и nonlocal.
- Python не функциональный язык, но в нём есть элементу функционального программирования
- Анонимные функции имеют вид
>>> lambda arguments: expression
и эквивалентны по поведению
>>> def <lambda>(arguments):
... return expression
- Всё, сказанное про аргументы именованных функций, справедливо и для анонимных
>>> lambda foo, *args, bar=None, **kwargs: 42
// Функции высшего порядка - функции, которые принимают другие функции
- Применяет функцию к каждому элементу последовательности (iterable, т.е. объекту, поддерживающему протокол итератора)
# Первый аргумент - это функция, которую мы применим к последовательности из итераторов
# Map ленивый, ничего не вернёт и считать не будет, если не указать
>>> map(indentity, range(4))
<map object>
>>> list(map(identity, range(4)))
[0, 1, 2, 3]
>>> set(map(lambda x: x % 7, [1, 9, 16, -1, 2, 5]))
{1, 2, 5, 6}
>>> map(lambda s: s.strip(), open("./HBA1.txt"))
<map object>
- или последовательностей, количество элементов в результате определяется длиной наименьшей из последовательностей
>>> list(map(lambda x, n: x ** n,
... [2, 3], range(1, 8)))
[2, 9]
Принимает предикат (т.е. какую-то функцию, возвращающую truthy value) и принимает коллекцию
- Убирает из последовательности элементы, не удовлетворяющие предикату
>>> filter(lambda x: x % 2 != 0, range(10))
<filter object>
>>> list(filter(lambda x: x % 2 != 0, range(10)))
[1, 3, 5, 7, 9]
- Вместо предиката можно передать None, в этом случае в последовательности останутся только truthy значения
>>> xs = [0, None, [], {}, set(), "", 42]
>>> list(filter(None, xs))
[42]
Сшивает несколько последовательностей
- Строит последовательность кортежей из элементов нескольких последовательностей
>>> list(zip("abc", range(3), [42j, 42j, 42j]))
[('a', 0, 42j), ('b', 1, 42j), ('c', 2, 42j)]
- Поведение в случае последовательностей различной длины аналогично map
>>> list(zip("abc", range(10)))
[('a', 0), ('b', 1), ('c', 2)]
- Выражение zip через map
map(lambda *args, argf)
Выглядят как плоский цикл for
- Пришли в Python из языка ABC, который позаимствовал их из языка SETL
>>> [x ** 2 for x in range(10) if x % 2 == 1]
[1, 9, 25, 49, 81]
- Компактная альтернатива комбинациям map и filter
>>> list(map(lambda x: x ** 2,
... filter(lambda x: x % 2 ==1,
... range(10))))
[1, 9, 25, 49, 81]
- Могут быть вложенными
# Расплющит вложенные списки. Здесь вложенность 2, поэтому 2 цикла for
# Если больше двух, то лучше явно написать цикл for и не заставлять людей страдать
>>> nested = [range(5), range(8, 10)]
>>> [x for xs in nested for x in xs] # flatten
[0, 1, 2, 3, 4, 8, 9]
>>> {x % 7 for x in [1, 9, 16, -1, 2, 5]}
{1, 2, 5, 6}
>>> date = {"year": 2015, "month": "September", "day": ""}
>>> {k: v for k, v in date.items() if v}
{'month': 'September', 'year': 2015}
>>> {x: x ** 2 for x in range(4)}
{0: 0, 1: 1, 2: 4, 3: 9}
- Наличие элементов функционального программирования позволяет компактно выражать вычисления
- В Python есть типичные для функциональных языков:
-
- анонимные функции lambda,
-
- функции map, filter и zip,
-
- генераторы списков.
- Синтаксис Python также поддерживает генерацию других типов коллекций: множеств и словарей
https://www.python.org/dev/peps/pep-0008/ http://www.pocoo.org/internal/styleguide/
Для сравнения на равенство
- объектов используйте операторы == и !=,
- синглтонов используйте is и is not,
- булевых значений используйте сам объект или оператор not, например
if foo:
# ...
while not bar:
# ...
- проверяйте отсутствие элемента в словаре с помощью оператора not in
if not key in d: # Плохо
if key not in d: # Лучше
- Документируйте функции следующим образом:
def something_useful(arg, **options):
"""One-line summary.
Optional longer description of the function behaviour.
"""
pip install pep8
pep8 ./file.py
pip install autopep8
autopep8 -v ./file.py
- Декоратор - функция, которая принимает другую функцию и что-то возвращает
>>> @trace
... def foo(x):
... return 42
...
- Аналогичная по смыслу версия без синтаксического сахара
>>> def foo(x):
... return 42
...
>>> foo = trace(foo)
- По имени foo будет доступно то, что вернула функция trace. Это и есть результат применения декоратора
- Возвращаемый объект может быть любого типа
- Декоратор trace выводит на экран сообщение с информацией о вызове декорируемой функции
# Возвращает имя функции и аргументы, с которыми она была вызвана
>>> def trace(func):
... def inner(*args, **kwargs):
... print(func.__name__, args, kwargs)
... return func(*args, **kwargs)
... return inner
- Применим его к тождетсвенной функции
>>> @trace
... def identity(x):
... "I do nothing useful."
... return x
...
>>> identity(42)
identity(42, ) {}
42
- Проблема c help и атрибутами декорируемой функции
>>> help(identity)
Help on function inner in module __main__:
inner(*args, **kwargs)
- Возможность глобально отключать trace без лишних телодвижений
- Явное указание файла при использовании trace
>>> @trace
... def identity(x):
... return x
- Использование sys.stdout для вывода по умолчанию
>>> def identity(x):
... "I do nothing useful"
... return x
...
>>> identity.__name__, identity.__doc__
('identity', 'I do nothing useful.')
>>> identity = trace(identity)
>>> identity.__name__, identity.__doc__
('inner', None)
module У любой функции в Python есть атрибут module, содержащий имя модуля, в котором функция была определена. Для функций, определённых в интерпретаторе, например:
>>> identity.__module__
'__main__'
- В модуле functools из стандартной библиотеки Python есть функция, реализующая логику копирования внутренних атрибутов
import functools
def trace(func):
def inner(*args, **kwargs):
print(func.__name__, args, kwargs)
return func(args, kwargs)
functools.update_wrapper(inner, func)
return inner
- То же самое можно сделать с помощью декоратора wraps
def trace(func):
@functools.wraps(func)
def inner(*args, **kwargs):
print(func.__name__, args, kwargs)
return func(args, kwargs)
return inner
- Заведём глобальную переменную trace_enabled и с её помощью будем отключать и включать trace
trace_enabled = False
def trace(func):
@functools.wraps(func)
def inner(*args, **kwargs):
print(func.__name__, args, kwargs)
return func(*args, **kwargs)
return inner if trace_enabled else func
- Если trace выключен, то результатом применения декоратора является сама функция func - никаких дополнительных действий в момент её исполнения производиться не будет
@trace
def identity():
return x
# ==
def identity(x):
return x
identity = trace(identity)
- Для декораторов с аргументами эквивалентность сохраняется
@trace(sys.stderr)
def identity(x):
return x
# ==
def identity(x):
return x
deco = trace(sys.stderr)
identity = deco(identity)
def trace(handle):
def decorator(func):
@functools.wraps(func)
def inner(*args, **kwargs):
print(func.__name__, args, kwargs, file=handle)
return func(*args, **kwargs)
return inner
return decorator
- Можно обобщить логику декоратора с аргументами в виде декоратора with_arguments
def with_arguments(deco):
@functools.wraps(deco)
def wrapper(*dargs, **dkwargs):
def decorator(func):
result = deco(func, *dargs, **dkwargs)
functools.update_wrapper(result, func)
return result
return decorator
return wrapper
- Что происходит:
- with_arguments принимает декоратор deco и должна вернуть декоратор
- заворачивает его во wrapper, так как deco - декоратор с аргументами, а затем в decorator
- decorator конструирует новый декоратор с помощью deco и копирует в него внутренние атрибуты функции func
@with_arguments
def trace(func, handle):
def inner(*args, **kwargs):
print(func.__name__, args, kwargs, file=handle)
return func(*args, **kwargs)
return inner
@trace(sys.stderr)
def identity(x):
return x
# Тут декоратор может быть с аргументами или без них,
# есть обработка обоих случаев
# lambda выполняет функцию deco из предыдущих примеров
def trace(func=None, *, handle=sys.stdout):
# со скобками
if func is None:
return lambda func: trace(func, handle=handle)
# без скобок
@functools.wraps(func)
def inner(*args, **kwargs):
print(func.__name__, args, kwargs)
return func(*args, **kwargs)
return inner
- Декоратор - способ модифицировать поведение функции, сохраняя читаемость кода
- Декораторы бывают:
-
- без аргументов @trace
-
- с аргументами @trace(sys.stderr)
-
- с опциональными аргументами
def timethis(func=None, *, n_iter=100):
if func is None:
return lambda func: timethis(func, n_iter=n_iter)
@functools.wraps(func)
def inner(*args, **kwargs):
print(func.__name__, end=" ... ")
acc = float("inf")
for i in range(n_iter):
tick = time.perf_counter()
result = func(*args, **kwargs)
acc = min(acc, time.perf_counter - tick)
print(acc)
return result
return inner
result = timethis(sum)(range(10 ** 6))
# sum ... 0.021247283742833462
Профилирование для бедных :) считает количество вызовов функции
def profiled(func):
@functools.wraps(func)
def inner(func, *args, **kwargs):
inner.ncalls += 1
return func(*args, **kwargs)
inner.ncalls = 0
return inner
@profiled
def identity(x):
return x
# >>> identity(42)
# 42
# >>> identity.ncalls
# 1
Функция, которая делает что-то один раз.
Данная реализация работает, только если внутренняя функция ничего не возвращает. Иначе нужно либо писать результат в атрибут, либо в переменную из объемлющей области видимости.
def once(func):
@functools.wraps(func)
def inner(*args, **kwargs):
if not inner.called:
func(*args, **kwargs)
inner.called = True
inner.called = False
return inner
@once
def initialize_settings():
print("Settings initialized.")
# >>> initialize_settings()
# Settings initialized.
# >>> initialize_settings()
#
- Мемоизация - сохранение результатов выполнения функции для предотвращения избыточных вычислений.
- Декоратор для автоматической мемоизации "любой" функции:
# Неработающий (из-за изменяемости словарей, словарь не хешируемый) декоратор
# Если уже считали - вернём,
# если ещё не - посчитаем и запомним
def memoized(func):
cache = {}
@functools.wraps(func)
def inner(*args, **kwargs):
key = args, kwargs
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return inner
# Функция Аккермана и @memoized
def ackermann(m, n):
if not m:
return n + 1
elif not n:
return ackermann(m - 1, 1)
else:
return ackermann(m - 1, ackermann(m, n - 1))
# >>> ackermann(3, 4)
# TypeError: unhashable type: 'dict'
# Работающий декоратор
# Частное решение проблемы:
def memoized(func):
cache = {}
@functools.wraps(func)
def inner(*args, **kwargs):
key = args + tuple(sorted(kwargs.items()))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return inner
ackermann(3, 4)
# 125
Нет универсального и быстрого решения. Можно сериализовывать аргументы в строку, например, через str или, что более осмысленно, через pickle
def deprecated(func):
code = func.__code__
warnings.warn_explicit(
func.__name__ + "is deprecated.",
category=DeprecationWarning,
filename=code.co_filename,
lineno=co_firstlineno + 1)
return func
@deprecated
def identity(x):
return x
# <stdin>:2: DeprecationWarning: identity is deprecated.
# В момент импорта, в момент компиляции в байт-код
- Контрактное программирование - способ проектирования программ, основывающийся на формальном описании интерфейсов в терминах предусловий, постусловий и инвариантов.
- В Python контрактное программирование можно реализовать в виде библиотеки декораторов: https://pypi.python.org/pypi/contracts
# Проверка что аргумент логарифма не отрицателен
>>> @pre(lambda x: x >= 0, "negative argument")
... def checked_log(x):
... pass
...
# Проверка что функция NaN не вернула
# Декоратор можно записать в переменную
>>> is_not_nan = post(lambda r: not math.isnan(r), "not a number")
>>> @is_not_nan
... def something_useful():
... pass
...
def pre(cond, message):
def wrapper(func):
@functools.wraps(func)
def inner(*args, **kwargs):
assert cond(*args, **kwargs), message
return func(*args, **kwargs)
return inner
return wrapper
@pre(lambda r: r >= 0, "negative argument")
def checked_log(x):
return math.log(x)
# checked_log(-42)
# AssertionError: negative argument
# В post assert после применения функции
def post(cond, message):
def wrapper(func):
@functools.wraps(func)
def inner(*args, **kwargs)
result = func(*args, **kwargs)
assert cond(result), message
return result
return inner
return wrapper
@post(lambda x: not math.isnan(x), "not a number")
def something_useful():
return float("nan")
# >>> something_useful()
# AssertionError: not a numbet
- Синтаксис Python разрешает одновременное применение нескольких декораторов
- Порядок декораторов имеет значение:
>>> def square(func):
... return lambda x: func(x * x)
...
>>> def addsome(func)
... return lambda x: func(x + 42)
...
>>> # identity = square(addsome(identity)) Поэтому выполнится сверху вниз
>>> @square
... @addsome
... def identity(x):
... return x
...
>>> identity(2)
46
>>> # ============================================================
>>> @addsome
... @square
... def identity(x):
... return x
...
>>> identity(2)
1936
https://wiki.python.org/moin/PythonDecoratorLibrary
- Это декоратор
- Родственник уже рассмотренного memoized, сохраняющий фиксированное количество последних вызовов
- lru_cache с функцией Аккермана:
>>> @functools.lru_cache(maxsize=64)
... def ackermann(m, n):
... # ...
...
>>> ackermann(3, 4)
125
# сколько раз попали в кэш, сколько не попали, размер кэша
>>> ackermann.cache_info()
CacheInfo(hits=65, misses=315, maxsize=64, currsize=64)
- Можно не ограничивать количество сохраняемых вызовов, тогда мы получим в точности поведение memoized:
# MemoryLeak
>>> @functools.lru_cache(maxsize=None)
... def ackermann(m, n):
... # ...
...
- Функция
- С помощью partial можно зафиксировать часть позиционных и ключевых аргументов в функции
- Пример:
# По какому ключу делать сортировку (по второму элементу пары)
>>> f = functools.partial(sorted, key=lambda p: p[1])
>>> f([("a", 4), ("b", 2)])
[('b', 2), ('a', 4)]
>>> g = functools.partial(sorted, [2, 3, 1, 4])
>>> g()
[1, 2, 3, 4]
- Функция len называется обобщённой, так как её реализация может быть специализирована для конкретного типа
>>> len([1, 2, 3, 4])
4
>>> len((1, 2, 3, 4))
4
>>> len(range(4))
4
- Примеры обобщённых функций в Python:
>>> str([1, 2, 3, 4])
'[1, 2, 3, 4]'
>>> hash((1, 2, 3, 4))
485696759010151909
# конкатенация списков
>>> sum([[1], [2]], [])
[1, 2]
https://www.python.org/dev/peps/pep-0443/
- Декоратор, позволяет писать функции, которые по-разному работают в зависимости от аргумента, который им передали
- В качестве примера реализуем функцию pack, которая сериализует объект (упаковывает в бинарную строку) в компактное строковое представление
>>> @functools.singledispatch
... def pack(obj):
... type_name = type(obj).__name__
... assert False, "Unsupported type: " + type_name
- Научим функцию pack сериализовывать числа и списки
>>> @pack.register(int)
... def _(obj):
... return b"I" + hex(obj).encode("ascii")
...
>>> @pack.register(list)
... def _(obj):
... return b"L" + b",".join(map(pack, obj))
...
>>> pack([1, 2, 3])
b'LI0x1,I0x2,I0x3'
>>> pack(42.)
AssertionError: Unsupported type: float
Модуль functools: reduce
- reduce как правило не нужен, генератор чего угодно будет читаемее функциональщины
- Рассмотрим:
# левая свёртка (поскольку операция лево-ассоциативна)
>>> sum([1, 2, 3, 4], start=0)
10
>>> (((0 + 1) + 2) + 3) + 4
10
- Умножение
>>> ((1 * 2) * 3) * 4
24
- Функция reduce обобщает логику функции sum на произвольную бинарную операцию
>>> functools.reduce(lambda acc, x: acc * x, [1, 2, 3, 4])
24
- Функция reduce принимает три аргумента: бинарную функцию, последовательность и опциональное начальное значение
- Вычисление reduce(op, xs, initial) можно схематично представить как:
>>> op(op(op(op(initial, xs[0]), xs[1]), xs[2]), ...)
>>> op(op(op(xs[0], xs[1]), xs[2]), ...)
- Несколько примеров:
# Переводит строку к числу (на самом деле можно было просто int(строки) вызвать
# и не городить свёртку)
# Слева - то что уже накопили, справа - текущие элемент
>>> functools.reduce(lambda acc, d: 10 * acc + int(d),
... "1914", initial=0)
1914
# Допустим, у нас есть функция merge, которая сливает два сортированных списка,
# тогда с помощью reduce можно обобщить её на произвольное число списков
>>> functools.reduce(merge, [[1, 2, 7], [5, 6], [0]], initial=[])
[0, 1, 2, 5, 6, 7]
- Несмотря на свою популярность в функциональных языках, в Python довольно сложно придумать полезный пример использования reduce.
- Резюме про reduce:
-
- работает с любым объектом, поддерживающим протокол итератора;
-
- работает слева направо;
-
- использует перавый элемент последовательности, если начальное значение не указано явно.
- Подряд идущие строковые литералы "склеиваются"
# Плюсы не нужны! +
>>> "foo" "bar"
'foobar'
f("foo"
"bar"
"baz")
- Строковые литералы могут содержать экранированные последовательности, например:
\' одинарная кавычка,
\" двойная кавычка,
\t символ вертикальной табуляции,
\n символ переноса строки,
\xhh сивол с HEX кодом hh
- В "сырых" строковых литералах экранированные последовательности не обрабатываются.
>>> print("\tell me")
ell me
>>> print(r"\tell me")
\tell me
Полный список поддерживаемых последовательностей можно прочитать в документации
- Тип str - неизменяемая последовательность символов Юникод:
>>> s = "я строка"
>>> list(s)
['я', ' ', 'с', 'т', 'р', 'о', 'к', 'а']
- Отдельного типа для символов в Python нет: каждый символ строки - тоже строка:
>>> s[0], type(s[0])
('я', <class 'str'>)
>>> list(s[0])
['я']
https://www.python.org/dev/peps/pep-0393/
- Функция ord принимает символ и возвращает его номер в таблице Unicode
# Python 3.3 и старше
>>> list(map(ord, "hello")) # UCS-1
[104, 101, 108, 108, 111]
>>> list(map(ord, "привет"))
[1087, 1088, 1080, 1074, 1077, 1090]
>>> ord("🁦")
127028
- Python поддерживает экранированные последовательности для символов Юникода:
>>> "\u0068", "\U00000068"
('h', 'h')
>>> "\N{DOMINO TILE HORIZONTAL-00-00}"
'🀱'
- Получить символ Юникода по его коду можно с помощью функции chr:
>>> chr(0x86)
'h'
>>> chr(1087)
'n'
- Очевидное наблюдение:
>>> def identity(ch):
... return chr(ord(ch))
...
>>> identity("n")
'n'
- Не сразу очевидное наблюдение с эс-цет
>>> "\N{LATIN SMALL LETTER SHARP S}"
'ß'
>>> ch = "\N{LATIN SMALL LETTER SHARP S}"
>>> ch.upper()
'SS'
>>> ch.upper().lower()
'ss'
>>> "foo bar".capitalize() # делает из строки предложение
'Foo bar'
>>> "foo bar".title()
'Foo Bar'
>>> "foo bar".upper()
'FOO BAR'
>>> "foo bar".lower()
'foo bar'
>>> "foo bar".title().swapcase()
'fOO bAR'
- Группа методов, выравнивающих строку в "блоке" фиксированной длины. При этом дополнительные позиции заполняются указанным символом:
>>> "foo bar".ljust(16, '~')
'foo bar~~~~~~~~~'
>>> "foo bar".rjust(16, '~')
'~~~~~~~~~foo bar'
>>> "foo bar".center(16, '~')
'~~~~foo bar~~~~~'
- В качестве символа по умолчанию используется пробел:
>>> "foo bar".ljust(16)
'foo bar '
>>> "foo bar".rjust(16)
' foo bar'
>>> "foo bar".center(16)
' foo bar '
- Если длина "блока" меньше длины строки, то строка возвращается без изменений
- Группа методов, заканчивающихся на strip, удаляет все вхождения указанных символов слева, справа или с обоих концов строки:
# Аргумент - это не строка, это независимые символы
>>> "]>>foo bar<<[".lstrip("]>")
'foo bar<<['
>>> "]>>foo bar<<[".lstrip("[<")
']>>foo bar'
>>> "]>>foo bar<<[".strip("[]<>")
'foo bar'
- По умолчанию удаляются все пробельные символы (пробел, табуляция, возврат каретки, перенос строки)
>>> "\t foo bar \r\n ".strip()
'foo bar'
- Метод split разделяет строку на подстроки по указанному разделителю:
>>> "foo,bar".split(",")
['foo', 'bar']
>>> "foo...bar".split(",")
['foo', '', '', 'bar']
>>> "foo.bar.baz.tar.gz".split(".")
['foo', 'bar', 'baz', 'tar', 'gz']
>>> "foo.bar.baz.tar.gz".split(".", 1)
['foo', 'bar.baz.tar.gz']
>>> "foo.bar.baz.tar.gz".rsplit(".", 1) # rsplit!
['foo.bar.baz.tar', '.gz']
>>> "foo.bar.baz.tar.gz".rsplit(".", 1000)
['foo', 'bar', 'baz', 'tar', 'gz']
- Если разделитель не указан, то строка разделяется по пробелам (любое количество разных пробельных символов считаются одним пробельным символом)
>>> "\t foo bar \r\n. ".split()
['foo', 'bar']
- Метод partition возвращает кортеж из трёх элементов: подстрока до вхождения разделителя, сам разделитель и подстрока после вхождения разделителя
>>> "foo, bar,baz".partition(",")
('foo', ',', 'bar,baz')
>>> "foo, bar,baz".rpartition(",")
('foo, bar', ',', 'baz')
Структура результата у partition всегда фиксированна, в отличие от split
>>> "foobar".rsplit(".", 1)
['foobar']
>>> "foobar".rpartition(".")
('', '', 'foobar')
- Если надо много всего конкатенировать, лучше использовать join
- С помощью метода join можно соединить любую последовательность строк:
>>> ", ".join(["foo", "bar", "baz"])
'foo, bar, baz'
>>> ", ".join(filter(None, ["", "foo"]))
'foo'
>>> ", ".join("bar")
'b, a, r'
- Если последовательность содержит объекты другого типа, будет ошибка (нужно явно преобразовать всё к строке):
>>> ", ".join(range(10))
TypeError: sequence item 0: expected str instance [...]
- Вхождение подстроки идиоматично проверять с помощью операторов in и not in:
>>> "foo" in "foobar"
True
>>> "yada" not in "foobar"
True
- Также можно сравнивать префикс или суффикс строки с одной или несколькими строками:
>>> "foobar".startswith("foo")
True
>>> "foobar".endswith(("boo", "bar")) # True, если хотя бы один подошёл
True
django-admin startproject project
# PEP-0249
import MySQLdb
db = MySQLdb.connect(host="localhost", user="joe", passwd="moonpie", db="things")
cursor = db.cursor()
# запросы с использованием cursor
db.commit()
db.close()
cursor.execute("""update users set age = age + 1 where name = %s""", (name,))
cursor.execute("select * from users")
users = cursor.fetchall()
cursor.execute("""select * from users where name = %s""", (name,))
user = cursor.fetchone()
cursor.executemany(
"INSERT INTO users (name, age) VALUES (%s, %s)",
[
("Igor", 18),
("Petr", 16),
("Dasha", 17)
]
)
db.commit()
db.close()
from django.db import connection, connections
cur = connection.cursor()
cur.execute("select * from tbl limit 10")
default_cur = connections['default'].cursor() # default - имя db из конфига приложения
default_cur.execute("select * from tbl2 limit 10")
another_cur = connections['another'].cursor()
another_cur.execute("select * from tbl2 limit 10")
• ./manage.py validate - проверить структуру моделей • ./manage.py syncdb - создать таблицы в базе (1 раз) • ./manage.py shell - запустить python shell • ./manage.py dbshell - запустить клиент базы данных
Django - SQL класс Модели - Таблица объект модели - строка таблицы QuerySet - запрос
# ORM vs SQL
cursor.execute('select * from users where age > 18')
for user in cursor.fetchall():
pk, name, age = user
print name
for user in User.objects.filter(age__gt=18):
print user.name
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
creation_date = models.DateTimeField(blank=True)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return '/post/%d/' % self.pk
class Meta:
db_table = 'blogposts'
ordering = ['-creation_date']
Django - MySQL
CharField - VARCHAR(N)
EmailField
TextField - LONGTEXT
BooleanField - TINYINT(1)
IntegerField - INT(11)
DateField - DATE
DateTimeField - DATETIME
• blank - поле может быть пустым • null - при этом хранится в базе как NULL • max_length - максимальная длина поля • primary_key - это поле - первичный ключ • unique - поле уникально • db_index - для этого поля нужен индекс в базе • default - значение по-умолчанию • choices - варианты значений
import string
clean = lambda dirty: ''.join(filter(string.printable.__contains__, dirty))
It simply filters out all non-printable characters from the dirty string it receives.
>>> len(clean(map(chr, range(0x110000))))
100
String to bool
>>> import json
>>> json.loads("false".lower())
False
>>> json.loads("True".lower())
True