Найти - Пользователи
Полная версия: Покритикуйте, пожалуйста, код
Начало » Python для новичков » Покритикуйте, пожалуйста, код
1
Stright
Всем здравствуйте! Читаю книгу Лутца “Изучаем Python”, после прочтения части книги про функциональное программирование решил для закрепления прочитанного, а заодно для облегчения
задач по работе, попрактиковаться.
Имеется файл('input.xlsx'), в котором входные данные представлены в виде перечня участковых лесничеств, которые состоят из кварталов, которые, в свою очередь, состоят из выделов и занимают определенную площадь. Необходимо вывести данные в виде таблицы в выходном файле ('output.xlsx'), где в первой графе участковое лесничество, во второй перечень вида кварталы(выделы), в третьей площадь по участковому лесничеству (образцы файлов прикрепляю).
Вот что у меня получилось:
'''
Создание списка арендуемых кварталов и
выделов по участковым лесничествам для лесного участка
(в качестве исходных данных необходим файл .xlsx с 4
колонками: Участковое лесничество, Квартал, Выдел, Площадь)
'''
import os.path
import openpyxl as xl
def read_data():
    '''
    Читаем данные, на выходе получаем список списков
    вида [[Участковое лесничество, квартал, выдел, площадь], [...]]
    без шапки таблицы
    '''
    # Выбираем входной файл в формате .xlsx с данными на рабочем столе
    wb = xl.load_workbook((os.path.join(os.path.expanduser('~'), 'Desktop',
                           input('Enter the file name on your desktop: \n'))))
    ws = wb.active
    # Получаем список списков вида [Участковое лесничество,
    # Квартал, Выдел, Площадь]
    data = [[val.value for val in row] for row in ws.iter_rows()]
    # Удаляем шапку таблицы
    del data[0]
    return sorted(data)
def list_vyd_to_str(list_vyd):
    '''
    Получение строки со списком выделов вида
    '1-3,5,7,10-12'
    '''
    result_list = []
    lst = []
    # Проверяем список на наличие выделов-дубликатов,
    # преобразуем элементы списка в числа, сортируем
    for vyd in list_vyd:
        if vyd not in lst:
            lst.append(vyd)
        else:
            print('Выделы-дубликаты в квартале!')
    lst = sorted(map(int, lst))
    def check_list():
        """
        Функция для проверки отсутствия выделов
        в списке выделов в квартале
        """
        for k, v in enumerate(range(lst[0], lst[-1])):
            if v not in lst:
                return k
        return len(lst)
    while lst:
        t = check_list()
        if t > 2:
            result_list += '{}-{},'.format(lst[0], lst[t-1])
            lst = lst[t:]
        else:
            result_list += '{},'.format(lst[0])
            lst = lst[1:]
    result_list = result_list[:-1]
    return ''.join(result_list)
def data_processing(data_from_file):
    '''
    Получаем список вида ['Участковое лесничество', 'кв....(выд....)',
    'Сумма выделов по участковому лесничеству'] из списка вида [['Участковое
    лесничество', квартал, выдел, площадь], [...]]
    '''
    # Получаем список вида ['Участковое лесничество', 'Квартал', [],[]],
    # который будем использовать в качестве шаблона
    # для отбора выделов, соответствующих определенному лесничеству
    # и определенному кварталу, два пустых списка для
    # перечня выделов и площадей соответственно
    list_temp = [[line[0], line[1], [], []] for line in data_from_file]
    # Удаляем повторяющиеся элементы этого списка
    list_forestry_kv = []
    for forestry_kv in list_temp:
        if forestry_kv not in list_forestry_kv:
            list_forestry_kv.append(forestry_kv)
    # Добавляем в список выделы и их площади, получаем
    # список вида [['Участковое лесничество',
    # 'Квартал', [1,2,3,5,...], [0.1, ...]], [...]]
    for template in list_forestry_kv:
        for line in data_from_file:
            if (template[0] == line[0] and
               template[1] == line[1]):
                template[2].append(line[2])
                template[3].append(line[3])
    # Получаем список вида [['Участковое лесничество',
    # 'Квартал', '1-3,5', 0.1], [...]]
    for line in list_forestry_kv:
        line[2] = list_vyd_to_str(line[2])
        line[3] = round(sum(map(float, line[3])), 1)
    # Сливаем квартал и выделы каждого элемента
    # последовательности в строку вида
    # кв.1(выд.1,2,3)
    list_forestry_kv = [(str(item[0]), 'кв.' + str(item[1]) +
                         ' (выд.' + str(item[2]) + '),',
                         item[3]) for item in list_forestry_kv]
    # Составляем список участковых лесничеств в файле
    list_temp = [line[0] for line in data_from_file]
    # Удаляем повторяющиеся элементы этого списка
    forestry_lst = []
    for forestry in list_temp:
        if forestry not in forestry_lst:
            forestry_lst.append(forestry)
    # Создаем шаблон для выходных данных, который представляет
    # собой список списков по числу участковых лесничеств
    result_list = [[forestry, '', 0] for forestry in forestry_lst]
    # Приплюсовываем в каждый из этих списков во 2 и 3
    # элемент соответственно строки вида кв.1(выд.1) и
    # площади
    for forestry in forestry_lst:
        for kv in list_forestry_kv:
            for item in result_list:
                if (forestry in kv and
                   forestry in item):
                    item[1] += kv[1]
                    item[2] += kv[2]
    # Удаляем запятую в конце каждого списка,
    # а также округляем площади по лесничеству
    for item in result_list:
        item[1] = item[1][:-1]
        item[2] = round(item[2], 1)
    return result_list
def write_data(data_for_write):
    '''
    Записываем данные в файл
    '''
    # Добавляем шапку таблицы
    # в список с данными
    wb = xl.Workbook()
    ws = wb.active
    ws.append(['Участковое лесничество', 'Квартал (выдел)',
               'Площадь, га'])
    total = 0  # Сумма по лесному участку
    # Записываем в файл построчно все списки и
    # и подбиваем общую сумму
    for i in data_for_write:
        ws.append(i)
        total += float(i[-1])
    ws.append(['Итого', '', total])
    wb.save((os.path.join(os.path.expanduser('~'), 'Desktop',
             input('Enter the name for the saved file: \n'))))
if __name__ == '__main__':
    write_data(data_processing(read_data()))
Свою задачу код выполняет, но при выполнении занимает много памяти (при большом количестве строк в файле).
Подскажите, пожалуйста, какие ошибки в коде, может, что-то надо было сделать как-то по-другому.
Я новичок в Python и в программировании вообще.
Входной файл
Выходной файл
terabayt
def list_vyd_to_str(list_vyd):
а как же мой код? ))

'кв.' + str(item[1]) +
                         ' (выд.' + str(item[2]) + '),'
как-то не очень красиво, переделайте под format

подумайте, может есть способ сделать все красивее, а то 3 вложенных один в один цикла выглядит не очень
for forestry in forestry_lst:
        for kv in list_forestry_kv:
            for item in result_list:

Stright
но при выполнении занимает много памяти
там где вы пишите
Stright
# Удаляем повторяющиеся элементы
вы не удаляете а создаете новый!
я думаю что весь код можно с меньшем коичеством списков сделать

P.S. в код сильно не вникал. list_vyd_to_str мы вам помогли, в read_data и write_data не думаю что можно что-то испортить, а вот data_processing выделите в отдельную программу и на форум с примерами входных и выходных данных

FishHook
del data[0]
А точно нельзя указать начало диапазона для iter_rows(), чтобы потом не удалять?

    def check_list():
        """
        Функция для проверки отсутствия выделов
        в списке выделов в квартале
        """
        for k, v in enumerate(range(lst[0], lst[-1])):
            if v not in lst:
                return k
        return len(lst)
Мы, по моему, с вами уже обсуждали in в применении к списку? Повторю еще раз: это очень плохо, это по сути полный перебор списка, а вы этот перебор запихали еще и в цикл. Это значит, что ваша функция при плохом стечении обстоятельств выполнится len(lst) ** 2 раз. Хотя здесь легко можно обойтись одним проходом по списку. Как то так, например:
def check_list():
    enum = enumerate(xrange(lst[0], lst[-1]))
    l = iter(lst)
    for k, v in enum:
        litem = l.next()
        if litem != v:
          return k
    return len(lst)
ну и дальше у вас это везде по коду.
Stright
terabayt
а как же мой код? ))
Оставил более читаемый, хотя способ решения понравился )
terabayt
как-то не очень красиво, переделайте под format
Да, действительно некрасиво, переделал
terabayt
подумайте, может есть способ сделать все красивее, а то 3 вложенных один в один цикла выглядит не очень
думал долго, прежде, чем такое написать, но это единственное, что пришло в голову
terabayt
а вот data_processing выделите в отдельную программу и на форум с примерами входных и выходных данных
Немного пообрезал входные данные для краткости:
lst = [['Курбское', 1, 1, 0.6], ['Курбское', 1, 2, 0.6],
       ['Курбское', 1, 3, 2.4], ['Курбское', 1, 7, 0.2],
       ['Курбское', 1, 8, 1.8], ['Курбское', 1, 9, 1.7],
       ['Курбское', 2, 1, 10.2], ['Курбское', 2, 2, 0.2],
       ['Курбское', 2, 3, 4], ['Курбское', 2, 4, 6.9],
       ['Ставотинское', 1, 28, 0.6], ['Ставотинское', 1, 29, 2.9],
       ['Ставотинское', 1, 30, 1], ['Ставотинское', 1, 31, 1.4],
       ['Ставотинское', 1, 37, 0.1], ['Ставотинское', 1, 38, 0.1]]
