Size: a a a

PyWay – гуру Python 🐉

2020 May 08
PyWay – гуру Python 🐉
​​Что если нужно импортировать модули не из директории проекта и не те, что установлены через pip, а из произвольного места на диске?
Конечно, можно было бы скопировать код оттуда в своей проект, но так не рекомендуется делать. Есть и другие решения.

В модуле sys есть переменная path. Она содержит список путей, в которых Python ищет названия модулей для импорта. Пожалуйста, не путайте sys.path и переменную окружения PATH (которая, кстати, доступна через os.environ['PATH']). Это разные вещи, последняя не имеет отношения к поиску модулей Python.

>>> import sys
>>> sys.path
['', '/usr/local/Cellar/python@3.8/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python38.zip', ..., '/usr/local/lib/python3.8/site-packages']

Мы можем влиять на эту переменную, например, добавляя туда свои пути. Если добавить в начало списка, то поиск модулей начнется именно с нового пути.

import sys
sys.path.insert(0, '/Users/you/Projects/my_py_lib')
import my_module  # этот модуль лежит в my_py_lib


Порядок операторов здесь важен. Нельзя сделать сначала import, потому что на момент импорта my_module система еще не знает, где его можно найти.

import sys
import my_module  # ModuleNotFoundError
sys.path.insert(0, '/Users/you/Projects/my_py_lib')  # поздно

Функция site.addsitedir тоже модифицирует sys.path, добавляя путь в конец списка. Еще она делает некоторые дополнительные вещи, но мы их не касаемся. Пример использования:

import site
site.addsitedir('/Users/you/Projects/my_py_lib')
import my_module


Также, набрав команду python3 -m site в командной строке, вы можете узнать пути для импорта в текущим интерпретаторе Python.

Минус способов с добавлением путей через sys.path и site – IDE скорее всего не будет видеть и индексировать эти динамические пути, а значит будет много красных подчеркиваний и отсутствие автодополнения, даже если код при этом прекрасно выполняется.

PYTHONPATH – переменная окружения, которую вы можете установить перед запуском интерпретатора. Будучи заданной, она также влияет на sys.path, добавляя пути поиска модулей в начало списка.

На Windows можно использовать команду set. Если надо задать два и более путей, разделите их точкой с запятой:

set PYTHONPATH=C:\pypath1\;C:\pypath2\
python -c "import sys; print(sys.path)"
# Пример вывода:
['', 'C:\\pypath1', 'C:\\pypath2', 'C:\\opt\\Python36\\python36.zip', 'C:\\opt\\Python36\\DLLs', 'C:\\opt\\Python36\\lib', 'C:\\opt\\Python36', ..., 'Python36\\lib\\site-packages\\Pythonwin']


На Linux и macOS можно использовать export. Два и более путей разделяются двоеточием:

export PYTHONPATH='/some/extra/path:/foooo'
python3 -c "import sys; print(sys.path)"
# Пример вывода
['', '/some/extra/path', '/foooo', ...]


Или даже в одну строку:

PYTHONPATH='/some/path' python3 -c "import sys; print(sys.path)"

Кто не знал, ключ -c для python3 просто выполняет строчку кода. И да, лишних пробелов вокруг знака равно не должно быть, это такой синтаксис.

Если вам заранее известны пути импорта дополнительных модулей, можно задать их прямо в IDE. На примере PyCharm, заходите в настройки: Project: ваш проект – Project Structure – Add Content Root.

Таким образом, у вас будут работать все фишки IDE для импортированных по сторонним путям модулей, но код будет запускаться корректно только из этой IDE, а чтобы запустить его из-вне, например из терминала, придется все равно прописать PYTHONPATH.
источник
2020 May 20
PyWay – гуру Python 🐉
​​#интервью

Взял интервью у опытного Python-разработчика
источник
2020 June 01
PyWay – гуру Python 🐉
Знаю, давно ничего для вас не писал. Но я исправляюсь.
источник
2020 June 05
PyWay – гуру Python 🐉
Левенштейн на практике, это, например, нечеткое сравнение строк:

>>> from fuzzywuzz import fuzz
>>> fuzz.token_sort_ratio("я люблю спать", "Я люблю спать!")
100
>>> fuzz.token_sort_ratio("я люблю спать", "я люблю есть")
56


Или нечеткий поиск текста:

from fuzzywuzzy import process

strings = ['привет', 'здравствуйте', 'приветствую', 'хай', 'здорова', 'ку-ку']
process.extract("Прив", strings, limit=3)
# [('привет', 90), ('приветствую', 90), ('здравствуйте', 45)]


Интересно? Тогда читайте новую заметку.
источник
2020 June 10
PyWay – гуру Python 🐉
Задачка. Есть глобальная переменная g. В функции мы делаем del g.

g = 100

def f():
   global g
   g = 200
   del g
   g = 300

f()
print(g)
источник
PyWay – гуру Python 🐉
Что будет выведено на экран pring(g)?
Анонимная викторина
17%
100
5%
200
47%
300
31%
NameError (удалили же g)
Проголосовало: 346
источник
2020 June 11
PyWay – гуру Python 🐉
Инструкция del (от англ. delete), как можно понять из названия, нужна чтобы что-то удалять, а именно имена переменных, атрибуты объектов, элементы списков и ключи словарей.

1. Удаление элемента из списка по индексу:

>>> x = [1, 2, 3, 4, 5]
>>> del x[2]
>>> x
[1, 2, 4, 5]


Также можно удалять по срезам. Пример: удаление первых двух элементов:

>>> x = [1, 2, 3, 4, 5]
>>> del x[:2]
>>> x
[3, 4, 5]


Удаление последних n элементов: del x[n:].
Удаление элементов с четными индексами: del x[::2], нечетными: del x[1::2].
Удаление произвольного среза: del x[i:j:k].

Не путайте del x[2] и x.remove(2). Первый удаляет по индексу (нумерация с 0), а второй по значению, то есть находит в списке первую двойку и удаляет ее.

2. Удаление ключа из словаря. Просто:

>>> d = {"foo": 5, "bar": 8}
>>> del d["foo"]
>>> d
{'bar': 8}

А вот строки, байты и сеты del не поддерживают.

3. Удаление атрибута объекта.

class Foo:
   def __init__(self):
       self.var = 10

f = Foo()
del f.var
print(f.var)  # ошибка!


Примечание: можно через del удалить метод у самого класса (del Foo.method), но нельзя удалить метод у экземпляра класса (del Foo().method - AttributeError).

4. Что значит удалить имя переменной? Это просто значит, что надо отвязать имя от объекта (при этом если на объект никто более не ссылается, то он будет освобожден сборщиком мусора), а само имя станет свободно. При попытке доступа к этому имени после удаления будет NameError, пока ему снова не будет что-то присвоено.

>>> a = 5
>>> del a
>>> a
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined


Здесь кроется один нюанс. Если переменная была внутри функции помечена, как global, то после ее удаления глобальная переменная никуда не денется, а имя освободится лишь в зоне видимости функции. Причем если мы снова присвоим ей значение, то она опять окажется глобальной, т.е. del не очищает информацию о global!

g = 100
def f():
   global g
   g = 200
   del g  # g останется вне фукции
   g = 300  # таже самая глобальная g

f()
print(g) # 300


Чтобы реально удалить глобальную переменную, можно сделать так: del globals()['g'].

В пунктах 1, 2, 3 в качестве имен могут фигурировать выражения и ссылки, так как операции идут над содержимым объектов, а в пункте 4 должно быть строго формальное имя удаляемого объекта.

>>> x = [1, 2, 3]
>>> y = x
>>> del y  # удаляет именно y, но x остается
источник
PyWay – гуру Python 🐉
Еще одна особенность del – она может удалить несколько вещей за раз, если передать в нее кортеж или список объектов на удаление.

x, y, z = 10, 20, [1, 2, 3]
del x, y, z[2]

Пусть задан список из 5 элементов:

x = [1, 2, 3, 4, 5]
del x[2], x[4]
источник
PyWay – гуру Python 🐉
Чему будет равен список x?
Анонимная викторина
11%
x = [1, 3, 5]
64%
x = [1, 2, 4]
2%
x = [1, 2, 4, 5] без ошибок
23%
x = [1, 2, 4, 5] с ошибкой
Проголосовало: 288
источник
2020 June 25
PyWay – гуру Python 🐉
Подписчик просил меня показать пример игры по сети на Python, и вот я созрел!
Это будет карточная игра Дурак.
1️⃣ В первой части у нас будет логика игры (пока без сети на одной машине).
2️⃣ Во второй части я расскажу, как в локальной сети (включая WiFi дома или в любимой кафешке) автоматически находить себе соперников с помощью протокола UDP. Да-да, долой скучные клиент-серверные TCP соединения, как во всех обыденных примерах в интернете! 😎
3️⃣ В третьей части мы уже прикрутим полноценное сетевое взаимодействие между клиентами, которые уже нашли друг друга. Тоже на UDP.
4️⃣ И, если захотите, в четвертой части добавим графический интерфейс и протестируем игру в реальных условиях. ☕️
источник
2020 June 28
PyWay – гуру Python 🐉
Статья по второй части сетевой игры в Дурака готова! В ней расскажу о простеньком самодельном сетевом протоколе обнаружения клиентов по UDP. Он работает в пределах локальной сети (домашнего, кафешного, классного WiFi и т.п.) Протокол хорош тем, что вам не нужно запускать отдельный сервер и передавать клиентам его IP адрес. Клиенты игры, желающие начать новую партию сами найдут друг друга. Децентрализация!
источник
2020 July 08
PyWay – гуру Python 🐉
Напомню, что репозиторий игры по адресу https://github.com/tirinox/durakmq. Буду рад вашим звездочкам! 😋
источник
PyWay – гуру Python 🐉
Хотите часть 4, где мы прикрутим GUI – графический интерфейс к игре?
Анонимный опрос
89%
Да, конечно.
11%
Нет, не стоит.
Проголосовало: 123
источник
PyWay – гуру Python 🐉
Часть 3 – сетевая игра в Дурака.

В этой части рассматриваем аспекты обмена сообщения между двумя клиентами игры: сериализацию, отправку сообщений, создаем поток для приема сообщений, обрабатываем команды от пользователя. Короче, доводим игру до играбельного состояния и следим за соблюдением правил. https://t.me/iv?url=https://tirinox.ru/durak-game-p3/&rhash=56b30beec7290d
источник
2020 July 15
PyWay – гуру Python 🐉
​​Работа над частью 4 идет полным ходом. Я прикручиваю к сетевой игре в Дурака графический интерфейс на Kivy. Признаюсь честно, я раньше не работал с Kivy, поэтому учусь и делаю этот проект параллельно, встречая множество трудностей по собственному незнанию. Но дело тем не менее продвигается, и в ближайшие дни я закончу первую версию и выложу код с объяснениями. Самое классное, что игру можно сразу собирать под смартфон (проверял на Андроиде). Поэтому сможем прийти в кафе и поиграть. Пока вот скриншот-тизер:
источник
2020 July 18
PyWay – гуру Python 🐉
​​Блог разработки Durak GUI

По Kivy в интернете реально мало толковой информации даже на английском языке. Всевозможные туториалы примитивны и просто копируют друг друга. Некоторые из них уже не работают на современной версии Kivy. В целом они не покрывают практически никакие вопросы, возникающие в процессе разработки реального приложения. Как узнать размер окна? Не известно наверняка. Приходится копаться в исходном коде движка, чтобы понять. Как сделать элементарное окно типа Alert с кнопкой и текстом? Да никто не знает. В документации есть картинка такого окна, но код рядом просто не соответствует.

Какие решения я вижу в этой ситуации? Можно присылать вопросы на stackoverflow.com (долго, и не факт, что ответят).
Я рекомендую искать уже готовый открытый код приложений на Kivy либо в репозиториях на GitHub, либо, например, на сайте searchcode.com. По аналогии с тем, как кто-то сделал до вас можно кое-как разобраться.

Касательно проекта игры. Я начал со стандартных раскладок, используя BoxLayout, StackLayout и подобные инструменты. Вид игры рисовался скучно, топорно и кривовато, как вы можете заменить на скриншоте из предыдущего поста. На мои вопросы не находилось никаких ответов. Об анимациях даже не приходилось и думать в таком положении вещей, ибо Layout берут на себя контроль над положением и размером виджетов.

Как давнего игродела, меня это совершенно не устраивало, и я решил радикально все переделать. Все игровые объекты будут располагаться на FloatLayout, который дает программисту полный контроль над положением и размерами виджетов. Далее я начертил чертеж, где вычислил координаты каждой карты на экране. Например, карты текущего игрока располагаются на дуге окружности радиусом в 0.9 от ширины экрана и центром ниже нижней кромки экрана. Угловые границы дуги: от -30º до 30º относительно вертикали. Также, добавил вращение карт путем матричных преобразований, дабы карты выстраивались в традиционный веер.

Далее каждый виджет я наделил атрибутами target_position и target_rotation – это позиция и угол поворота, куда стремиться карта со временем. Задал такой интервал:

Clock.schedule_interval(self.update, 1.0 / 60.0)

Условно каждый кадр (1/60 долю секунды), реальное положение pos карты становится чуть ближе к ее целевому положению target_position. Движение получается экспоненциально затухающим: чем ближе карта к цели, тем она медленнее движется, поэтому анимации получились вполне естественные.

