Добавить годового общего для всех файлов Excel


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

У меня есть годовой папки с файлами Excel в нем, это примерно то же самое, только в прошлом году я внес изменения в структуру их. Моей целью было, чтобы перебрать их все и добавить одно значение из них список, а затем сравнить эти результаты. До сих пор это все работает, как я надеялся. Я не поняла, как определить ту часть, которая читает файлы Excel в вызываемый объект ((?)надеюсь, что это правильный термин).

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

import os
import openpyxl

my_list_2015 = []
my_list_2016 = []
my_list_2017 = []
year_show = 2015

def brutto_total(netto):
    brutto = netto + netto * 0.2
    return '%.2f' %  brutto

def show_result(year):
    global year_show
    print("-" * 14 + str(year_show) + "-" * 14)
    print("Total Sum Netto 2015:   " + str('%.2f' % year))
    print("Total Sum Brutto 2015:  " + str(brutto_total(year)))
    year_show = year_show + 1
    return year_show

for i in os.listdir(os.chdir('path_to_excel_files')):
    if i.endswith(".xlsx"):
        workbook = openpyxl.load_workbook(i, data_only=True)
        sheet = workbook['Blatt1']
        c=sheet['L46'].value
        my_list_2015.append(c)
        my_list_total_2015 = sum(my_list_2015)

for i in os.listdir(os.chdir('path_to_excel_files')):
    if i.endswith(".xlsx"):
        workbook = openpyxl.load_workbook(i, data_only=True)
        sheet = workbook['Blatt1']
        c=sheet['L46'].value
        my_list_2016.append(c)
        my_list_total_2016 = sum(my_list_2016)

for i in os.listdir(os.chdir('path_to_excel_files')):
    if i.endswith(".xlsx"):
        workbook = openpyxl.load_workbook(i, data_only=True)
        sheet = workbook['Zusammen']
        c=sheet['C11'].value
        my_list_2017.append(c)
        my_list_total_2017 = sum(my_list_2017)

show_result(my_list_total_2015)
show_result(my_list_total_2016)
show_result(my_list_total_2017)


286
5
задан 6 февраля 2018 в 08:02 Источник Поделиться
Комментарии
1 ответ

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

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

В show_results функция должна принимать в качестве параметра, но мы можем исправить это позже

Звоню os.chdir наобум просто перечислить некоторые файлы-это немного перебор. Я предполагаю, что вы хотите разрешить единственном числе i переменная быть открыты без использования абсолютных путей, но это может быть опасно, если этот код выполняется в многопоточном контексте (скажем, вы хотите обрабатывать несколько файлов за один раз), как-нить изменить рабочий каталог будет менять их на все, что потенциально нарушая кодом, выполняемым в других потоках.

К счастью, Python предлагает гораздо лучший способ Глоб эти файлы из пути, используя модуль, кстати названа glob. Теперь петля просто

# insert these into the imports
from os.path import join
from glob import glob

for path in glob(join('path_to_excel_files'. '*.xlsx')):

Причина, почему мы используем os.path.join предназначен для кросс-платформенной совместимости. Также старайтесь избегать одного названия письма, если есть более емкое название доступны.

Наконец, sum вызова в каждом цикле может быть сделано сразу после того, как все ценности были собраны, а не каждый цикл, в итоге будет неважно.

Чтобы упростить код, искать детали, которые являются общими, и от той части, что принимают ценности, которые определяют специфику использования. Из трех циклов мы видим, что это просто лист идентификатор в книге, идентификатор ячейки с листа. Это может выглядеть так (Обратите внимание, что у меня нет библиотеки установить, ни файлы Excel, поэтому я не могу точно проверить следующие)

def process_workbooks_in_dir(excel_root, sheet_id, cell_id):
values = []
for path in glob(join(excel_root, '*.xlsx')):
workbook = openpyxl.load_workbook(path, data_only=True)
sheet = workbook[sheet_id]
value = sheet[cell_id].value
values.append(value)

return values

Обратите внимание, что sum можно назвать позже резюмирую список возвращаемых значений, эта функция теперь просто обработать список файлов Excel в базовый путь, и load_workbook функция будет предоставлен абсолютный путь каждого из файлов Excel для обработки. Чтобы использовать это, следующее Может быть сделано.

my_list_2015 = process_workbooks_in_dir('path_to_excel_files', 'Blatt1', 'L46') 
my_list_2016 = process_workbooks_in_dir('path_to_excel_files', 'Blatt1', 'L46')
my_list_2017 = process_workbooks_in_dir('path_to_excel_files', 'Zusammen', 'C11')

