Size: a a a

PyWay – гуру Python 🐉

2019 December 16
PyWay – гуру Python 🐉
Однажды, я уже рассказывал вам, как использовать встроенную сортировку в Python. Убежден, что для разработчиков уровня middle и выше важно также знать, как она работает внутри. Ответ нашелся на канале Python in depth. Очень познавательно и наглядно.
Telegram
Python in depth
Timsort

Для сортировки в CPython используется алгоритм Timsort, который его создатель Тим Петерс застенчиво назвал в честь себя😏. Timsort — это комбинация сортировки вставками и сортировки слиянием, заточенная под работу с реальными данными. Дело в том, что на практике массивы, которые нужно сортировать, часто бывают частично упорядочены и Timsort пользуется этим предположением для ускорения работы.

Логика работы Timsort на самом деле довольно прозрачная:
🐠 Timsort делит входной массив на подмассивы;
🐠 Сортирует подмассивы вставками;
🐠 Объединяет их попарно сортировкой слиянием;
🐠 И возвращает отсортированный массив.

Но дьявол кроется в деталях.

Делим на подмассивы

Части, на которые алгоритм делит входной массив, в описании алгоритма автор называет run'ами. Так на какое количество run'ов следует разделить входной массив? Здесь два соображения. Во-первых, части должны быть достаточно короткими, чтобы их было выгодно сортировать вставками. А еще мы хотим, чтобы пары последовательностей, которые позже поступят…
источник
2019 December 20
PyWay – гуру Python 🐉
"Сломанный" set

Вопрос: может ли set содержать два одинаковых объекта?

Ответ: да, запросто! Делаем класс:

class Foo:
   def __init__(self, x):
       self.x = x
   def __hash__(self):
       return self.x
   def __eq__(self, other):
       return self.x == other.x
   def __repr__(self):
       return f'Foo({self.x})'

# создаем set из трех разных объектов
hacker = Foo(20)
s = {Foo(10), hacker, Foo(30)}

print(s)  # {Foo(10), Foo(20), Foo(30)}

hacker.x = 30  # взлом системы
print(s)  # {Foo(10), Foo(30), Foo(30)}

from collections import Counter
c = Counter(s)
print(c)  # Counter({Foo(30): 2, Foo(10): 1})


Как это? set запоминает хэш объекта при вставке, а потом не следит за тем, меняется ли как-то объект или нет, это было бы очень накладно. Изначально мы вставляли 20, но потом уже поменяли его на 30, тем самым сломав set.

"Починить" такой set можно, сделав из него список, а потом новый set, тогда хэши будут заново пересчитаны. Лучше до такого не доводить!

s2 = set(list(s))
print(s2)  # {Foo(10), Foo(30)}


Примечание: а метод s.copy() не сработает, потому что он копирует уже вычисленные хэши.

Мораль: если вы помещаете свои объекты в set, вы должны самостоятельно обеспечить их логическую иммутабельность. Иными словами обеспечить неизменяемость именно тех атрибутов, которые участвуют в сравнении и хэшировании: не менять их самому и сокрыть от внешних изменений. Те же правила относятся к объектам, которые вы хотите сделать ключами словаря dict.
источник
2019 December 21
PyWay – гуру Python 🐉
Друзья, если вы любите котиков, как любим их мы, то, возможно, вам стоит оценить наш второй канал "Котодела". Он появился примерно в одно время с PyWay, а ведет его моя жена Катя. Она ежедневно публикует милых и забавных котов и кошек для вашего хорошего настроения! До Нового года осталось 10 дней и очень бы хотелось сделать Кате подарок, доведя число подписчиков до тысячи 😺
источник
2019 December 23
PyWay – гуру Python 🐉
О точности float

Вбейте в интерпретаторе Python:

>>> 0.1 + 0.1 + 0.1 == 0.3
False

Здравый смысл говорит, что равенство должно иметь место. Однако, Python с этим не согласен. Но откуда берутся ошибки подобного рода?

Числа типа float в Python хранятся в стандартном для современных компьютеров формате с плавающей точкой двойной точности (см. IEEE-754). Такие числа имеют 64 бита, в которые упакованы, как пара целых чисел: мантисса (53 бита) и экспонента (11 бит). Реальное значение вычисляется по формуле:

Число = ±мантисса * (2 ** экспонента)

Думаете, любое реальное число можно представить, используя эти 64 бита? Конечно, нет. Простая комбинаторика скажет, что у нас может быть не более 2**64 разных чисел (64 позиции по 2 варианта), а на деле их и того меньше. Диапазон чисел, представимых таким форматом составляет: ±1.710E-308 до 1.710E+308. То есть от очень малых по модулю чисел, до очень больших. Допустимые числа на числовой прямой распределены неравномерно: гуще в районе нуля и реже в районе огромных чисел.

Оказывается, что числа 0.1 – нет! Действительно, нет способа представить это немудреное число в формате с плавающей точкой с основанием 2! 0.1 – это просто текст, для которого Python должен подобрать максимально близкое представление в памяти компьютера. Можно найти число поменьше или побольше, но точно 0.1 – не получится. Давайте проверим. Задача – подобрать числа J и N:

0.1 = 1 / 10 ≈ J / (2**N) или J ≈ 2**N / 10

При этом в J должно быть ровно 53 бита. Наиболее подходящие N для такого случая равняется 56.

>>> 2**52 <= 2**56 // 10 < 2**53
True

>>> divmod(2**56, 10)
(7205759403792793, 6)

Остаток от деления – 6 чуть больше половины делителя (10), поэтому наилучшее приближение будет, если мы округлим частное вверх, то есть добавим к 7205759403792793 + 1 = 7205759403792794. Таким образом, это будет ближайшее к 0.1 число, возможное в представлении float. Доказательство проверкой:

>>> 7205759403792794 / 2 ** 56 == 0.1
True

Теперь понятно, что 0.1 и 0.3 аппроксимируются к каким-то ближайшим представлениям в экспоненциальной форме в памяти компьютера. Поэтому и возникает эта крошечная разница:

>>> format(0.1 + 0.1 + 0.1 - 0.3, '.56f')
'0.00000000000000005551115123125782702118158340454101562500'


Вывод: не рекомендуется никогда сравнивать float между собой на точное равенство!

Обязательно изучите новую статью, там больше подробностей и иллюстраций по теме.
источник
2019 December 26
PyWay – гуру Python 🐉
​​Python в быту или кладем плитку в ванной

Друзья, с октября я поставил себе цель: каждый день вносить какой-то вклад в свои репозитории на GitHub, чтобы полоса активности была замощена красивыми зелеными плиточками. Увы, я признаю, что в силу разных причин я пропустил пару дней, что привело к появлению пары серых прогалов. Я решил исправить картину, написав скрипт, а заодно поделиться с вами знанием, как применять скрипты Python для упрощения подобных задач.

Итак, задача – совершить коммиты в прошлом. Чтобы установить дату коммита, нужно задать две переменные окружения перед вызовом git commit:

GIT_AUTHOR_DATE='новая-дата' GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git commit -m 'сообщение'

Команда довольно громоздкая, а дату нужно вводить в неудобном формате ISO или пользоваться программой date. Выяснилось, что date имеет совершенно разный синтаксис на Linux и macOS, поэтому я решил все это дело упростить скриптом на Python.

Из параметров командной строки я беру количество дней "назад" (оно может быть дробным) и сообщение коммита. Затем получаю дату в прошлом и устанавливаю переменные окружения, после выполняю команду:

date = datetime.now() - timedelta(days=days)
os.environ['GIT_AUTHOR_DATE'] = os.environ['GIT_COMMITTER_DATE'] = date.isoformat()
r = os.system(f'git commit -m "{message}"')
exit(r)


Полный код скрипта тут. Сделаем из скрипта удобную утилитку для nix систем.

1. Сохраним код в файл commit-ago (без расширения) и добавим в начале шебанг, иными словами заголовок, указывающий интерпретатор для скрипта.

#!/usr/local/bin/python3

Если вы не знаете, где ваш Python, можно узнать командой which python3

2. Сделаем скрипт исполняемым

chmod +x commit-ago

3. Поместим его в какую-нибудь директорию, которая есть в переменной PATH, чтобы можно было запускать скрипт без указания полного адреса. Неплохое место:

mv commit-ago /usr/local/bin

Можно пользоваться. Заходим в какой-либо репозиторий, делаем изменения, добавляем файлы в индекс и вызываем команду commit-ago.

touch foo.txt
git add foo.txt
commit-ago 1 "I did it yesterday"


Здесь, первый параметр – на сколько дней назад отмотать время: число 1 значит "вчера", 2 значило бы "позавчера", а 7 – "неделю назад". Второй параметр – комментарий к коммиту.
источник
2019 December 31
PyWay – гуру Python 🐉
​​Дорогие друзья!

🎄 Поздравляю вас всех с наступающим Новым Годом 2020! 🎄

Желаю вам счастья, здоровья, хорошего настроения! Поменьше багов в коде и побольше интересных проектов! А также любви, путешествий, достижений, новых открытий и позитивных эмоций!

Я искренне рад и благодарен, что вы читаете наш канал. В новом году я буду радовать вас множеством свежих интересных статей! 🎇

🎅 А пока вот небольшой праздничный сюрприз: написал программу, которая приклеивает шапку и бороду Деда Мороза на лица из видео-файла или с камеры. Используются библиотеки OpenCV и face-recognition. Код доступен на GitHub. Буду рад вашим звездочкам 🌟

Спасибо за поддержку! 🥂
источник
2020 January 02
PyWay – гуру Python 🐉
​​Всех уже с наступившим 2020! #Новость в том, что 2020 будет последним годом Python 2. Вообще, планировалось прекратить его поддержку еще в 2015, потом решено было продлить до 1 января 2020, чтобы дать время разработчикам на миграцию кода. А теперь у нас есть официальная новость, что последняя версия Python 2.7 выйдет в апреле 2020, после чего работа над ним будет прекращена. Спасибо, Python 2.7, ты послужил верой и правдой, но пора двигаться дальше. Надеюсь, вы начинаете свои новые Python-проекты только на 3 версии. Все мои посты именно про Python 3.X.
источник
2020 January 07
PyWay – гуру Python 🐉
Decimal против float

Недавно мы рассмотрели внутреннее устройство float и его подводные камни. В области финансов и бизнеса бывает недопустимо совершать ошибки в расчетах. Компьютер должен помогать человеку считать, и он должен делать это точно и по правилам, которые привычны и интуитивны для нас. Короче говоря, как учат в школе. А учат нас десятичным дробям, то есть тем, представление которых записывается через степени числа 10. А float построен вокруг степеней числа 2, что удобнее и проще для двоичных компьютеров, но контр-интуитивно для человека:

>>> 0.1 + 0.1 + 0.1 == 0.3
False

>>> from decimal import Decimal as d
>>> d('0.1') + d('0.1') + d('0.1') == d('0.3')
True


В новой заметке я более подробно разбираю:

➡ Как же устроен и работает Decimal?
➡ Плюсы и минусы в сравнении с float?
➡ Как пользоваться Decimal?
➡ И почему писать числа в строках – это нормально?

Нажимай на кнопку, и статья откроется прямо в Телеграме!
источник
2020 January 09
PyWay – гуру Python 🐉
Вопрос, чтобы я понимал, стоит ли адаптировать контент под определенные системы.
Какой ОС вы в основном пользуетесь для разработки на Python?
Анонимный опрос
52%
Windows
13%
macOS
33%
Linux
2%
Другая
Проголосовало: 310
источник
2020 January 10
PyWay – гуру Python 🐉
Недавно узнал, в цикле for/in после for может стоять не только имя переменной, но и любое выражение, которому можно присвоить что-либо: элемент списка/словаря или атрибут класса.

Пример – таблица чисел Фибоначчи, позволняет узнать по числу Фибоначчи его номер:

fib = {}

a, b = 1, 1
for fib[a] in range(1, 100):
   a, b = a + b, a

print(fib[13])  # это №6 число Фиб.
print(fib[433494437])  # это №42 число Фиб.
print(fib[10])  # KeyError - 10 - это не число Фиб.


На каждой итерации fib[a] будет присвоено число из range(1, 100). Но на каждой итерации будет свое a – новое число Фибоначчи:

fib[1] = 1
fib[2] = 1 + 1 = 2
fib[3] = 2 + 1 = 3


По мне, это не то, чтобы супер-полезная возможность, потому что мало людей, кто этим пользуется, и такой код сложновато осознать без привычки. А вы знали?
источник
2020 January 13
PyWay – гуру Python 🐉
Деструктор __del__
(часто спрашивают на собеседованиях)

Этот магический метод позволяет определить поведение экземпляра пользовательского типа при готовности интерпретатора уничтожить его.

Многие думают, что del x вызывает метод x.__del__. Это неправда. Python использует механизм подсчета ссылок, и del x – лишь один из способов уменьшить количество ссылок на 1.

📎 Примеры. Определим такой класс, где будем следить за вызовом его метода __del__:

class Bazooka:
   def __del__(self):
       print('Bazooka.__del__()')

>>> b = Bazooka()   # тут мы привяжем новый объект Bazooka() к имени переменной b (1 ссылка)
>>> del b           # тут мы удаляем имя b, объект отвязывется, ссылок - 0, вызывается деструктор.
Bazooka.__del__()

>>> b = Bazooka()
>>> b = None        # имя b не удалено, но объект будет отвязан от него и деструктор вызовется
Bazooka.__del__()

>>> b = Bazooka()
>>> b2 = b          # объект привязан и к b, и к b2 (ссылок - 2)
>>> del b           # удаляем первый, ссылок - 1, деструктор не вызван
>>> del b2          # удаляем второй, ссылок - 0, деструктор вызывается
Bazooka.__del__()

Важные нюансы:

❗️ Не гарантируется, что метод будет вызван для объектов, всё ещё существующих на момент выхода интерпретатора.

❗️ Если метод определён в базовом классе, то потомок должен вызывать его явно, чтобы удаление части экземпляра, реализуемой базовым классом произошло без ошибок.

❗️ Ссылка на объект может остаться при броске исключения в sys.exc_traceback (исправляется путем sys.last_traceback = None)

❗️ До Python 3.4 сборщик мусора не мог разорвать циклические ссылки с объектами, у которых определен __del__.

❗️ Не стоит путать __del__ и __delete__, __delete__ используется напрямую крайне редко, и нужен он для объектов-дескрипторов.
источник
2020 January 14
PyWay – гуру Python 🐉
Если нужно формировать пары из двух списков (или итераторов), пригодится встроенная функция zip:

>>> x = [1, 2, 3, 4, 5]
>>> y = ['one', 'two', 'three']
>>> list(zip(x, y))
[(1, 'one'), (2, 'two'), (3, 'three')]


Из примера видно, что zip останавливается, как только в одном списков закончились значения. А в списке x остались бесхозные числа 4 и 5.

Если нужно брать по максимуму до конца самого длинного из списков, то пригодится функция zip_longest из стандартного модуля itertools. Если в одном из списков кончились значения, то zip_longest будет ставить на их место None, и закончит работу только тогда, когда каждый из списков будет исчерпан.

>>> import itertools
>>> list(itertools.zip_longest(x, y))
[(1, 'one'), (2, 'two'), (3, 'three'), (4, None), (5, None)]

Кстати, функции zip и zip_longest могут принимать не только два аргумента, а произвольное число входных аргументов, т.е. можно делать из трех списков тройки, из четырех – четверки и так далее.

Еще обе функции возвращают итератор, поэтому для наглядности я применяю функцию list, чтобы извлечь все значения итератора в список. Конечно, можно извлекать пары по одной в ленивом режиме функцией next:

>>> it = itertools.zip_longest(x, y)
>>> next(it)
(1, 'one')
источник
2020 January 19
PyWay – гуру Python 🐉
Final

В современных ООП языках есть способы, как запретить создание подклассов какого-то класса. Например, в Java и Swift для этого есть ключевое слово final.

Мы можем добавить схожее поведение и в Python с помощью метаклассов. Напомню, что метакласс – это класс класса, т.е. тип (класс) – это экземпляр метакласса. В момент объявления подкласса B для класса A, вызывается метод new метакласса A. В этом методе мы проверим, есть ли среди базовых классов (bases) хоть один, чьим метаклассом является Final, если да – бросим исключение, сигнализирующее, что нельзя создать подкласс. Если среди базовых классов нет таких, то вызовем стандартный super().__new__. Вот так выглядит код метакласса:

# метакласс наследуется от Type
class Final(type):
   def __new__(mcls, name, bases, attrs):
       for base in bases:
           # является ли тип класса подклассом Final
           if issubclass(type(base), Final):
               # тогда - ошибка! нельзя содзать подкласс Final
               raise TypeError(f'{name} is final!')
     
       # иначе - обычное поведение
       return super().__new__(mcls, name, bases, attrs)

Как пользоваться:

# тут отработает Final.__new__ для A - успешно
class FinalA(metaclass=Final):
   ...

# тут отработает Final.__new__ для B - с ошибкой!
class B(FinalA):
   ...


Можно оформить в виде декоратора класса, чтобы писать так:

@final
class A:
   ...

Справедливости ради, хочу сказать, что в Python 3.8 появился декоратор final! Но, он относится только к подсказкам типов (type hinting), и объявление подкласса от final класса не даст никаких ошибок!

from typing import final

@final
class A:
   ...

# никаких ошибок
class B(A):
   ...


А вот зато утилита mypy, которая проверяет типы, выдаст ошибку:

➜  2 mypy final38.py
final38.py:8: error: Cannot inherit from final class "A"
Found 1 error in 1 file (checked 1 source file)
источник
2020 January 21
PyWay – гуру Python 🐉
​​#Задача#Задача

Новая рубрика – задачка на программирование. Сегодня для разгона – простая. Напишите функцию comprime(*numbers), которая принимает произвольное количество аргументов – натуральных чисел, и проверяет, являются ли все эти числа взаимно простыми, то есть не имеют общих делителей, кроме 1.

>>> coprime(13, 20)
True
>>> coprime(13, 26)
False
>>> coprime(2, 3, 5, 7, 11)
True


Присылайте свои варианты решения в наш чатик!
источник
2020 January 23
PyWay – гуру Python 🐉
​​Расскажите про любой шаблон проектирования на ваш выбор.

Случалось слышать такое на собеседованиях? Большинство людей в этот момент начинают рассказывать про синглтон (одиночку). Потому что он... простой? Да, вообще-то не очень. Попробуйте сходу вспомнить, как там реализовать его через метакласс. Да и часто ли приходится? Скорее всего вы пользуетесь уже готовым кодом для синглтона. Его даже называют "анти-паттерном", потому что он часто маскирует плохой дизайн кода, вызывает проблемы при тестировании и нарушает принцип единственной отвественности класса (и порождает себя, и делает какую-то работу). А еще, он может вызывать проблемы с многопоточностью или "многопроцессностью" в случае с Python. Поэтому хвастать знанием синглотона – не лучшая стратегия на собеседовании...

Ага! Стратегия! Это именно тот шаблон, который действительно подойдет для рассказа, потому что он простой и реально часто применяется на практике, даже если вы порой сами это не осознаете.

Стратегия – поведенческий шаблон, призванный для обеспечения взаимозаменяемости разных алгоритмов или вариаций алгоритма с одинаковыми интерфейсами. Стратегии – и есть эти варианты. В зависимости от условий (контекст) код выбирает подходящий алгоритм.

Реализация этого шаблона может быть не только объектная, но и функциональная. С нее и начнем:

# стратегия печатать на экран
def console_writer(info):
   print(info)

# стратегия выводить в файл
def file_writer(info):
   with open('log.txt', 'a') as file:
       file.write(info + '\n')

def client(writer):
   writer('Hello world!')
   writer('Good bye!')

# пользователь выбирает стратегию
if input('Write to file? [Y/N]') == 'Y':
   client(writer=file_writer)
else:
   client(writer=console_writer)


Стратегия выбирается пользователем, а функция client даже не знает, какой вариант алгоритма ей дадут. Она знает лишь то, что writer(info) – это некая функция, принимающая строку (это и есть общий интерфейс для всех стратегий). Таким образом, мы делегируем работу стратегиям, скрывая детали реализации каждой из них.

В объектном варианте:

class Adder:
   def do_work(self, x, y):
       return x + y

class Multiplicator:
   def do_work(self, x, y):
       return x * y

class Calculator:
   def set_strategy(self, strategy):
       self.strategy = strategy

   def calculate(self, x, y):
       print('Result is', self.strategy.do_work(x, y))

calc = Calculator()
calc.set_strategy(Adder())
calc.calculate(10, 20)

calc.set_strategy(Multiplicator())
calc.calculate(10, 20)


Мы обеспечили горячую заменя алгоритмов для класса Calculator. Для простоты, здесь я не применял наследование (спасибо динамической природе Python), но в серьезных проектах, вам следовало бы написать что-то подобное:

from abc import ABC, abstractmethod

class BaseStrategy(ABC):
   @abstractmethod
   def do_work(self, x, y):
       pass

class Adder(BaseStrategy):
   def do_work(self, x, y):
       return x + y

class Multiplicator(BaseStrategy):
   def do_work(self, x, y):
       return x * y

class Calculator:
   def set_strategy(self, strategy: BaseStrategy):
       self.strategy = strategy

   def calculate(self, x, y):
       print('Result is', self.strategy.do_work(x, y))


Здесь мы создаем общий интерфейс стратегий BaseStrategy – как абстрактный класс ABC. Далее в каждой стратегии реализуем этот интерфейс.

Надеюсь, было полезно. Если хотите еще больше подробностей, то читайте 1, 2, 3.
источник
2020 January 29
PyWay – гуру Python 🐉
Как отправить Bitcoin на Python?

1. Установим библиотеку bit: pip install bit

2. Загрузим приватный ключ из WIF:

from bit import Key
my_key = Key('L3Bc5K68nfDzaW4n4ag7eERKGw6WvsvTmqvirzn1wut7uL3sg76o')


3. Отправим монеты:

my_key.send([
   ('1HB5XMLmzFVj2ALj6mfBsbаfRoD4miY36v', 0.0024, 'btc'),
], message='За кофе')


Очень просто и удобно! Заинтересованы? Хотите узнать больше, о том, как работать с транзакциями Bitcoin на Python? Читайте мою новую заметку!
источник
2020 January 31
PyWay – гуру Python 🐉
Вы удивитесь, когда увидите эти расчеты! А еще вы немного научитесь работать с данными, делать простые предсказания и строить графики на Python.
источник
2020 February 03
PyWay – гуру Python 🐉
Лямбда или оператор?

Допустим у нас есть список напитков, где каждый элемент – кортеж (название, цена):

drinks = [
   ('Juice', 100),
   ('Beer', 200),
   ('Soda', 50),
   ('Cocktail', 400),
   ('Water', 20)
]

Хотим отсортировать их по цене, тогда нужно в функцию sorted передать параметр key – функцию, которая достанет из кортежа элемент с индексом 1 – цену. Можно поступить разными способами. Способ 1 – lambda:

>>> sorted(drinks, key=lambda d: d[1])
[('Water', 20), ('Soda', 50), ('Juice', 100), ('Beer', 200), ('Cocktail', 400)]


Способ 2 – operator.itemgetter:

>>> from operator import itemgetter
>>> sorted(drinks, key=itemgetter(1))
[('Water', 20), ('Soda', 50), ('Juice', 100), ('Beer', 200), ('Cocktail', 400)]


Если хотите побольше узнать, как пользоваться key, то приглашаю прочитать новую заметку. Еще там есть про сортировку по нескольким признакам и про сравнение производительности разных способов задания key.

Какой бы способ вы предпочли?
источник
2020 February 05
PyWay – гуру Python 🐉
При разборе вложенных структур из словарей и списков (например, конфигов), удобно пользоваться блоком try-except.

Ловим IndexError, если индекс отсутствует в списке, и KeyError, если ключ отсутствует в словаре. Однако, лучше ловить LookupError, который является предком обоих исключений:

>>> issubclass(KeyError, LookupError)
True
>>> issubclass(IndexError, LookupError)
True


Пример:

config = {}

try:
   admin = config['db'][0]['admins']['list'][0]
except LookupError:
   admin = 'all'


Альтернативно, вы можете сразу обновлять записи словаря (если они не найдены) методом dict.setdefault(key, default). Этот метод проверяет, есть ли ключ в словаре, если его нет, то в словарь добавляется значение по умолчанию, и оно же возвращается. А если ключ был в словаре, то вернется значение по этому ключу. Поэтому такой неуклюжий код:

if 'workers' not in config:
   config['workers'] = 8
workers = config['workers']


Может быть переписан как:

workers = config.setdefault('workers', 8)

Заметьте, что повторный вызов с другим default не поменяет уже записанное в первый раз значение:

>>> d = {}
>>> d.setdefault('foo', 10)
10
>>> d.setdefault('foo', 20)
10


Красивого всем кода!
источник
2020 February 06
PyWay – гуру Python 🐉
Помните, недавно рассказывал про библиотеку bit? Она славится быстрыми алгоритмами для генерации ключей. Мне пришла идея, что можно воспользоваться этим, чтобы нагенерить красивых Bitcoin адресов, начинающихся или заканчивающихся с заданного слова. Например такого:

1PyWaytDDQMSsEDSpBHgfPrEUJJg8NiC9w

Так как заранее мы не можем знать, какие символы в адресе, и где они будут, то приходится делать большой перебор и проверять каждый адрес:

PATTERN = 'PyWay'
def predicate(addr: str):
# первый символ – ID сети, пропустим его
   return addr[1:].startswith(PATTERN)


Вот код перебора:

from bit import Key
while True:
   k = Key()  #  новый случайны ключ
   if predicate(k.segwit_address):
       print(f'{k.segwit_address} with WIF private key {k.to_wif()}')
       break


Сразу предупреждаю, что слова длиннее 5 букв ищет очень долго (сам дождался только 5-символьного). Как только адрес найден, не забудьте сохранить приватный ключ, если хотите им пользоваться.

В заметке даю дополнительную информацию по теме и советы, как ускорить поиск.
источник