EXP_ATT = 5.0

def update(self, dt):
   df = self.EXP_ATT * dt
   for child in self.root.children:
       if hasattr(child, 'target_position'):
           x, y = child.pos
           # компенсируем положение точки, смещая ее из нижнего левого угла в середину виджета
           x += child.size[0] / 2
           y += child.size[1] / 2
           tx, ty = child.target_position
           if fast_dist(x, y, tx, ty) >= 0.1:
               x += (tx - x) * df
               y += (ty - y) * df
               # возвращаем обратно из середины точку к углу
               child.pos = (x - child.size[0] / 2, y - child.size[1] / 2)
       if hasattr(child, 'target_rotation'):
           tr, r = child.target_rotation, child.rotation
           if abs(tr - r) >= 0.1:
               child.rotation += (tr - r) * df


Это все! Просто меняя атрибуты target_position и target_position можно метать карты по столу с приятной анимацией. Ниже прикреплю GIF-анимацию.
источник
PyWay – гуру Python 🐉
источник
2020 July 24
PyWay – гуру Python 🐉
​​Уже на финишной прямой. Осталось придумать, как окончательно запараллелить GUI и сетевое взаимодействие. Скорее всего отдельный поток будет получать данные из сети и уведомлять GUI. А еще я стараюсь сохранять код максимально компактным и доступным для изучения, при этом сделать так, чтобы конечный продукт был удобен и приятен глазу. Вот так это выглядит на смартфоне.
источник
2020 August 12
PyWay – гуру Python 🐉
Разрабатывая Durak GUI я столкнулся с интересной проблемой.

Для удобства я решил объединить строковые константы названий сетевых сообщений в класс, наследуемый от Enum (встроенный тип перечислений).

from enum import Enum
class UpdateAction(Enum):
   FINISH_TURN = 'finish_turn'
   ATTACK = 'attack'
   DEFEND = 'defend'


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

import json
json.dumps(UpdateAction.ATTACK)
# TypeError: Object of type UpdateAction is not JSON serializable

Какие тут могут быть решения?

1. Добавить в цепочку наследования класс str. То есть родителями UpdateAction будут и str, и Enum. В этом случае все опции тоже будут кодированы строками. Пример:

from enum import Enum
class UpdateAction(str, Enum):
   FINISH_TURN = 'finish_turn'
   ATTACK = 'attack'
   DEFEND = 'defend'


import json
j = json.dumps(UpdateAction.ATTACK)
print(j)  # "attack"

UpdateAction(json.loads(j))  # загрузка обратно в Enum

2. От строк перейти к числам. В самом деле строки нужны, чтобы легче человеку легче читать состояние при отладке. Но можно взять IntEnum, он поддерживает JSON-кодирование из коробки, кроме того числа требуют меньше байт.

from enum import IntEnum
class UpdateAction(IntEnum):
   FINISH_TURN = 1
   ATTACK = 2
   DEFEND = 3

3. Не применять Enum вообще! В самом деле, я не пользуюсь в коде никакими возможностями Enum, можно просто выбросить наследование:

class UpdateAction:
FINISH_TURN = 'finish_turn'
   ...

Иногда не стоит переусложнять простое!

По поводу самой игры, как видите возникли задержки, но прямо сейчас я продолжаю над ней трудиться.
источник
2020 August 13
PyWay – гуру Python 🐉
​​При работе в Kivy с потоками (threading) вы должны иметь в виду, что все изменения в графическом интерфейсе должны делаться из главного потока. Да, и это не смотря даже на GIL! Дело в том, что контекст OpenGL очень не любит, когда его трогают из чужого потока, что приводит к случайным и иногда сложно уловимым визуальным глюкам (см. прикрепленное фото).

Как это сделать? Воспользоваться декоратором mainthread, который поставит при вызове оригинальной функции поставит ее выполнение в следующий тик главного цикла приложения в главном потоке.

Пример использования из Durak GUI. Метод self.on_found_peer вызывается из другого вспомогательного потока, но он меняет интерфейс, поэтому должен быть снабжен декоратором mainthread:

from kivy.clock import mainthread

class DurakFloatApp(App):
...
  @mainthread
   def on_found_peer(self, addr, peer_id):
       print(f'Найден соперник {peer_id}@{addr}')
       # делать что-то с GUI!

   ...
   self.discovery = DiscoveryProtocol(self.my_pid, PORT_NO)
   #
   self.discovery.run_in_background(self.on_found_peer)
источник