my_list_total_2015 = sum(my_list_2015)
my_list_total_2016 = sum(my_list_2016)
my_list_total_2017 = sum(my_list_2017)

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

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


Как есть значительные накладные расходы при Открытие и закрытие файлов (особенно для формата сложная файлов, таких как электронные таблицы Excel), было бы здорово, если это делается только один раз для всех необходимых операций. Давайте рассмотрим для того, чтобы упорядочить поток лучше, так что это может быть сделано. Обратите внимание, что фактический акт открытия/закрытия файлов может быть отсоединен от использования книги, и что книга может быть передан в качестве параметра. Давайте перейдем к этой книге Логика использования отдельной функции:

def read_value_from_workbook(workbook, sheet_id, cell_id):
sheet = workbook[sheet_id]
return sheet[cell_id].value

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

def process_workbooks_by_spec(excel_root, specification):
results = {}
# predefine the results with the year lists as required
for year, sheet_id, cell_id in specification:
results[year] = []

# for the actual work
for path in glob(join(excel_root, '*.xlsx')):
workbook = openpyxl.load_workbook(path, data_only=True)
for year, sheet_id, cell_id in specification:
value = read_value_from_workbook(workbook, sheet_id, cell_id)
results[year].append(value)

return results

Обратите внимание, что Python может распаковать кортежи в несколько переменных, поэтому мы можем есть список кортежей, положить в дружественные имена переменных внутри цикла for, чтобы назначить year, sheet_id и cell_id для использования. Также еще одна вещь, которая может упростить/ликвидации первоначального назначения петли заключается в использовании collections.defaultdict, которые позволили бы сократить код просто:

from collections import defaultdict

def process_workbooks_by_spec(excel_root, specification):
results = defaultdict(list)
for path in glob(join(excel_root, '*.xlsx')):
workbook = openpyxl.load_workbook(path, data_only=True)
for year, sheet_id, cell_id in specification:
value = read_value_from_workbook(workbook, sheet_id, cell_id)
results[year].append(value)

return results

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

spec = [
('2015', 'Blatt1', 'L46'),
('2016', 'Blatt1', 'L46'),
('2017', 'Zusammen', 'C11'),
]

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

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

Теперь, чтобы использовать его:

results = process_workbooks_by_spec('path_to_excel_files', spec)

Доступ results['2015'] теперь должна вернуть список значений извлекается, а можно просто передать результаты в функцию, которая будет обрабатывать это. Рассмотреть вопрос об изменении show_results к этому:

def show_results(results):
for year, values in sorted(results.items()):
result = sum(values)
print("-" * 14 + str(year) + "-" * 14)
print("Total Sum Netto %s: %.2f" % (year, result))
print("Total Sum Brutto %s: %s" % (year, brutto_total(result)))

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

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

from os.path import join
from glob import glob
from collections import defaultdict
import openpyxl

def brutto_total(netto):
return netto + netto * 0.2

def read_value_from_workbook(workbook, sheet_id, cell_id):
sheet = workbook[sheet_id]
return sheet[cell_id].value

def process_workbooks_by_spec(excel_root, specification):
results = defaultdict(list)
for path in glob(join(excel_root, '*.xlsx')):
workbook = openpyxl.load_workbook(path, data_only=True)
for year, sheet_id, cell_id in specification:
value = read_value_from_workbook(workbook, sheet_id, cell_id)
results[year].append(value)

return results

def show_results(results):
for year, values in sorted(results.items()):
result = sum(values)
print("-" * 14 + year + "-" * 14)
print("Total Sum Netto %s: %.2f" % (year, result))
print("Total Sum Brutto %s: %.2f" % (year, brutto_total(result)))

spec = [
('2015', 'Blatt1', 'L46'),
('2016', 'Blatt1', 'L46'),
('2017', 'Zusammen', 'C11'),
]

results = process_workbooks_by_spec('path_to_excel_files', spec)
show_results(results)

Это, как я мог бы сделать это (также напоминание: у меня нет файлов и библиотеки установлены, но это полная версия, по крайней мере, пройти синтаксис проверки); надеюсь, это показывает, как можно использовать богатые библиотеки стандартных функций, доступных в Python, чтобы упростить некоторые задачи, а также думать о том, как можно сломать программу и перестроить его в более понятную, многоразовые формы.

4
ответ дан 7 февраля 2018 в 03:02 Источник Поделиться