Так случилось, что довольно давно я работаю со "страшно дорогой 1С-кой немецкого происхождения"(с) по имени SAP, которая написана на ABAP.
Поэтому, можно сказать, что по долгу службы, для меня это основной язык.
Но как-то решил я выучить какой-нибудь еще язык для своих рутинных консовских задач (сверок/сводок и т.д.).
Выбор пал на Python. В этих ваших интернетах он позиционировался как один из самых популярных языков в силу своей простоты и большого количества библиотек (по данным интернетов).
После прохождения одного из базовых курсов, который знакомил с синтаксисом, я придумал себе относительно типовую задачу и начал ее делать.
Дальше речь пойдет о частностях, поэтому коротко напишу, что задача (pet project) состоит в следующем: есть отчет для сдачи в госорганы в формате xml, есть проверочные данные в excel. Нужно выбрать файлы через GUI и их сравнить.
Если более общо, то задача состояла в том, чтобы научиться переводить с abap на python.
Для GUI был tkinter, для xml - xml.etree, для остального - pandas.
Ожидаемо, возник нюанс.
В ABAP для большинства задач, workhorse - это loop, read, sort внутренних таблиц по различным условиям.
И как правило, для задания условий используются имена колонок. И к этой опции (обращение по именам колонок) очень быстро привыкаешь.
После базового курса, я, с огромным удивлением, не обнаружил каких-то базовых вещей которые позволяли бы организовать цикл по внутренней таблице и обращаться к содержимому строки по именам полей, а не по какому-нить индексу.
Для понимания. Я пытался перевести на Python вот такой ABAP-код.
TYPES: BEGIN OF ts_type,
item TYPE c LENGTH 10,
number TYPE i,
price TYPE d,
END OF ts_type,
tt_type TYPE TABLE OF ts_type WITH EMPTY KEY.
DATA(lt) = VALUE tt_type( ( item = 'item1' number = 95 price = 100 )
( item = 'item2' number = 85 price = 200 )
( item = 'item3' number = 75 price = 300 )
( item = 'item4' number = 65 price = 400 )
( item = 'item5' number = 55 price = 500 )
( item = 'item6' number = 45 price = 600 )
( item = 'item7' number = 35 price = 700 )
( item = 'item8' number = 25 price = 800 )
( item = 'item9' number = 15 price = 900 )
).
LOOP AT lt ASSIGNING FIELD-SYMBOL(<fs>) WHERE item EQ 'item2' OR ( number GE 30 AND price LE 300 ).
WRITE:/ <fs>-item.
<fs>-price = 999.
ENDLOOP.
Основное, конечно, это найти аналог вот этому:
LOOP AT lt ASSIGNING FIELD-SYMBOL(<fs>) WHERE item EQ 'item2' OR ( number GE 30 AND price LE 300 ).
WRITE:/ <fs>-item.
<fs>-price = 999.
ENDLOOP.
Для перевода куска выше (а он для меня был очень важен т.к. в моей голове, это workhorse для моих задач), я вышел в интернет(с). И почему-то не обнаружил искомого на поверхности. Может плохо искал. Собственно, поэтому и пишу этот пост.
Итого.
Дано: нужно пробежаться только по определенным строкам внутренней таблицы (без for ... if...), обращаться нужно по именам полей, содержимое таблицы нужно менять.
Больше всего хотелось оперировать именами полей. Ошень удобно...
namedtuple. Организация "таблицы" с полями "+", обращение по имени "+", поиск по условиям "-", изменение данных "-".
classdata. Либо я не понял, либо это аналог структуры(очень условно).
pandas dataframe. Вроде подошел.
import pandas as pd
df = pd.DataFrame(columns=['ITEM', 'NUMBER', 'PRICE'])
sample = [pd.Series(['item1', 95, 100], index=df.columns),
pd.Series(['item2', 85, 200], index=df.columns),
pd.Series(['item3', 75, 300], index=df.columns),
pd.Series(['item4', 65, 400], index=df.columns),
pd.Series(['item5', 55, 500], index=df.columns),
pd.Series(['item6', 45, 600], index=df.columns),
pd.Series(['item7', 35, 700], index=df.columns),
pd.Series(['item8', 25, 800], index=df.columns),
pd.Series(['item9', 15, 900], index=df.columns)
]
df = df.append(sample, ignore_index=True)
print('Data frame')
print(df)
print('Loop with multiply conditions')
for ind, line in (df.loc[
(df['ITEM'] == 'item2') |
((df['NUMBER'] >= 30) & (df['PRICE'] <= 300))
]).iterrows():
print(line['ITEM'])
df.at[ind, 'PRICE'] = 999
print('Modified data frame')
print(df)
Modified data frame
ITEM NUMBER PRICE
0 item1 95 999
1 item2 85 999
2 item3 75 999
3 item4 65 400
4 item5 55 500
5 item6 45 600
6 item7 35 700
7 item8 25 800
8 item9 15 900
Формирование первоначального df сделано с помощью Series чтобы можно было дальше добавлять новые строки. Иначе у меня не получалось(сформировать df, потом еще какой-то кусок и добавить его к первоначальному df).
P.S. Комментарии по оптимизации сильно приветствуются. Может чукча(который не сварщик) чего-то не нашел, хотя старался.