LINUX.ORG.RU

resize изображения pillow(вопрос от начинающего) по упрощению функции.

 ,


0

1

Подскажите нубу, как правильно функцию `def get_new_size()` составить и избавиться от кучи ifов. Понимаю ,что функция грязная и надо все проверки из нее вынести и возможно расщепить на несколько функций, но ,как именно сделать никак не догадаюсь.

import os
import argparse
from PIL import Image


def create_parser():
    parser = argparse.ArgumentParser(
        description='Module for resize image.')
    parser.add_argument(
        'image', help='Where get the image.')
    parser.add_argument(
        '-w', '--width', type=int, help='Input new width image.')
    parser.add_argument(
        '-he', '--height', type=int, help='Input new height image.')
    parser.add_argument(
        '-sc', '--scale', type=float, help='Input how scale image.')
    parser.add_argument(
        '-out', '--output', help='Where to put the image.')
    return parser


def get_image(path_to_original):
    return Image.open(path_to_original)


def get_new_size(original_image,
                 new_width,
                 new_height,
                 scale):

    width_original, height_original = original_image.size
    if (new_width or new_height) and scale:
        raise RuntimeError('You must use scale without width or height!')
    elif new_width and new_height:
        if width_original / height_original != new_width / new_height:
            print('***The proportions do not match the original image.***')
        new_size = (new_width, new_height)
    elif new_width:
        height = int(new_width * height_original / width_original)
        new_size = (new_width, height)
    elif new_height:
        width = int(new_height * width_original / height_original)
        new_size = (width, new_height)
    elif scale:
        new_size = [round(scale * size) for size in original_image.size]
    else:
        raise RuntimeError('Width or height or scale required!')
    return new_size


def resize_image(original_image, new_size):
    return original_image.resize(new_size, Image.ANTIALIAS)


def chose_path_to_result(path_to_result, path_to_original, resized_image):
    if path_to_result:
        return path_to_result
    else:
        name_file, file_extension = os.path.splitext(path_to_original)

        created_path = '{}__{}x{}{}'.format(
            name_file,
            *resized_image.size,
            file_extension)
        return created_path


def save_image(resized_image, path_to_result):
    resized_image.save(path_to_result)


if __name__ == '__main__':
    parser = create_parser()
    namespace = parser.parse_args()

    path_to_original = namespace.image
    path_to_result = namespace.output
    image = get_image(path_to_original)
    new_size = get_new_size(image,
                            namespace.width,
                            namespace.height,
                            namespace.scale)

    resized_image = resize_image(image, new_size)

    chosen_path_to_result = chose_path_to_result(
        path_to_result, path_to_original, resized_image)

    save_image(resized_image, chosen_path_to_result)

Можно ли в «if __name__ == '__main__':» плодить ifы с проверками? Или это тоже не правильно. Если что сильно не пинайте)) с питоном общаюсь менее 2х месяцев..

У меня вопрос, вот этот весь код должен делать что-то осмысленное? Или он чиста в учебных целях накидать разных функций в разных местах?

Зачем ты проверяешь правильность полученных аргументов внутри get_new_size? Как досюда программа вообще дошла то? Если аргументы негодные подъехали так ты и должен был вывалиться из программы во время их парсинга в самой первой функции.

Зачем нужны функции из одной строчки? Ты их как-то собираешься использовать повторно с разным аргументами?

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

Код у учебных целях. С задачей он вроде справляется:

«Скрипт принимает на вход изображение и кладёт изображение с новым размером куда скажет пользователь или рядом с исходным. У него есть обязательный аргумент – путь до исходной картинки. И несколько необязательных: width - ширина результирующей картинки, height - её высота, scale - во сколько раз увеличить изображение (может быть меньше 1), output - куда класть результирующий файл.

Логика работы такая:

Если указана только ширина – высота считается так, чтобы сохранить пропорции изображения. И наоборот. – Если указана и ширина и высота – создать именно такое изображение. Вывести в консоль предупреждение, если пропорции не совпадают с исходным изображением. Если указан масштаб, то ширина и высота указаны быть не могут. Иначе никакого ресайза не происходит и скрипт ломается с понятной ошибкой. Если не указан путь до финального файла, то результат кладётся рядом с исходным файлом. Если исходный файл называется pic.jpg (100x200), то после вызова python image_resize.py --scale 2 pic.jpg должен появиться файл pic__200x400.jpg.»

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

