Заметки по книге "Простой python" [Глава 5-8]


Глава 5. Py Boxes: модули, пакеты и программы

Created: Jan 31, 2021 10:09 AM

Запуск файла


#!/usr/bin/env python3

print("Этот текст из скрипта")

$ python3 hello.py 
Этот текст из скрипта

Передать аргументы скрипту


#!/usr/bin/env python3

import sys

print("Аргументы скрипта: ", sys.argv)

$ python3 hello.py test=1 test2
Аргументы скрипта:  ['hello.py', 'test=1', 'test2']

#!/usr/bin/env python3

import sys

print("Аргументы скрипта: ", sys.argv)

first_argv = sys.argv[1]
print("Первый аргумент: ", first_argv)

$ python3 hello.py test
Аргументы скрипта:  ['hello.py', 'test']
Первый аргумент:  test

Без аргументов упадет


$ python3 hello.py
Аргументы скрипта:  ['hello.py']
Traceback (most recent call last):
  File "hello.py", line 7, in <module>
    first_argv = sys.argv[1]
IndexError: list index out of range

Импорт модуля

Свой модуль


$ cat ./weather.py 
#!/usr/bin/env python3

from random import choice

def get_desc():
    '''
    Вернуть рандомную погоду
    '''
    weather_list = ['rain', 'snow', 'sleet', 'fog', 'sun', 'who knows']
    return choice(weather_list)


$ cat ./hello.py 
#!/usr/bin/env python3

import weather

today_weather = weather.get_desc();
print("Сегодня ", today_weather)

$ python3 hello.py
Сегодня  who knows
$ python3 hello.py
Сегодня  rain
$ python3 hello.py
Сегодня  rain
$ python3 hello.py
Сегодня  snow
$ python3 hello.py
Сегодня  snow
  • import ... - импортирует весь модуль и нужно обращаться по имени модуля.название функции

import weather
weather.get_desc();
  • from ... import ... - импортирует только указанную функцию. Нужно обращаться по имени функции

>>> from random import choice
>>> test = [1,2,3]
>>> choice(test)
3

>>> random.choice(test)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined
>>>

Импорт модуля с алиасом: as


#!/usr/bin/env python3

import random as r
print(r.choice([1,2,3]))

$ python3 hello.py
3
$ python3 hello.py
2
$ python3 hello.py
3
$

Тоже самое только с названием функции


#!/usr/bin/env python3

from random import choice as rand
print( rand([1,2,3]) )

$ python3 hello.py
3
$ python3 hello.py
1
$ python3 hello.py
1

Пакеты

Основная программа


$ cat ./boxes/weather.py 

from sources import daily, weekly

print("Прогноз на день: ", daily.forecast())
print("Недельный прогноз: ")

for index, weather in enumerate(weekly.forecast()):
    print(index, weather)

Модуль daily.py


$ cat ./boxes/sources/daily.py 

def forecast():
    return 'как вчера'

Модуль weekly.py


$ cat ./boxes/sources/weekly.py 

def forecast():
    return ['snow', 'more snow', 'sleet', 'rain']

$ tree ./boxes/
./boxes/
├── sources
│   ├── daily.py
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── daily.cpython-35.pyc
│   │   ├── __init__.cpython-35.pyc
│   │   └── weekly.cpython-35.pyc
│   └── weekly.py
└── weather.py

2 directories, 7 files
  • Есть еще init.py но он пустой
  • pycache - питон сам создал, пока не понятно зачем

$ python3 boxes/weather.py 
Прогноз на день:  как вчера
Недельный прогноз: 
0 snow
1 more snow
2 sleet
3 rain

Стандартные функции: setdefault(), defaultdict()

При обращении к последовательности в которой нет элемента, вызывается исключение

Словарь:


>>> test_dict = {'test1': 1, 'test2': 2}
>>> print(test_dict['test3'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'test3'
>>>

Можно сделать get()


>>> test_dict = {'test1': 1, 'test2': 2}
>>> print(test_dict.get('test3'))
None
>>>

или setdefault() если эл. не найдено


>>> test_dict = {'test1': 1, 'test2': 2}
>>> print(test_dict.setdefault('test3', 3))
3
>>> test_dict
{'test2': 2, 'test3': 3, 'test1': 1}

если эл. уже был, ничего не измениться


>>> test_dict
{'test2': 2, 'test3': 3, 'test1': 1}
>>> print(test_dict.setdefault('test2', 99999))
2
>>> test_dict
{'test2': 2, 'test3': 3, 'test1': 1}

defaultdict возвращает значение указанного типа если не найден эл.


>>> from collections import defaultdict
>>> 
>>> table = defaultdict(int)
>>> table['test2']
0
>>> table
defaultdict(<class 'int'>, {'test2': 0})
>>>

Можно использовать свои значения


from collections import defaultdict

def default_value():
    return 'default = 3'

table = defaultdict(default_value)

table['A'] = 1
table['B'] = 2

print(table['A'])
print(table['B'])
print(table['C'])

$ python3 ./test.py 
1
2
default = 3

можно обращаться и не боятся исключения


from collections import defaultdict

food_counter = defaultdict(int)

for food in ['картошка', 'яйцо', 'капуста', 'хлеб', 'яйцо']:
    food_counter[food] += 1 

for food, count in food_counter.items():
    print(food, count)

$ python3 ./test.py 
картошка 1
яйцо 2
капуста 1
хлеб 1
$

Доп. функции для работы со словарями

Счетчики: Counter, most_common

Кол-во uniq значений в списке


from collections import Counter

breakfast = ['яйцо', 'хлеб', 'яйцо', 'яйцо']
breakfast_counter = Counter(breakfast)
print(breakfast_counter)

$ python3 ./test.py 
Counter({'яйцо': 3, 'хлеб': 1})

from collections import Counter

test_dict = Counter({
    'test1': 1,
    'test2': 2,
    'test3': 3
})

test_list = dict( test_dict.most_common(1) )
print( test_list )

$ python3 ./test.py 
{'test3': 3}

Можно объединять, вычитать и все тоже самое что и с множеством


#!/usr/bin/env python3

from collections import Counter

breakfast = ['яйцо', 'хлеб', 'яйцо', 'яйцо']
breakfast_counter = Counter(breakfast)
print(breakfast_counter)

lunch = ['хлеб', 'борщь', 'чеснок', 'хлеб', 'компот']
lunch_counter = Counter(lunch)
print(lunch_counter)

print("Всего: ", breakfast_counter + lunch_counter)
print("Завтрак без обеда: ", breakfast_counter - lunch_counter)
print("Общее у завтрака и обеда: ", breakfast_counter & lunch_counter)

$ python3 ./test.py 
Counter({'яйцо': 3, 'хлеб': 1})
Counter({'хлеб': 2, 'чеснок': 1, 'компот': 1, 'борщь': 1})
Всего:  Counter({'хлеб': 3, 'яйцо': 3, 'чеснок': 1, 'борщь': 1, 'компот': 1})
Завтрак без обеда:  Counter({'яйцо': 3})
Общее у завтрака и обеда:  Counter({'хлеб': 1})

Запомнить в каком порядке возвращать ключи у словаря: OrderedDict


from collections import OrderedDict

test_dict = OrderedDict([
    ('test1', 'test_v1'),
    ('test2', 'test_v2'),
    ('test3', 'test_v3')
])

print(test_dict)

for i, k in enumerate(test_dict):
    print("index =", i, "key =", k, "value =", test_dict[k])

$ python3 ./test.py 
OrderedDict([('test1', 'test_v1'), ('test2', 'test_v2'), ('test3', 'test_v3')])
index = 0 key = test1 value = test_v1
index = 1 key = test2 value = test_v2
index = 2 key = test3 value = test_v3

Очередь: deque


#!/usr/bin/env python3

from collections import deque

def palindrome(word):
    dq = deque(word)
    while len(dq) > 1:
        if dq.popleft() != dq.pop():
            return False
    return True

print( palindrome('') )
print( palindrome('a') )
print( palindrome('racecar') )
print( palindrome('test') )

$ python3 ./test.py 
True
True
True
False
  • popleft - возвращает и удаляет эл. слева
  • pop - возвращает и удаляет эл. справа

Глава 6: объекты и классы

Created: Feb 1, 2021 8:09 PM

Пример создания класса

Самый простой класс, класс который ничего не делает


class Person():
    pass

someone = Person()
print(someone)

$ python3 ./test.py 
<__main__.Person object at 0x7fbab9efea58>

class Person():
    def __init__(self, name):
        self.name = name
        pass

someone = Person('Распутин')
print(someone)
print(someone.name)

$ python3 ./test.py 
<__main__.Person object at 0x7fba8a7e7a90>
Распутин
  • init(self, name) - спец. название метода по которому инициализируется объект.
    • self - первый объект при инициализации, всегда называется self
    • name - атрибут объекта

Наследование


class Car():
    def exclaim(self):
        print("Я машина", self)

class Yoga(Car):
    pass

car = Car()
yoga = Yoga()

car.exclaim()
yoga.exclaim()

$ python3 ./test.py 
Я машина <__main__.Car object at 0x7f955bdc6c50>
Я машина <__main__.Yoga object at 0x7f955bdc6c88>
  1. Создается класс Car
  2. У класса метод exclaim выводит строку “Я машина”
  3. Создается класс Yoga с наследованием от класса Car
  4. Создается два объекта и у обоих доступен метод exclaim

Перегрузка метода


class Car():
    def print_test(self):
        print("Test from class Car")
    def exclaim(self):
        print("Я машина", self)

class Yoga(Car):
    def print_test(self):
        print("Test from class Yoga")

car = Car()
yoga = Yoga()

car.exclaim()
yoga.exclaim()
print()
car.print_test()
yoga.print_test()

$ python3 ./test.py 
Я машина <__main__.Car object at 0x7fc70d9d3cc0>
Я машина <__main__.Yoga object at 0x7fc70d9d3cf8>

Test from class Car
Test from class Yoga
  • В методе Car есть метод print_test который переопределен в классе Yoga

Можно перегружать любые методы, в том числе инициализацию (init)


class Person():
    def __init__(self, name):
        self.name = name

class MPerson(Person):
    def __init__(self, name):
        self.name = "Doctor " + name

someone = Person('Распутин')
print(someone)
print(someone.name)

some_else = MPerson('Двапутин')
print(some_else.name)

$ python3 ./test.py 
<__main__.Person object at 0x7fd122ef5c18>
Распутин
Doctor Двапутин

Отмена перегрузки, вызов родительского метода: super()


class Person():
    def __init__(self, name):
        self.name = name

class EmailPerson(Person):
    def __init__(self, name, email):
        super().__init__(name)
        self.email = email

someone = EmailPerson('Распутин', 'rasputin@mail.ru')
print(someone)
print(someone.name)
print(someone.email)

$ python3 ./test.py 
<__main__.EmailPerson object at 0x7f2f92a19c18>
Распутин
rasputin@mail.ru

Публичные/приватные атрибуты (геттеры и сеттеры)


class Duck():
    def __init__(self, name):
        self.hidden_name = name
    def get_name(self):
        print('inside the getter')
        return self.hidden_name
    def set_name(self, name):
        print('inside the setter; new name = ', name)
        self.hidden_name = name
    name = property(get_name, set_name)

duck = Duck('Скрудж')
print(duck.name)

duck.name = 'Не скрудж'
print(duck.name)

$ python3 ./test.py 
inside the getter
Скрудж
inside the setter; new name =  Не скрудж
inside the getter
Не скрудж
  • Все методы как обычные, только property отличается
  • property - первый аргумент геттер, второй - сеттере
  • Можно вызвать как обычный метод -

duck = Duck('Скрудж')
print(duck.get_name())

duck.set_name('Не скрудж')
print(duck.name)

$ python3 ./test.py 
inside the getter
Скрудж
inside the setter; new name =  Не скрудж
inside the getter
Не скрудж

Через декораторы: @property, @self.setter


class Duck():
    def __init__(self, name):
        self.hidden_name = name
    @property
    def name(self):
        print('inside the getter')
        return self.hidden_name
    @name.setter
    def name(self, name):
        print('inside the setter; new name = ', name)
        self.hidden_name = name

duck = Duck('Скрудж')
print(duck.name)

duck.name = 'Не скрудж'
print(duck.name)

$ python3 ./test.py 
inside the getter
Скрудж
inside the setter; new name =  Не скрудж
inside the getter
Не скрудж

Значение может быть вычисляемым


class Circle():
    def __init__(self, radius):
        self.radius = radius
    @property
    def diameter(self):
        return 2 * self.radius

circle = Circle(2)
print("1. Радиус = ", circle.radius, "Диаметр = ", circle.diameter)
circle.radius = 7
print("2. Радиус = ", circle.radius, "Диаметр = ", circle.diameter)

circle2 = Circle(4)
print("3. Радиус = ", circle2.radius, "Диаметр = ", circle2.diameter)

$ python3 ./test.py 
1. Радиус =  2 Диаметр =  4
2. Радиус =  7 Диаметр =  14
3. Радиус =  4 Диаметр =  8
  • По факту, property позволяет делать методы как атрибуты объекта
  • Но при этом теряются все свойства как метода

Protected атрибуты

Если просто записать атрибут в self, то даже при наличии сеттеров и геттеров, можно получить доступ извне


class Duck():
    def __init__(self, name):
        self.hidden_name = name
    @property
    def name(self):
        print('inside the getter')
        return self.hidden_name
    @name.setter
    def name(self, name):
        print('inside the setter; new name = ', name)
        self.hidden_name = name

duck = Duck('Скрудж')
print(duck.hidden_name)

duck.hidden_name = 'Не скрудж'

print(duck.name)

$ python3 ./test.py 
Скрудж
inside the getter
Не скрудж

Чтобы закрыть атрибуты, нужно добавить __ в начало атрибута


class Duck():
    def __init__(self, name):
        self.__name = name
    @property
    def name(self):
        print('inside the getter')
        return self.__name
    @name.setter
    def name(self, name):
        print('inside the setter; new name = ', name)
        self.__name = name

duck = Duck('Скрудж')
print(duck.name)

duck.name = 'Не скрудж'
print(duck.name)

$ python3 ./test.py 
inside the getter
Скрудж
inside the setter; new name =  Не скрудж
inside the getter
Не скрудж

При обращении к закрытому атрибуту, будет исключение


duck = Duck('Скрудж')
print(duck.__name)

$ python3 ./test.py 
Traceback (most recent call last):
  File "./test.py", line 32, in <module>
    print(duck.__name)
AttributeError: 'Duck' object has no attribute '__name'

Однако и эту защиту можно обойти


duck = Duck('Скрудж')
print(duck._Duck__name)

duck._Duck__name = 'Тест'
print(duck.name)

$ python3 ./test.py 
Скрудж
inside the getter
Тест

Методы и атрибуты класса

Для определения метода класса, нужно добавить декоратор @classmethod


class TestClass():
    count = 0
    def __init__(self):
        TestClass.count += 1
    def print_test(self):
        print('test', self)
    @classmethod
    def kids(cls):
        print("Всего ", cls.count, "объектов")

a = TestClass()
b = TestClass()
c = TestClass()

a.print_test()
b.print_test()
c.print_test()

TestClass.kids()

$ python3 ./test.py 
test <__main__.TestClass object at 0x7f77e3c4ac18>
test <__main__.TestClass object at 0x7f77e3c4ac88>
test <__main__.TestClass object at 0x7f77e3c4acc0>
Всего  3 объектов

Полиморфизм! Утиная типизация?

Полиморфизм - одна операция может быть произведена над разными объектами независимо от их класса. Если нечто выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, и есть утка!


class Quote():
    def __init__(self, person, words):
        self.person = person
        self.words = words
    def who(self):
        return self.person
    def says(self):
        return self.words + '.'

class QuestionQuote(Quote):
    def says(self):
        return self.words + '?'

class ExclaimQuote(Quote):
    def says(self):
        return self.words + '!!!'

barney = Quote('Барни Стинсон', 'Ложь - это прекрасная история, которую портят правдой')
print(barney.who(), 'однажды сказал:', barney.says())

marshal = QuestionQuote('Маршал Эриксон', 'Мы щас точно говорим про блинчики')
print(marshal.who(), 'однажды сказал:', marshal.says())

marshal = ExclaimQuote('Маршал Эриксон', 'Ох ты ж ёёёёёжик')
print(marshal.who(), 'однажды сказал:', marshal.says())

$ python3 ./test.py 
Барни Стинсон однажды сказал: Ложь - это прекрасная история, которую портят правдой.
Маршал Эриксон однажды сказал: Мы щас точно говорим про блинчики?
Маршал Эриксон однажды сказал: Ох ты ж ёёёёёжик!!!

Специальные методы:

Простой метод сравнения строк


class Word():
    def __init__(self, text):
        self.text = text
    def equals(self, word2):
        return self.text.lower() == word2.lower()

f1 = Word('привет')
f2 = Word('ПРИВЕТ')
f3 = Word('Пока')

print(f1.text, '==', f2.text, '? ', f1.equals(f2.text) )
print(f2.text, '==', f3.text, '? ', f2.equals(f3.text) )

$ python3 ./test.py 
привет == ПРИВЕТ ?  True
ПРИВЕТ == Пока ?  False

Спец метод eq переопределяет операцию сравнения


class Word():
    def __init__(self, text):
        self.text = text
    def __eq__(self, word2):
        return self.text.lower() == word2.text.lower()

f1 = Word('привет')
f2 = Word('ПРИВЕТ')
f3 = Word('Пока')

print(f1.text, '==', f2.text, '? ', f1 == f2 )
print(f2.text, '==', f3.text, '? ', f2 == f3 )

python3 ./test.py 
привет == ПРИВЕТ ?  True
ПРИВЕТ == Пока ?  False

Композиция


class Bill():
    def __init__(self, desc):
        self.desc = desc

class Tail():
    def __init__(self, length):
        self.length = length

class Duck():
    def __init__(self, bill, tail):
        self.bill = bill
        self.tail = tail
    def about(self):
        print("Это утка, у нее", self.bill.desc, "клюв и", self.tail.length, "хвост")

bill = Bill("широкий и оранжевый")
tail = Tail("длиный")

duck = Duck(bill, tail)
duck.about()

$ python3 ./test.py 
Это утка, у нее широкий и оранжевый клюв и длиный хвост

Именованный кортеж: nametuple


from collections import namedtuple

Duck = namedtuple('Duck', 'bill tail')
duck = Duck("широкий и оранжевый", "длинный")

print(duck)
print('bill = ', duck.bill)
print('tail = ', duck.tail)

$ python3 ./test.py 
Duck(bill='широкий и оранжевый', tail='длинный')
bill =  широкий и оранжевый
tail =  длинный

Тоже самое только через словарь


from collections import namedtuple

Duck = namedtuple('Duck', 'bill tail')

parts = {'bill': "широкий и красный", 'tail': "узкий"}
duck2 = Duck(**parts)

print(duck2)
print('bill = ', duck2.bill)
print('tail = ', duck2.tail)

$ python3 ./test.py 
Duck(bill='широкий и красный', tail='узкий')
bill =  широкий и красный
tail =  узкий
  • **parts - разыменование аргументов

Классы и объекты vs модули

  • Объекты наиболее полезны, когда вам нужно иметь некоторое количество отдельных экземпляров с одинаковым поведением (методами), но различающихся внутренним состоянием (атрибутами).
  • Классы, в отличие от модулей, поддерживают наследование.
  • Если вам нужен только один объект, модуль подойдет лучше. Независимо от того, сколько обращений к модулю имеется в программе, будет загружена толь- ко одна копия.
  • Если у вас есть несколько переменных, которые содержат разные значения и могут быть переданы как аргументы в несколько функций, лучше всего опре- делить их как классы.
    • Например, вы можете использовать словарь с ключами size и color , чтобы представить цветное изображение. Вы можете создать разные словари для каждого изображения в программе и передавать их в качестве аргументов в функции scale() и transform(). По мере добавления новых ключей и функций может начаться путаница. Более последовательно было бы опреде- лить класс Image с атрибутами size или color и методами scale() и transform(). В этом случае все данные и методы для работы с цветными изображениями будут определены в одном месте.
  • Используйте простейшее решение задачи. Словарь, список или кортеж проще, компактнее и быстрее, чем модуль, который, в свою очередь, проще, чем класс.

Глава 7: Работаем с данными профессионально

Created: Feb 3, 2021 7:28 PM

Строки

Unicode

Проверка


import unicodedata

def unicode_test(val):
    name = unicodedata.name(val)
    value2 = unicodedata.lookup(name)
    print("value=[%s]; name=[%s]; value2=[%s]" % (val, name, value2))

unicode_test('A')
unicode_test('$')
unicode_test('П')
unicode_test('ё')

unicode_test('\u20ac')
unicode_test('\u20a2')

$ python3 ./test.py 
value=[A]; name=[LATIN CAPITAL LETTER A]; value2=[A]
value=[$]; name=[DOLLAR SIGN]; value2=[$]
value=[П]; name=[CYRILLIC CAPITAL LETTER PE]; value2=[П]
value=[ё]; name=[CYRILLIC SMALL LETTER IO]; value2=[ё]
value=[€]; name=[EURO SIGN]; value2=[€]
value=[₢]; name=[CRUZEIRO SIGN]; value2=[₢]

Кодирование строк


snowman = '\u2603'
print(snowman.encode('utf-8'))

$ python3 ./test.py 
b'\xe2\x98\x83'

Декодирование строк


snowman = '\u2603'
encode_str = snowman.encode('utf-8')

encode_str = encode_str.decode('utf-8')

print(encode_str)

$ python3 ./test.py 
☃

Форматирование строк

Старый стиль - процентный


n = 42
f = 99998.04
s = 'Строка'

print('%d %f %s' % (n, f, s))

print("Минимальная длина поля 10 символов, выравнивание по правому краю")
print('%10d %10f %10s' % (n, f, s))

print("Выравнивание по левому краю")
print('%-10d %-10f %-10s' % (n, f, s))

print("Макс. кол-во символов = 4. Для дробных - 4 символа после запятой")
print('%10.4d %10.4f %10.4s' % (n, f, s))

print("Длинна полей из аргументов")
print('%*.*d %*.*f %*.*s' % (10, 3, n, 10, 3, f, 10, 3, s))

$ python3 ./test.py 
42 99998.040000 Строка
Минимальная длина поля 10 символов, выравнивание по правому краю
        42 99998.040000     Строка
Выравнивание по левому краю
42         99998.040000 Строка    
Макс. кол-во символов = 4. Для дробных - 4 символа после запятой
      0042 99998.0400       Стро
Длинна полей из аргументов
       042  99998.040        Стр

Новый стиль - {} и format


n = 42
f = 99998.04
s = 'Строка'

print('{} {} {}'.format(n, f, s))

print("Можно менять местами а не в порядке появления")
print('{1} {2} {0}'.format(n, f, s))

print("Или именовать")
print('{f} {n} {s}'.format(n=42, f=99998.04, s='Строка'))

d = {'n': 42, 'f': 99998.04, 's': 'Строка'}
print("Можно передать словарь")
print('{0[f]} {0[n]} {0[s]} {1}'.format(d, 'other'))

print("Формат поля через :")
print('{0:d} {1:f} {2:s}'.format(n, f, s))

print("Тоже самое только для именованных")
print('{f:f} {n:d} {s:s}'.format(n=42, f=99998.04, s='Строка'))

print("Длинна поля. К какому краю прижимать указывается через ><")
print('{0:>10d} {1:>10f} {2:>10s}'.format(n, f, s))

print("Длинна поля. К какому краю прижимать указывается через ><")
print('{0:<10d} {1:<10f} {2:<10s}'.format(n, f, s))

print("Длинна поля + по центру ^")
print('{0:^10d} {1:^10f} {2:^10s}'.format(n, f, s))

print("Заполнитель")
print('{0:!^10d} {1:!^10f} {2:!^10s}'.format(n, f, s))

print("Значение точность (после запятой)")
print('{0:>10.4f} {1:>10.4s}'.format(f, s))

print("Значение точности нельзя для целых чисел!")
print('{0:>10.4d}'.format(n))

$ python3 ./test.py 
42 99998.04 Строка
Можно менять местами а не в порядке появления
99998.04 Строка 42
Или именовать
99998.04 42 Строка
Можно передать словарь
99998.04 42 Строка other
Формат поля через :
42 99998.040000 Строка
Тоже самое только для именованных
99998.040000 42 Строка
Длинна поля. К какому краю прижимать указывается через ><
        42 99998.040000     Строка
Длинна поля. К какому краю прижимать указывается через ><
42         99998.040000 Строка    
Длинна поля + по центру ^
    42     99998.040000   Строка  
Заполнитель
!!!!42!!!! 99998.040000 !!Строка!!
Значение точность (после запятой)
99998.0400       Стро
Значение точности нельзя для целых чисел!
Traceback (most recent call last):
  File "./test.py", line 42, in <module>
    print('{0:>10.4d}'.format(n))
ValueError: Precision not allowed in integer format specifier

Супер новый формат (>+3.7) f”{}”


>>> hello = 'hello'
>>> f'{hello}'
'hello'
>>>

Больше напоминает интерполяцию, но типа больше возможностей?…


>>> f'2'
'{ 2 + 3 }'
>>> f'{2 + 3}'
'5'
>>>

Регулярные выражения

Если будет найдена подстрока в source, то выводится то что удалось найти


import re

source = "Godsmack - Just One Time"

match = re.match('God', source)
if match:
    print(match.group())

$ python3 ./test.py 
God

match работает только если найдет в начале строки!


>>> import re
>>> source = "Godsmack - Just One Time"
>>> match = re.match('Time', source)
>>> if match:
...     print(match.group())
...
>>>

Даже 1 символ не в начале будет не найдено


import re

source = "Godsmack - Just One Time"

match = re.match('odsmack', source)
if match:
    print(match.group())


  • search работает по всей строке, возвращает первое что найдет

import re

source = "Godsmack - Just One Time"

match = re.search('Just', source)
if match:
    print(match.group())

$ python3 ./test.py 
Just
  • findall - находит все совпадения и возвращает список

import re

source = "Godsmack - Just One Time"

match = re.findall('s', source)
print(match)

$ python3 ./test.py 
['s', 's']

import re

source = "Godsmack - Just One Time"

match = re.findall('s\w+', source)
print(match)

$ python3 ./test.py 
['smack', 'st']
  • split - разбить строку и вернуть кортеж

import re

source = "Godsmack - Just One Time"

match = re.split(' ', source)
print(type(match))
print(match)

$ python3 ./test.py 
<class 'list'>
['Godsmack', '-', 'Just', 'One', 'Time']
  • sub - найти и заменить

import re

source = "Godsmack - Just One Time"

match = re.sub(' ', '?', source)
print(type(match))
print(match)

$ python3 ./test.py 
<class 'str'>
Godsmack?-?Just?One?Time

Поиск через регулярные выражения


import re
import string

long_str = string.printable

print("Оригинальная строка:", long_str)

print("Только цифры:", re.findall("\d", long_str) )
print("Цифры, буквы, знак подчеркивания:", re.findall("\w", long_str) )
print("Пробелы:", re.findall("\s", long_str) )

$ python3 ./test.py 
Оригинальная строка: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~   

Только цифры: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
Цифры, буквы, знак подчеркивания: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_']
Пробелы: [' ', '\t', '\n', '\r', '\x0b', '\x0c']

Спецификаторы

Шаблон Совпадения abc Буквосочетание abc (expr) expr expr1 | expr2 expr1 или expr2 Любой символ, кроме \n ^ Начало строки источника $ Конец строки источника prev ? Ноль или одно включение prev prev * Ноль или больше включений prev, максимальное количество prev *? Ноль или больше включений prev, минимальное количество prev + Одно или больше включений prev, максимальное количество prev +? Одно или больше включений prev, минимальное количество prev { m } m последовательных включений prev prev { m, n } От m до n последовательных включений prev, максимальное количество prev { m, n }? От m до n последовательных включений prev, минимальное количество [abc] a, или b, или c (аналогично a|b|c) [^abc] Не (a, или b, или c) prev (?= next) prev, если за ним следует next prev (? ! next) prev, если за ним не следует next (?<=prev ) next next, если перед ним находится prev (?<! prev) next next, если перед ним не находится prev


import re

source = "Godsmack - Just One Time"

match = re.findall('One|Time', source)
print(type(match))
print(match)

$ python3 ./test.py 
<class 'list'>
['One', 'Time']

Получение нескольких значений

  • search -> groups возвращает кортеж,

import re

source = "Godsmack - Just One Time"

match = re.search('(One)|(Time)', source)
print(type(match))
print(match.groups())
print(match.group(0))

$ python3 ./test.py 
<class '_sre.SRE_Match'>
('One', None)
One

Можно указать имена через ?P&lt; name &gt; expr


import re

source = "Godsmack - Just One Time"

match = re.search('(?P<One>One).*(?P<Time>Time)', source)
print(type(match))
print(match.groups())
print(match.group('One'))

$ python3 ./test.py 
<class '_sre.SRE_Match'>
('One', 'Time')
One

Бинарные данные

bytes и bytearrey

  • bytes - кортеж байтов
  • bytearrey - список байтов

blist = [1,2,3,255]

vbytes = bytes(blist)
print('vbytes=', vbytes)

avbytes = bytearray(blist)
print('avbytes=', avbytes)

avbytes[1] = 10
print('avbytes=', avbytes)

vbytes[1] = 10 # кортеж не изменяем!
print('vbytes=', vbytes)

$ python3 test.py 
vbytes= b'\x01\x02\x03\xff'
avbytes= bytearray(b'\x01\x02\x03\xff')
avbytes= bytearray(b'\x01\n\x03\xff')
Traceback (most recent call last):
  File "test.py", line 15, in <module>
    vbytes[1] = 10 # кортеж не изменяем!
TypeError: 'bytes' object does not support item assignment
  • каждый элемент списка байтов может принимать значение от 0 до 256

byte_list = bytes([0, 255])
print("byte =", byte_list)

$ python3 test.py 
byte = b'\x00\xff'

Преобразование: struct



Битовые операторы

Глава 8: Данные должны куда-то попадать

Created: Feb 5, 2021 6:26 PM

Чтение и запись в файл: open


fileobj = open(filename, mode)
  • fileobj - объект файла (файлхендлер?)
  • filename - путь до файла
  • mode - тип файла и действие над файлом. Первая буква - операция
    • r - чтение. По умолчанию. Если файла нет, будет исключение
    • w - запись. Если файл не существует, то будет создан. Если существует - перезаписан
    • x - запись, но только если файл не существует
    • a - запись в конец если существует
  • Вторая буква - тип файла
    • t - текстовый файл
    • b - бинарный

text='''
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
'''

file = open('./test_file.txt', 'wt')
file.write(text)
file.close()

$ python3 test.py && cat test_file.txt 

Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.

write vs print

  • print по умолчанию добавляет пробел между параметрами и добавляет перенос в конце

file = open('./test_file.txt', 'wt')
print('test1', 'test2', 'test3', file=file)
file.close()

$ python3 test.py && cat test_file.txt 
test1 test2 test3

Чтобы убрать это свойство нужно передать sep и end


text='''\
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
'''

file = open('./test_file.txt', 'wt')
print(text, file=file, sep='', end='')
file.close()

$ python3 test.py && cat test_file.txt 
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.

Запись частями


text='''\
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
'''

file = open('./test_file.txt', 'wt')

size = len(text)
offset = 0
chunk = 10

while offset < size:
    print('offset =', offset, 'text =', text[offset:offset+chunk])
    file.write( text[offset:offset+chunk] )
    offset += chunk

file.close()

$ python3 test.py && cat test_file.txt 
offset = 0 text = Я инженер 
offset = 10 text = на сотню р
offset = 20 text = ублей,
И б
offset = 30 text = ольше я не
offset = 40 text =  получу.
М
offset = 50 text = не двадцат
offset = 60 text = ь пять, и 
offset = 70 text = я до сих п
offset = 80 text = ор
Не знаю
offset = 90 text = , чего хоч
offset = 100 text = у.

Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.

Запись только если файл существует: x


text='''\
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
'''

file = open('./test_file.txt', 'xt')
file.write(text)
file.close()

$ rm -rf ./test_file.txt 
$ python3 test.py && cat test_file.txt 
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
$ python3 test.py && cat test_file.txt 
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    file = open('./test_file.txt', 'xt')
FileExistsError: [Errno 17] File exists: './test_file.txt'
  • Можно отловить исключение и переопределить поведение

try:
    file = open('./test_file.txt', 'xt')
    file.write(text)
    file.close()
except FileExistsError:
    print("Файл существует. Попробуйте его удалить")
    exit(1)

$ python3 test.py && cat test_file.txt 
Файл существует. Попробуйте его удалить

Чтение файла: read, readline, readlines

  • Просто чтение файла целиком

file = open('./test_file.txt', 'rt')
text = file.read()
print(type(text))
print(text)
file.close

$ python3 test.py
<class 'str'>
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.

по умолчанию read читает весь файл. 1гб файл будет занимать 1гб оперативки

Чтение по частям

  • read вернет пустую строку если ничего не осталось

file = open('./test_file.txt', 'rt')

text = ''
chunk = 52
while True:
    fragment = file.read(chunk)
    print('---->>> fragment =', fragment)
    if not fragment:
        break
    text += fragment

print('all text =', text)

file.close

$ python3 test.py
---->>> fragment = Я инженер на сотню рублей,
И больше я не получу.
Мне
---->>> fragment =  двадцать пять, и я до сих пор
Не знаю, чего хочу.

---->>> fragment = 
all text = Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
  • readline - читает по срокам

file = open('./test_file.txt', 'rt')

text = ''
while True:
    line = file.readline()
    print('---->>> line =', line)
    if not line:
        break
    text += line

print('all text =', text)

file.close

$ python3 test.py
---->>> line = Я инженер на сотню рублей,

---->>> line = И больше я не получу.

---->>> line = Мне двадцать пять, и я до сих пор

---->>> line = Не знаю, чего хочу.

---->>> line = 
all text = Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
  • Тоже самое только через итератор

file = open('./test_file.txt', 'rt')

text = ''
for line in file:
    text += line

print('all text =', text)

file.close

$ python3 test.py
all text = Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
  • readlines читает построчно и возвращает список строк

file = open('./test_file.txt', 'rt')

lines = file.readlines()
print(type(lines))
print(lines)
print()

for line in lines:
    print(line, end='')

file.close

$ python3 test.py
<class 'list'>
['Я инженер на сотню рублей,\n', 'И больше я не получу.\n', 'Мне двадцать пять, и я до сих пор\n', 'Не знаю, чего хочу.\n']

Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
  • print(line, end=’’) - не будет добавлять перенос после каждого вывода т.к. он изначально сохранился в строке

Чтение и запись бинарных файлов


file = open('./binary_file.txt', 'wb')
bdata = bytes(range(0, 100))
file.write(bdata)
file.close()

open_file = open('./binary_file.txt', 'rb')
bdata = open_file.read()
print('bdata =', len(bdata))
open_file.close()

$ python3 test.py
bdata = 100

Закрытие файла автоматически после выхода из блока


file_name = 'text_file.txt'

with open(file_name, 'wt') as file:
    text = '''\
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.
    '''
    file.write(text)

open_file = open(file_name, 'rt')
print(open_file.read())
open_file.close()

$ python3 test.py
Я инженер на сотню рублей,
И больше я не получу.
Мне двадцать пять, и я до сих пор
Не знаю, чего хочу.

Смещение в файле: seek(), tell()

  • tell - говорит на какой позиции находится курсор

>>> bfile = open('./binary_file.txt', 'rb')
>>> bfile.tell()
0
  • seek - перемещает курсор на заданную позицию

>>> bfile.seek(99)
99
>>> bfile.tell()
99
>>> bdata = bfile.read()
>>> len(bdata)
1
>>> bdata
b'c'
>>>
  • Также можно передать откуда смещаться - origin

seek(offset, origin)
  • origin = 0 (default) - сместиться на offset байт с начала файла
  • origin = 1 - сместиться на offset с текущей позиции
  • origin = 2 - сместиться на offset с конца файла

>>> bfile.seek(0)
0
>>> bfile.seek(10)
10
>>> bfile.seek(10, 1)
20
>>> bfile.tell()
20
>>> bfile.seek(-10, 2)
90
>>>

Форматы файлов

csv

  • Запись в файл

import csv

music = [
    ['You Me At Six', 'Room to Breathe'],
    ['Saliva', 'Lose Yourself'],
    ['Kingdom of Giants', 'Burner'],
    ['Palisades', 'War']
]

with open('./last_music.csv', 'wt') as file:
    csvout = csv.writer(file)
    csvout.writerows(music)

$ python3 ./test.py && cat last_music.csv 
You Me At Six,Room to Breathe
Saliva,Lose Yourself
Kingdom of Giants,Burner
Palisades,War
  • Чтение из csv файла

def read_from_csv():
    with open(file_name, 'rt') as file:
        data = csv.reader(file)
        print(data)
        return [row for row in data]

music = read_from_csv()
print(music)

$ python3 ./test.py 
<_csv.reader object at 0x7f36b747bac8>
[['You Me At Six', 'Room to Breathe'], ['Saliva', 'Lose Yourself'], ['Kingdom of Giants', 'Burner'], ['Palisades', 'War']]
  • Через DictReader можно указать название колонок

import csv
from pprint import pprint

file_name = './last_music.csv'

def read_from_csv():
    with open(file_name, 'rt') as file:
        data = csv.DictReader(file, fieldnames=['artist', 'name'])
        print(data)
        return [row for row in data]

music = read_from_csv()
pprint(music)

<csv.DictReader object at 0x7f31a2465ba8>
[{'artist': 'You Me At Six', 'name': 'Room to Breathe'},
 {'artist': 'Saliva', 'name': 'Lose Yourself'},
 {'artist': 'Kingdom of Giants', 'name': 'Burner'},
 {'artist': 'Palisades', 'name': 'War'}]
  • DictWriter - записывает словарь + можно добавить заголовок через writeheader

import csv
from pprint import pprint

file_name = './last_music.csv'

music = [
    {
        'artist': 'You Me At Six',
        'name': 'Room to Breathe'
    },
    {
        'artist': 'Saliva', 'name':
        'Lose Yourself'
    },
    {
        'artist': 'Kingdom of Giants',
        'name': 'Burner'
    },
    {
        'artist': 'Palisades',
        'name': 'War'
    }
]

with open(file_name, 'wt') as file:
    cout = csv.DictWriter(file, fieldnames=['artist', 'name'])
    cout.writeheader()
    cout.writerows(music)

$ python3 ./test.py && cat last_music.csv 
artist,name
You Me At Six,Room to Breathe
Saliva,Lose Yourself
Kingdom of Giants,Burner
Palisades,War

def read_from_csv():
    with open(file_name, 'rt') as file:
        data = csv.DictReader(file)
        print(data)
        return [row for row in data]

music = read_from_csv()
pprint(music)

$ python3 ./test.py 
<csv.DictReader object at 0x7f02a4affb38>
[{'artist': 'You Me At Six', 'name': 'Room to Breathe'},
 {'artist': 'Saliva', 'name': 'Lose Yourself'},
 {'artist': 'Kingdom of Giants', 'name': 'Burner'},
 {'artist': 'Palisades', 'name': 'War'}]

xml


import xml.etree.ElementTree as et

tree = et.ElementTree(file='menu.xml')
root = tree.getroot()
print(root.tag)

for child in root:
    print("tag = [", child.tag, "] atributtes = [", child.attrib, ']', sep='')
    for grandchild in child:
        print("\ttag = [", grandchild.tag, "] atributtes = [", grandchild.attrib, ']', sep='')

$ cat menu.xml && python3 ./test.py 
<?xml version="1.0"?>
<menu>
    <breakfast hours="7-11">
        <item price="10">Блинчики</item>
    </breakfast>
</menu>menu
tag = [breakfast] atributtes = [{'hours': '7-11'}]
  tag = [item] atributtes = [{'price': '10'}]
$

JSON

  • dumps - закодировать в json строку

import json
from pprint import pprint

file_name = './last_music.json'

music = [
    {
        'artist': 'You Me At Six',
        'name': 'Room to Breathe'
    },
    {
        'artist': 'Saliva', 'name':
        'Lose Yourself'
    },
    {
        'artist': 'Kingdom of Giants',
        'name': 'Burner'
    },
    {
        'artist': 'Palisades',
        'name': 'War'
    }
]

with open(file_name, 'wt') as file:
    music_json = json.dumps(music)
    print(type(music_json))
    pprint(music_json)
    file.write(music_json)

$ python3 ./test.py && cat last_music.json 
<class 'str'>
('[{"artist": "You Me At Six", "name": "Room to Breathe"}, {"artist": '
 '"Saliva", "name": "Lose Yourself"}, {"artist": "Kingdom of Giants", "name": '
 '"Burner"}, {"artist": "Palisades", "name": "War"}]')
[{"artist": "You Me At Six", "name": "Room to Breathe"}, {"artist": "Saliva", "name": "Lose Yourself"}, {"artist": "Kingdom of Giants", "name": "Burner"}, {"artist": "Palisades", "name": "War"}]$
  • loads - декодирует json строку

music_json = json.dumps(music)
print(type(music_json))
pprint(music_json)

music2 = json.loads(music_json)
print(type(music2))
pprint(music2)

$ python3 ./test.py 
<class 'str'>
('[{"name": "Room to Breathe", "artist": "You Me At Six"}, {"name": "Lose '
 'Yourself", "artist": "Saliva"}, {"name": "Burner", "artist": "Kingdom of '
 'Giants"}, {"name": "War", "artist": "Palisades"}]')
<class 'list'>
[{'artist': 'You Me At Six', 'name': 'Room to Breathe'},
 {'artist': 'Saliva', 'name': 'Lose Yourself'},
 {'artist': 'Kingdom of Giants', 'name': 'Burner'},
 {'artist': 'Palisades', 'name': 'War'}]

Не все данные удастся сериализовать. datetime например ломается, нужно приводить к строке.


import datetime
import json

now = datetime.datetime.now()
print( now )

datetime_str = json.dumps( now )
print("datetime_str =", datetime_str)

$ python3 ./test.py 
2021-02-06 23:36:06.805184
Traceback (most recent call last):
  File "./test.py", line 9, in <module>
TypeError: datetime.datetime(2021, 2, 6, 23, 36, 6, 805184) is not JSON serializable

import datetime
import json

now = datetime.datetime.now()
print( now )

datetime_str = json.dumps( str(now) )
print("datetime_str =", datetime_str)

$ python3 ./test.py 
2021-02-06 23:34:23.983503
datetime_str = "2021-02-06 23:34:23.983503"

YAML

  • dump - выгружает в yaml формате

import yaml

file_name = './last_music.yaml'

music = [
    {
        'artist': 'You Me At Six',
        'name': 'Room to Breathe'
    },
    {
        'artist': 'Saliva', 'name':
        'Lose Yourself'
    },
    {
        'artist': 'Kingdom of Giants',
        'name': 'Burner'
    },
    {
        'artist': 'Palisades',
        'name': 'War'
    }
]

with open(file_name, 'wt') as file:
    data = yaml.dump(music)
    file.write(data)

$ python3 ./test.py && cat last_music.yaml 
- artist: You Me At Six
  name: Room to Breathe
- artist: Saliva
  name: Lose Yourself
- artist: Kingdom of Giants
  name: Burner
- artist: Palisades
  name: War
$
  • safe_load - чтение из yaml файла

import yaml
from pprint import pprint

file_name = './last_music.yaml'

with open(file_name, 'rt') as file:
    music = yaml.safe_load(file)
    print(type(music))
    pprint(music)

$ python3 ./test.py 
<class 'list'>
[{'artist': 'You Me At Six', 'name': 'Room to Breathe'},
 {'artist': 'Saliva', 'name': 'Lose Yourself'},
 {'artist': 'Kingdom of Giants', 'name': 'Burner'},
 {'artist': 'Palisades', 'name': 'War'}]

Конфиг файлы .cfg


import configparser

cfg = configparser.ConfigParser()
cfg.read('settings.cfg')
print(cfg)
print(cfg['settings'])
print(cfg['settings']['test'])

$ python3 ./test.py && cat ./settings.cfg 
<configparser.ConfigParser object at 0x7f4d32eaaa90>
<Section: settings>
file

[settings]
test = file

Сериализация с помощью pickle


import pickle
import datetime
from pprint import pprint

music = [
    {
        'artist': 'You Me At Six',
        'name': 'Room to Breathe'
    },
    {
        'artist': 'Saliva', 'name':
        'Lose Yourself'
    },
    {
        'artist': 'Kingdom of Giants',
        'name': 'Burner'
    },
    {
        'artist': 'Palisades',
        'name': 'War'
    }
]

music[0]['time'] = str(datetime.datetime.now())

def write_binary_data():
    with open('binary_music.bin', 'wb') as file:
        pickled = pickle.dumps(music)
        file.write(pickled)

def read_binary_data():
    with open('binary_music.bin', 'rb') as file:
        data = file.read()
        music = pickle.loads(data)
        pprint(music)

write_binary_data()
read_binary_data()

$ python3 ./test.py 
[{'artist': 'You Me At Six',
  'name': 'Room to Breathe',
  'time': '2021-02-07 13:08:56.532117'},
 {'artist': 'Saliva', 'name': 'Lose Yourself'},
 {'artist': 'Kingdom of Giants', 'name': 'Burner'},
 {'artist': 'Palisades', 'name': 'War'}]
$
  • Можно так же преобразовать объекты

import pickle

class Tiny():
    def __str__(self):
        return 'tiny'

file_name = 'binary_object.bin'

def write_binary_data():
    with open(file_name, 'wb') as file:
        tiny = Tiny()
        print(tiny)
        pickled = pickle.dumps(tiny)
        file.write(pickled)

def read_binary_data():
    with open(file_name, 'rb') as file:
        data = file.read()
        obj = pickle.loads(data)
        print(obj)

write_binary_data()
read_binary_data()

$ python3 ./test.py 
tiny
tiny

$ cat binary_object.bin 
�c__main__
Tiny
q)�q.$ 
$

Работа с БД

DB-API

SQLite


import sqlite3

conn = sqlite3.connect('test.db')
curs = conn.cursor()

curs.execute('''
    CREATE TABLE zoo (
        name VARCHAR(20) PRIMARY KEY,
        count INT,
        cost FLOAT
    )
'''
)

insert = 'INSERT INTO zoo (name, count, cost) VALUES (?, ?, ?)'
res = curs.execute(insert, ('медведь', 1, 1000.1))

curs.execute('SELECT * FROM zoo')
rows = curs.fetchall()
print(rows)

curs.close()
conn.commit()
conn.close()

$ python3 ./test.py 
[('медведь', 1, 1000.1)]

MySQL



PostgreSQL

  • Для начала нужно установить драйвер -

sudo apt-get install postgresql python-psycopg2 libpq-dev
pip3 install psycopg2
  • Саму БД можно поднять в докере

PG_VER=11
sudo mkdir -p /var/tmp/pg_data/local/${PG_VER};
docker run --rm -d --name postgres -e POSTGRES_PASSWORD=1234 --net=host -v /var/tmp/pg_data/local/${PG_VER}:/var/lib/postgresql/data/ postgres:${PG_VER}
sleep 3;
psql -p 5432 -U postgres -h localhost -c 'create database local_db'

import psycopg2

conn = psycopg2.connect(
    host = 'localhost',
    port = 5432,
    user = 'postgres',
    password = 1234,
    database = 'local_db'
)

curs = conn.cursor()

curs.execute('''
    CREATE TABLE public.zoo (
        name VARCHAR(20) PRIMARY KEY,
        count INT,
        cost FLOAT
    )
''')

insert = "INSERT INTO zoo (name, count, cost) VALUES (%(name)s, %(count)s, %(cost)s)"
res = curs.execute(insert, {'name': 'медведь', 'count': 1, 'cost': 1000.1})

curs.execute('SELECT * FROM zoo')
rows = curs.fetchall()
print(rows)

curs.close()
conn.commit()
conn.close()

$ python3 ./test.py 
[('медведь', 1, 1000.1)]
$

Как и с sqlite если в конце не сделать commit, то ничего не сохраниться

  • По сравнению с sqlite, формат curs.execute(insert) принимает шаблон (специфичный) или готовую строку.
    • Можно как обычное процентное форматирование
    
      "INSERT INTO zoo (name, count, cost) VALUES (%s, %s, %s)"
      
    • Так и передать словарь
    
      "INSERT INTO zoo (name, count, cost) VALUES (%(name)s, %(count)s, %(cost)s)"
      

Как оно вообще заработало в sqlite?

SQLAlchemy


pip3 install sqlalchemy
  • Строка подключения к БД

dialect + driver :// user : password @ host : port / dbname
  • dialect - тип БД
  • driver - драйвер
  • user и password - параметры подключения
  • host и port - расположения сервера БД
  • dbname - имя БД

Уровень движка (чуть лучше DB-API)


import sqlalchemy as sa

conn = sa.create_engine('postgresql+psycopg2://postgres:1234@localhost:5432/local_db')

result = conn.execute('''
    CREATE TABLE IF NOT EXISTS public.zoo (
        name VARCHAR(20) PRIMARY KEY,
        count INT,
        cost FLOAT
    )
''')

print(result)

insert = "INSERT INTO zoo (name, count, cost) VALUES (%s, %s, %s)"

conn.execute(insert, ('медведь', 1, 1000.0))
conn.execute(insert, ('утка', 4, 400.0))
conn.execute(insert, ('ласка', 2, 400.0))

rows = conn.execute('SELECT * FROM zoo')
print(rows)

for row in rows:
    print(row)

$ python3 ./test.py 
<sqlalchemy.engine.result.ResultProxy object at 0x7f92af7aa080>
<sqlalchemy.engine.result.ResultProxy object at 0x7f92af7aa390>
('медведь', 1, 1000.0)
('утка', 4, 400.0)
('ласка', 2, 400.0)

После выполнения SELECT, вернется объект ResultProxy по которому можно итерироваться

Язык выражений SQL (level up)


import sqlalchemy as sa

conn = sa.create_engine('postgresql+psycopg2://postgres:1234@localhost:5432/local_db')

meta = sa.MetaData()
print(meta)

zoo = sa.Table('zoo', meta,
    sa.Column('name',  sa.String, primary_key = True),
    sa.Column('count', sa.Integer),
    sa.Column('cost',  sa.Float)
)

meta.create_all(conn)

# Нужно больше скобок, богу скобок
conn.execute( zoo.insert( ('медведь', 1, 1000.0) ) )
conn.execute( zoo.insert( ('утка', 4, 400.0) ) )
conn.execute( zoo.insert( ('ласка', 2, 400.0) ) )

# По факту превращается в 
# [SQL: INSERT INTO zoo (name, count, cost) VALUES (%(name)s, %(count)s, %(cost)s)]
# [parameters: {'count': 1, 'name': 'медведь', 'cost': 1000.0}]

result = conn.execute( zoo.select() )
rows = result.fetchall()
print(rows)

$ python3 ./test.py 
MetaData(bind=None)
[('медведь', 1, 1000.0), ('утка', 4, 400.0), ('ласка', 2, 400.0)]
$

The Object-Relational Mapper (ORM)


import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

conn = sa.create_engine('postgresql+psycopg2://postgres:1234@localhost:5432/local_db')

# DDL

Base = declarative_base()
class Zoo(Base):
    __tablename__ = 'zoo'
    name  = sa.Column('name',  sa.String, primary_key = True)
    count = sa.Column('count', sa.Integer)
    cost  = sa.Column('cost',  sa.Float)

    def __init__(self, name, count, cost):
        self.name  = name
        self.count = count
        self.cost  = cost

    def __repr__(self):
        return '<Zoo({}, {}, {})>'.format(self.name, self.count, self.cost)

Base.metadata.create_all(conn)

# DML

one = Zoo('медведь', 1, 1000.0)
two = Zoo('утка', 4, 400.0)
three = Zoo('ласка', 2, 400.0)

print(one)

Session = sessionmaker(bind = conn)
session = Session()

session.add(one)
session.add_all([two, three])

session.commit()

$ python3 ./test.py 
<Zoo(медведь, 1, 1000.0)>

name   | count | cost 
---------+-------+------
 медведь |     1 | 1000
 утка    |     4 |  400
 ласка   |     2 |  400
(3 rows)

NoSQL - не только SQL

dbm

  • dbm - хранилка ключ/значение на диске. В принципе удобно не надо ничего подымать

import dbm

db = dbm.open('local_file.db', 'c')

db['test1'] = 'hello'
db['test2'] = 'world'

# Сколько эл. в хранилище
print( len(db) )
# 2

# Значение эл. test1
print( type(db['test1']) )
print( db['test1'] )
# <class 'bytes'>
# b'hello'

db.close()

db = dbm.open('local_file.db', 'c')

print( db['test2'] )
# b'world'

$ python3 ./test.py 
2
<class 'bytes'>
b'hello'
b'world'

Memcached

doc - https://github.com/eguven/python3-memcached

  • сервер кеширования строк. Основное плюшки
  • Уст и получать значения
  • Увел и умень значения

docker run --rm -d --name memcached --net=host memcached

pip3 install python-memcached

Проверить можно через telnet, но как-то сложно…


~$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
get foo  
END

import memcache

db = memcache.Client(['localhost:11211'])

db.set('foo', 'bar')
print( db.get('foo') )
# bar

db.set('test1', 0)
print( db.get('test1') )
# 0

db.incr('test1')
print( db.get('test1') )
# 1

$ python3 ./test.py 
bar
0

Redis

  • redis - сервер структуры данных. Основные плюшки
  • сохраняет данные на диск в случае перезагрузки
  • больше ассортимент по структурам данных

docker run --rm -d --name redis --net=host redis
redis-cli -h localhost -p 6379

pip3 install redis

Строки


import redis

redis = redis.Redis()
# или redis = redis.Redis('localhost')
# или redis = redis.Redis('localhost', 6379)

# Записать и получить значение
redis.set('test1', 'hello')

print( type( redis.get('test1') ) )
print( redis.get('test1') )
# <class 'bytes'>
# b'hello'

# setnx - Записать если ключа нет
redis.setnx('test2', 'world')

print( redis.get('test2') )
# b'world'

redis.setnx('test2', 'alarm')
print( redis.get('test2') )
# b'world'

# getset - уст новое значение и вернуть старое
print( redis.getset('test2', 'alarm') )
print( redis.get('test2') )
# b'world'
# b'alarm'

# getrange - вернуть часть строки
print( redis.getrange('test2', -3, -1) )
# b'arm'

# Если ключа не будет - то вернется пустая строка
print( redis.getrange('test3', 0, 0) )
# b''

# setrange - заменить часть строки
redis.setrange('test2', 0, '12')
print( redis.get('test2') )
# b'12arm'

# Если ключа не будет - то исключение если выход за диапазон
# print( redis.setrange('test3', -1, '12') )
# print( redis.get('test3') )

# mset - уст значения сразу нескольким ключам
redis.mset({'test4': 4, 'test5': 5})

# mget - получить значения по нескольким ключам
print( redis.mget(['test4', 'test5']) )
# [b'4', b'5']

# delete - удалить ключ
redis.delete('test4')
print( redis.get('test4') )
# None

# incr - инкремент
print( redis.incr('test4') )
# 1

# decr - декремент
print( redis.decr('test4') )
# 0

<class 'bytes'>
b'hello'
b'12arm'
b'12arm'
b'12arm'
b'alarm'
b'arm'
b''
b'12arm'
[b'4', b'5']
None
1
0

Списки


#!/usr/bin/env python3

import redis

redis = redis.Redis()
# или redis = redis.Redis('localhost')
# или redis = redis.Redis('localhost', 6379)

# lpush - добавить в начало списка 
redis.lpush('zoo', 'медведь')

# lindex - получить эл. по смещению
print( str( redis.lindex('zoo', 0).decode('utf8') ) )
# медведь

# linsert - добавить эл. относительно смещения (before/after)
redis.linsert('zoo', 'before', 'медведь', 'лиса')
print( str( redis.lindex('zoo', 0).decode('utf8') ) )
# лиса

redis.linsert('zoo', 'after', 'лиса', 'утка')
print( str( redis.lindex('zoo', 1).decode('utf8') ) )
# утка

# lset - добавить в индекс 
redis.lset('zoo', 2, 'крокодил')
print( str( redis.lindex('zoo', 2).decode('utf8') ) )
# крокодил

# rpush - добавить в конец списка
redis.rpush('zoo', 'слон')

# lrange - получить список в диапазоне
print('\nlrange:');
print( [i.decode('utf8') for i in redis.lrange('zoo', 0, 3) ] )

# ltrim - обрезать список по диапазону
print('\nltrim:');
print( redis.ltrim('zoo', 1, 3) )
print( [i.decode('utf8') for i in redis.lrange('zoo', 0, 3) ] )

redis.flushall()

$ python3 ./test.py 
медведь
лиса
утка
крокодил

lrange:
['лиса', 'утка', 'крокодил', 'слон']

ltrim:
True
['утка', 'крокодил', 'слон']

Хеши


#!/usr/bin/env python3

import redis

redis = redis.Redis()
# или redis = redis.Redis('localhost')
# или redis = redis.Redis('localhost', 6379)

music = { 'artist': 'You Me At Six', 'name': 'Room to Breathe' }

# hmset - записать хеш
print("\nhmset:")
print( redis.hmset('music', music) )

# hmget - получить несколько значений по ключам
print("\nhmget:")
print( redis.hmget('music', 'artist', 'name') )

# hset - записать новое значение ключа хеша
print("\nhset:")
print( redis.hset('music', 'test1', 1) )

# hget - получить значение ключа
print("\nhget:")
print( redis.hget('music', 'test1') )

# hkeys - получить все ключи хеша
print("\nhkeys:")
print( redis.hkeys('music') )

# hvals - получить все значения хеша
print("\nhvals:")
print( redis.hvals('music') )

# hgetall - получить все ключи и значения хеша
print("\nhgetall:")
print( redis.hgetall('music') )

# hlen - получить кол-во ключей
print("\nhlen:")
print( redis.hlen('music') )

# hsetnx - записать новый эл. если нет такого ключа
print("\nhsetnx:")
print( redis.hsetnx('music', 'test2', 2) )
print( redis.hsetnx('music', 'test2', 2) )

redis.flushall()

$ python3 ./test.py 

hmset:
True

hmget:
[b'You Me At Six', b'Room to Breathe']

hset:
1

hget:
b'1'

hkeys:
[b'artist', b'name', b'test1']

hvals:
[b'You Me At Six', b'Room to Breathe', b'1']

hgetall:
{b'test1': b'1', b'artist': b'You Me At Six', b'name': b'Room to Breathe'}

hlen:
3

hsetnx:
1
0

Множества

#TODO стр. 250

Упорядоченные множества

#TODO стр. 251

Биты

#TODO стр. 253

Full-Text Databases

#TODO стр. 255

  • Lucene - pylucene
  • Solr - SolPython
  • ElasticSearch - pyes
  • Sphinx - sphinxapi
  • Xapian - xappy
  • Whoosh - Написан на Python, уже содержит API