LINUX.ORG.RU

python и датаклассы

 ,


0

2

Гуглю и не врубаюсь, как правильно сделать датакласс, в котором я изначально не знаю значения полей. Я знаю типы, но значение будет известно только после нескольких циклов. Простейший пример, счётчик минимального числа, т.е. при обработке некоторого объекта мне надо отобрать объект с минимальным отклонением, которое измеряется целочисленным значением и не может быть меньше нуля. Однако может быть очень большим числом, хотя в 99% случаев будет не больше сотни. Объект сложный, так что датакласс хочется делать. Проблема в том, что стандартный фокус на случай максимума с подстановкой 0 и увеличением не годится. А писать None как-то идеологически неправильно.

from dataclasses import dataclass

@dataclass
class ShortestWord:
    word: str
    length: int


first_shortest_word = ShortestWord(None, None)
a = ['Вася', 'ел', 'бананы']
for word in a:
    if first_shortest_word.word is None:
        first_shortest_word.word = word
        first_shortest_word.length = len(word)
    else:
        if first_shortest_word.length > len(word):
            first_shortest_word.word = word
            first_shortest_word.length = len(word)
print(first_shortest_word)
Реальная штука гораздо сложнее, но смысл такой же.

PS

Можно конечно и простой класс бахнуть, но фиг лучший ли это вариант.

PPS

А всё из-за довольно большого числа полей и вообще шляпного алгоритма из костылей, я не хочу чтобы люди кому это достанется бахнули не тот тип в не то поле, так как много похожих полей, разных по смыслу но похожих, так хоть IDE их ругнёт.

PPPS

Я знаю что этот пример можно упростить, но в реальности там ещё десяток отдельных полей которые надо считать и я хочу отбрасывать те объекты для которых явно считать бессмысленно. Так что ещё несколько циклов for пропущено.

★★★★★

Проблема в том, что стандартный фокус на случай максимума с подстановкой 0 и увеличением не годится. А писать None как-то идеологически неправильно.

не очень врубаюсь, что тебе надо, но какое значение ты ожидаешь от «неинициализированной» переменной? Как раз таки None здесь выглядит разумным компромиссом. Сделай тип Optional[str]

Sahas ★★★★★ ()
Ответ на: комментарий от Sahas

Не, сам себя перемудрил. Проще всё гораздо.

from dataclasses import dataclass


@dataclass
class ShortestWord:
    word: str = None
    length: int = None


first_shortest_word = ShortestWord()
a = ['Вася', 'ел', 'бананы']
for word in a:
    if first_shortest_word.word is None:
        first_shortest_word.word = word
        first_shortest_word.length = len(word)
    else:
        if first_shortest_word.length > len(word):
            first_shortest_word.word = word
            first_shortest_word.length = len(word)
print(first_shortest_word)

peregrine ★★★★★ ()
Ответ на: комментарий от peregrine

Гораздо быстрее стандартных. Вообще не понимаю на кой нужны дейтаклассы при наличи pydantic.

Хотя не помню, откуда я взял, что гораздо быстрее, по-правде говоря. Мб сам бенчил своё прямое приведение типов в __post_init__ и классметоде для предачи уже верных значений в инит (условный cls.from_dict) в сравнении с pydantic, то ли какой-то чужой бенч смотрел. Не помню, давно было, но оба варианта возможны, ибо я и бенчи гуглил, и сам проверял.

WitcherGeralt ★★ ()
Последнее исправление: WitcherGeralt (всего исправлений: 1)

С точки зрения понимаемости и модульности изменение значения переменной по месту аля многие вариации тьюринговой трясины — зло и применяется там, где без этого вполне можно обойтись. Смотри сюда:

first_shortest_word = None
a = ['Вася', 'ел', 'бананы']
for word in a:
    if first_shortest_word is None:
        first_shortest_word = ShortestWord(word, len(word))
    else:
        if first_shortest_word.length > len(word):
            first_shortest_word = ShortestWord(word, len(word))
print(first_shortest_word)

Ты спросишь «а что же мне делать, если я захочу сделать манкипатч для объекта ShortestWord, но при пересоздании объекта все изменения потеряются» — я отвечу «ССЗБ, в этом и весь смысл моего ответа: изолировать одни модули от эффектов остальных модулей».

С точки зрения производительности разницы между моим и твоим кодом нет, поскольку в CPython это все равно будет создание-пересоздание объектов-контейнеров, а какое-нибудь PyPy выкинет все контейнеры и положит word и length в регистры.

byko3y ★★ ()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от WitcherGeralt

Не очень гарантирует, скорее приводит к нужному типу

И это хорошо. В питоне не существует никакого универсального способа проверить соответствие некоторого значения некоторому типу. Причин несколько, начиная от использования строк в качестве чисел, и заканчивая равными с точки зрения утиной типизации типами, которые не состоят в отношениях наследования. Потому единственная гарантия типа в питоне — это пересоздание с нужным типом значения неизвестного типа. Если я не ошибаюсь, для int и str в простейших случаях это вырождается до банального увеличения счетчика ссылок, никакие новые контейнеры не создаются.

А за pydantic спасибо — надеюсь, что можно будет как-нибудь его быстро и красиво присобачить для описания операций с неизменяемыми составными значеняими.

byko3y ★★ ()
Ответ на: комментарий от peregrine

Они есть, но не оч большие. Для «конкретно твоей задачи» есть смысл сделать бенчмарк с проверкой. Без него сложно сказать будут ли критичным этот оверхед

Aswed ★★★★★ ()

class ShortestWord:

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

счётчик минимального числа

я не хочу чтобы люди кому это достанется бахнули не тот тип в не то поле

с такими классами и определениями по-другому не получится.

мне надо отобрать объект с минимальным отклонением, которое измеряется целочисленным значением и не может быть меньше нуля

min(filter(lambda x:x>=0, objlst))

в реальности там ещё десяток отдельных полей которые надо считать и я хочу отбрасывать те объекты для которых явно считать бессмысленно. Так что ещё несколько циклов for пропущено.

man filter

конструируешь какой твоей душе угодно предикат и получаешь итератор только по нужным объектам. вообще без циклов.

anonymous ()

class ShortestWord:

if first_shortest_word.word is None:

first_shortest_word.word = word

Ты натягиваешь ООП на глобус. Не надо так.

но значение будет известно только после нескольких циклов.

Вангую, ты опять натягиваешь ООП на глобус. Вангую, значение, которое будет известно только после нескольких циклов, должно быть атрибутом совсем другого класса и проблема поздней инициализации отпадёт сама собой.

anonymous ()
Ответ на: комментарий от anonymous

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

Нет конечно, там ещё статистика по его встречаемости, позиция в предложении, предполагаемый член предложения и прочее и вообще это не самое короткое слово. Можно было бы и СУБД натягивать, но не охота какие-то объекты которые потом будут не нужны туда впихивать.

конструируешь какой твоей душе угодно предикат и получаешь итератор только по нужным объектам. вообще без циклов.

Смысл не в том, чтобы красиво, смысл в том, чтобы быстро и много раз обращаться, а не перекладывать потом это всё в списки , потому что итератор одноразовый.

peregrine ★★★★★ ()