Негодные аргументы лучше в первой функции отсекать или в if_name это можно делать?


...
def get_new_size(original_image,
                 new_width,
                 new_height):

    width_original, height_original = original_image.size
    if new_width and new_height:
        if width_original / height_original != new_width / new_height:
            print('***The proportions do not match the original image.***')
        new_size = (new_width, new_height)
    elif new_width:
        height = int(new_width * height_original / width_original)
        new_size = (new_width, height)
    elif new_height:
        width = int(new_height * width_original / height_original)
        new_size = (width, new_height)
    return new_size

...

if __name__ == '__main__':
    parser = create_parser()
    namespace = parser.parse_args()

    image = get_image(namespace.image)

    if namespace.scale and (namespace.width or namespace.height):
        raise RuntimeError('You must use scale without width or height!')
    elif not namespace.scale and not namespace.width and not namespace.height:
        raise RuntimeError('Width or height or scale required!')
    elif namespace.scale:
        new_size = get_new_scale_size(image, namespace.scale)
    else:
        new_size = get_new_size(image,
                                namespace.width,
                                namespace.height)

    resized_image = resize_image(image, new_size)

    chosen_path_to_result = chose_path_to_result(
        namespace.output, namespace.image, resized_image)

    save_image(resized_image, chosen_path_to_result)
[\code]
PavelShturm
() автор топика
Ответ на: комментарий от PavelShturm

Код у учебных целях. С задачей он вроде справляется:

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

А ты разбрасывая алгоритм программы по разным функциям занимаешься «преждевременной оптимизацией», которая в данном случае нафиг никому не нужна.

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

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

Например

Задача: разослать поздравительные открытки на Новый Год адресатам

Первый файл

Поздравить_всех(картинка_девочка, картинка_шапочка, адресаты)
...
Главный модуль:
 Проверить_аргументы()
 Произвести_цветокоррекцию(картинка_девочка)
 Произвести_цветокоррекцию(картинка_шапочка)
 Вычислить_координаты_для_шапочки(картинка_девочка)
 Надеть_шапочку_на_девочку(картинка_девочка, картинка_шапочка)
 Разослать_адресатам(адресаты, картинка_девочка_в_шапочке)
Второй файл и далее (сами функции)
Проверить_аргументы():
 код
 ...

Произвести_цветокоррекцию(картинка):
 кода_нет_просто_заглушка

Вычислить_координаты_для_шапочки(картинка_девочка):
 код
 ...

Надеть_шапочку_на_девочку(картинка_девочка,картинка_шапочка):
 код
 ...

Разослать_адресатам(адресаты, картинка_девочка_в_шапочке):
 цикл_по_адресатам:
  Добавить_персональную_надпись(адресат, картинка_девочка_в_шапочке)
  код отправки
  ...

Добавить_персональную_надпись(адресат, картинка_девочка_в_шапочке):
 код
 ...

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

Функция Добавить_персональную_надпись(...) вообще не присутствует в главном модуле, но является вспомогательной для функции отправки картинок и отделяет логически код отправки от кода добавления надписи в целях повышения удобства отладки и общего понимания логики работы функции.

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

Учебный код проверяют менторы. Так же они следят за чистотой функций и логичностью написания. Поэтому портянка и грязные функции не прокатят.

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

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

Учебный код проверяют менторы. Так же они следят за чистотой функций и логичностью написания. Поэтому портянка и грязные функции не прокатят.

Тогда делаешь так:

  1. Пишешь всё одной длинной портянкой, чтобы всё правильно работало
  2. Чистишь/оптимизируешь код
  3. Разрезаешь код на функции
  4. Сдаёшь менторам
  5. Повторяешь пункты 2-4 до ...
justAmoment ★★★★★
()
Ответ на: комментарий от justAmoment

Я как раз сейчас пункты 2-3 пытаюсь делать. Подскажите, от ifов в моем случае ,как можно избавить и нужно ли? И можно ли проводить проверку валидности в if _name или лучше в отдельную функцию вынести?

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

А пункт 1 ты уже сдедал? Т.е. возьми весь свои код и объедини его, чтобы вообще ни одной твоей функции просто не было. И только после этого приступай к пункту 2 чистке и оптимизации. А уже только потом переходишь к 3 разрезанию на функции.

ПЫСЫ Вот прямо здесь приведи эту всю портянку, тогда тебе смогут посоветовать где и что разрезать на функции.

justAmoment ★★★★★
()
Последнее исправление: justAmoment (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.