,где
lst[i][0] - участковое лесничество,
lst[i][1] - квартал, lst[i][2] - выдел, lst[i][3] - площадь
Должно получиться на выходе:
[['Курбское', 'кв.1 (выд.1-3,7-9),кв.2 (выд.1-4)', 28.6], ['Ставотинское', 'кв.1 (выд.28-31,37,38)', 6.1]]
Код:
def list_vyd_to_str(list_vyd):
    '''
    Получение строки со списком выделов вида
    '1-3,5,7,10-12' из списка вида [1,2,3,5,7,10,11,12]
    '''
    result_list = []
    lst = sorted(list(set(list_vyd)))
    if len(lst) != len(list_vyd):
        print('Выделы-дубликаты в квартале')
    def check_list():
        """
        Функция для проверки отсутствия выделов
        в списке выделов в квартале
        """
        l = iter(lst)
        for k, v in enumerate(range(lst[0], lst[-1])):
            l_item = l.__next__()
            if v != l_item:
                return k
        return len(lst)
    while lst:
        t = check_list()
        if t > 2:
            result_list.append('{}-{},'.format(lst[0], lst[t-1]))
            lst = lst[t:]
        else:
            result_list.append('{},'.format(lst[0]))
            lst = lst[1:]
    return ''.join(result_list)[:-1]
def data_processing(data_from_file):
    '''
    Получаем список вида ['Участковое лесничество', 'кв....(выд....)',
    'Сумма выделов по участковому лесничеству'] из списка вида [['Участковое
    лесничество', квартал, выдел, площадь], [...]]
    '''
    # Получаем список вида ['Участковое лесничество', 'Квартал', [],[]],
    # который будем использовать в качестве шаблона
    # для отбора выделов, соответствующих определенному лесничеству
    # и определенному кварталу, два пустых списка для
    # перечня выделов и площадей соответственно
    list_temp = [[line[0], line[1], [], []] for line in data_from_file]
    # Удаляем повторяющиеся элементы этого списка
    list_forestry_kv = []
    for forestry_kv in list_temp:
        if forestry_kv not in list_forestry_kv:
            list_forestry_kv.append(forestry_kv)
    # Добавляем в список выделы и их площади, получаем
    # список вида [['Участковое лесничество',
    # 'Квартал', [1,2,3,5,...], [0.1, ...]], [...]]
    for template in list_forestry_kv:
        for line in data_from_file:
            if (template[0] == line[0] and
               template[1] == line[1]):
                template[2].append(line[2])
                template[3].append(line[3])
    # Получаем список вида [['Участковое лесничество',
    # 'Квартал', '1-3,5', 0.1], [...]]
    for line in list_forestry_kv:
        line[2] = list_vyd_to_str(line[2])
        line[3] = round(sum(map(float, line[3])), 1)
    # Сливаем квартал и выделы каждого элемента
    # последовательности в строку вида
    # кв.1(выд.1,2,3)
    list_forestry_kv = [(str(item[0]),
                        'кв.{} (выд.{}),'.format(item[1], item[2]),
                        item[3]) for item in list_forestry_kv]
    # Составляем список участковых лесничеств в файле
    list_temp = [line[0] for line in data_from_file]
    # Удаляем повторяющиеся элементы этого списка
    forestry_lst = []
    for forestry in list_temp:
        if forestry not in forestry_lst:
            forestry_lst.append(forestry)
    # Создаем шаблон для выходных данных, который представляет
    # собой список списков по числу участковых лесничеств
    result_list = [[forestry, '', 0] for forestry in forestry_lst]
    # Приплюсовываем в каждый из этих списков во 2 и 3
    # элемент соответственно строки вида кв.1(выд.1) и
    # площади
    for forestry in forestry_lst:
        for kv in list_forestry_kv:
            for item in result_list:
                if (forestry in kv and
                   forestry in item):
                    item[1] += kv[1]
                    item[2] += kv[2]
    # Удаляем запятую в конце каждого списка,
    # а также округляем площади по лесничеству
    for item in result_list:
        item[1] = item[1][:-1]
        item[2] = round(item[2], 1)
    return result_list
print(data_processing(lst))
Функцию list_vyd_to_str оставил, т.к. она используется в data_processing
Stright
FishHook
А точно нельзя указать начало диапазона для iter_rows(), чтобы потом не удалять?
Спасибо, поправил
FishHook
Мы, по моему, с вами уже обсуждали in в применении к списку
Нет, не со мной, был бы благодарен за ссылку на эту тему. Спасибо за замечание, действительно, намного быстрее работает.
terabayt
нужно же в том же духе и вторуюфункцию сделать ))
def data_processing(lst, a={}):
    for i in lst: a[i[0]][1][i[1]], a[i[0]][0] = a.setdefault(i[0], [0, {}])[1].setdefault(i[1], []) + [i[2]], a[i[0]][0] + i[3]
    return [[i, ','.join(['кв.%d (выд.%s)' % (j, list_vyd_to_str(a[i][1][j][::2])) for j in a[i][1]]), a[i][0]] for i in a]
вот вам и задание
перепешите этот код в нормальный вид, эт будет строк 10
и тогда вы сможете увидеть мой подход
Stright
Спасибо большое! Так и думал, что словари нужны, пытался сначала с помощью генераторов создать,
но запутался и взялся за списки
Получилось как-то так:
lst = [['Курбское', 1, 1, 0.6], ['Курбское', 1, 2, 0.6],
       ['Курбское', 1, 3, 2.4], ['Курбское', 1, 7, 0.2],
       ['Курбское', 1, 8, 1.8], ['Курбское', 1, 9, 1.7],
       ['Курбское', 2, 1, 10.2], ['Курбское', 2, 2, 0.2],
       ['Курбское', 2, 3, 4], ['Курбское', 2, 4, 6.9],
       ['Ставотинское', 1, 28, 0.6], ['Ставотинское', 1, 29, 2.9],
       ['Ставотинское', 1, 30, 1], ['Ставотинское', 1, 31, 1.4],
       ['Ставотинское', 1, 37, 0.1], ['Ставотинское', 1, 38, 0.1]]
def list_vyd_to_str(list_vyd):
    '''
    Получение строки со списком выделов вида
    '1-3,5,7,10-12' из списка вида [1,2,3,5,7,10,11,12]
    '''
    # Проверка полученного списка на дубликаты
    lst = sorted(list(set(list_vyd)))
    if len(lst) != len(list_vyd):
        print('Выделы-дубликаты в квартале')
    # Получаем список выделов, отсутствующих в
    # списке в виде списка [n - 1, ..., n + 1]
    items1 = sorted(list(set(range(int(list_vyd[0])-1,
                                   int(list_vyd[-1])+2)) -
                         set(map(int, list_vyd))))
    # Получаем список выделов, отсутствующих в
    # списке в виде списка [n, ..., n + 1]
    items2 = sorted(list(set(range(int(list_vyd[0])-1,
                                   int(list_vyd[-1])+2)) -
                         set(map(int, list_vyd))))
    missing_items = zip(items1, items2[1:])
    # Соединяем в строку
    return ','.join('{}-{}'.format(a+1, b-1)
                    if b - a > 3
                    else ','.join(map(str, range(a+1, b)))
                    for a, b in missing_items
                    if b - a > 1)
def data_processing(lst, a={}):
    '''
    Из списка вида [[Участковое лесничество, квартал, выдел, площадь],
    [...]], полученного с файла получаем список вида
    [Участковое лесничество, 'кв.n(выд. ...)', ]
    '''
    # Создаем словарь вида
    # {'Участковое лесничество': [{'Кварталы': [выделы]},
    # Площадь по участковому]}
    for i in lst:
        a[i[0]] = a.setdefault(i[0], [{}, 0])
        a[i[0]][0][i[1]] = a[i[0]][0].setdefault(i[1], [])
        a[i[0]][0][i[1]].append(i[2])
        a[i[0]][1] += i[3]
    # Создаем выходной список
    result = []
    for i in a:
        result.append([i,
                       ','.join(['кв.{} (выд.{})'.format(j,
                       list_vyd_to_str(a[i][0][j])) for j in a[i][0]]),
                       a[i][1]])
    return result
print(data_processing(lst))
Только вот здесь опечатка
list_vyd_to_str(a[i][1][j][::2]) # [::2] не надо
И в случае большого количества данных номера кварталов неупорядочены, потому как это ключи словаря, ну с этим я разберусь
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB