Closed yakovistomin closed 4 years ago
Hello. Есть понимание какие классы должны быть? Хочу попробовать реализовать.
Я думал сделать это по аналогии с ChoiceProcessor.
1) Есть необязательный параметр raw_value_processor (в данном случае raw_value_processor=lambda x: x) Этот обработчик отвечает за обработку сырых значений и приведение их к определенному типу
Пример без указания raw_value_processor:
processor = ClassifierProcessor(
choices={
'd': lambda x: x in ['test', 'test2'],
'a': lambda x: 0 <= x <= 10,
'b': lambda x: 10 <= x <= 100,
'c': lambda x: isinstance(x, str),
})
assert processor(3) == 'a'
assert processor('4') == 'c'
with pytest.raises(ColumnError):
processor(-10)
assert processor('test') == 'd'
Пример с указанием raw_value_processor для int:
processor = ClassifierProcessor(
choices={
'a': lambda x: 0 <= x <= 10,
'b': lambda x: 10 <= x <= 100,
},
raw_value_processor=IntegerProcessor(),
)
assert processor(3) == 'a'
assert processor('4') == 'a'
with pytest.raises(ColumnError):
processor(-10)
Пример с указанием raw_value_processor для datetime:
processor = ClassifierProcessor(
choices={
'a': lambda x: datetime.date(2020, 1, 1) <= x <= datetime.date(2020, 12, 31),
'b': lambda x: datetime.date(2021, 1, 1) <= x <= datetime.date(2021, 12, 31),
},
raw_value_processor=DateProcessor(),
)
assert processor('2020-01-01') == 'a'
assert processor('2021-01-01') == 'b'
with pytest.raises(ColumnError):
processor('2022-01-01')
2) Как должно работать - в process_value перебираем a) choice_value, func for value, func in choices.items() b) вызываем func(value), если функция вернула True - process_value возвращает choice_value, если функция вернула False или исключение, то пробуем следующую пару с) если перебрали все choices, но ни одна из функций не вернула True, то raise ColumnError('unknown value')
Как бы мог выглядеть process_value
class ClassifierProcessor(...):
def process_value(self, value):
value = self.raw_value_processor(value)
for item, func in self.choices.items():
try:
if func(value):
return item
except Exception:
pass
raise ColumnError('unknown value')
1 - raw_value_processor по умолчанию - lambda x: x (ChoiceProcessor нужно переделать, чтобы по умолчанию было так же)
- Да, можно подставить любой существующий или написанный отдельно. Я в примерах указал существующие
- Да. Я думал сделать именно так. Есть идеи это сделать как-то логичнее?
@dumbturtle @Melevir я вот еще подумал, что может быть нам не достаточно просто словаря choices, где key - это значение после классификации.
Может лучше как-то так определять обработчик:
processor = ClassifierProcessor(
choices=[
['a', lambda x: x < 10],
['b', lambda x: x < 100],
['a', lambda x: x < 1000],
['b', lambda x: x < 10000],
],
)
assert processor(1) == 'a'
assert processor(11) == 'b'
assert processor(101) == 'a'
assert processor(1001) == 'b'
таким образом можно было бы вообще любую логику описать
Тогда можно было бы вообще универсальный ChoiceProcessor сделать
processor = ChoiceProcessor(
choices=[
['A', 'a'],
['B', 'a'],
[lambda x: x < 10, 'a'],
[lambda x: x < 100, 'b'],
[lambda x: x < 1000, 'a'],
[lambda x: x < 10000, 'b'],
],
)
assert processor('A') == 'a'
assert processor('B') == 'b'
assert processor(1) == 'a'
assert processor(11) == 'b'
assert processor(101) == 'a'
assert processor(1001) == 'b'
а в коде просто перебирать choices и смотреть callable item[0] или нет.
def process_value(self, value):
...
for item in self.choices:
if callable(item[0]) and item[0](value):
return item[1]
elif item[0] == value:
return item[1]
raise ColumnError('unknown value')
Да, по мне со списком лучше. Можно начать с этого, потом дописать проверку, чтобы не повторять lamba и в choices указывать только услования.
Добавить обработчик, позволяющий классифицировать данные
Пример: