введение таково:
Вы являетесь членом команды разработчиков биотехнологий, которая отвечает за создание системы для лаборантов, которая поможет им с анализом лекарств.
Ваша цель — создать приложение, которое позволит им вводить свои выводы в систему, предоставлять значимый анализ и проверять правильность отправленных данных.
задания:
Ваша цель в этой части — реализовать
app.drug_analyzer.DrugAnalyzer
учебный класс. Он будет отвечать за анализ данных, таких как данные, представленные ниже:+-----------+-------------+------------------+-------------+ | pill_id | pill_weight | active_substance | impurities | +-----------+-------------|------------------|-------------| | L01-10 | 1007.67 | 102.88 | 1.00100 | | L01-06 | 996.42 | 99.68 | 2.00087 | | G02-03 | 1111.95 | 125.04 | 3.00004 | | G03-06 | 989.01 | 119.00 | 4.00062 | +-----------+-------------+-------------+-------------+-----
Инициализация класса может быть выполнена из списка списков Python (или ничего) и сохранена в переменной экземпляра с именем data, как показано в примере ниже:
>>> my_drug_data = [ ... ['L01-10', 1007.67, 102.88, 1.00100], ... ['L01-06', 996.42, 99.68, 2.00087], ... ['G02-03', 1111.95, 125.04, 3.00100], ... ['G03-06', 989.01, 119.00, 4.00004] ... ] >>> my_analyzer = DrugAnalyzer(my_drug_data) >>> my_analyzer.data [['L01-10', 1007.67, 102.88, 0.001], ['L01-06', 996.42, 99.68, 0.00087], > ['G02-03', 1111.95, 125.04, 0.00100], ['G03-06', 989.01, 119.00, 0.00004]] >>> DrugAnalyzer().data []
У класса также должна быть возможность добавлять отдельные списки в объект. Добавление списка в
DrugAnalyzer
объект должен вернуть новый экземпляр этого объекта с дополнительным элементом. Добавление неправильного типа или списка неправильной длины должно вызватьValueError
. Пример правильного и неправильного вывода сложения показан ниже:>>> my_new_analyzer = my_analyzer + ['G03-01', 789.01, 129.00, 0.00008] >>> my_new_analyzer.data [['L01-10', 1007.67, 102.88, 0.001], ['L01-06', 996.42, 99.68, 0.00087], > ['G02-03', 1111.95, 125.04, 0.00100], ['G03-06', 989.01, 119.00, 0.00004], ['G03-01', 789.01, 129.00, 0.00008]] >>> my_new_analyzer = my_analyzer + ['G03-01', 129.00, 0.00008] Traceback (the most recent call is displayed as the last one): File "<stdin>", line 1, in <module> ValueError: Improper length of the added list.
Часть 2
Реализовать
verify_series
метод внутриapp.drug_analyzer.DrugAnalyzer
учебный класс.Цель этого метода — получить список параметров и использовать их для проверки того, соответствуют ли таблетки, описанные в данных переменных экземпляра, заданным критериям. В результате он должен вернуть логическое значение.
Функция будет вызываться следующим образом:
verify_series(series_id = 'L01', act_subst_wgt = 100, act_subst_rate = 0.05, allowed_imp = 0.001)
Где:
- в
series_id
представляет собой строку длиной 3 символа, которая присутствует в начале каждогоpill_id
, перед знаком -; Например,L01
этоseries_id
вpill_id = L01-12
.- в
act_subst_wgt
— ожидаемая масса (мг) содержания активного вещества в данной серии в одной таблетке.- в
act_subst_rate
— допустимая величина отклонения массы действующего вещества от ожидаемой. Например, для 100 мг допустимые значения будут от 95 до 105.- в
allowed_imp
допустимая норма нечистых веществ вpill_weight
. Например, на 1000 мгpill_weight
и 0,001 нормы, допустимое количество примесей — 1 мг.Функция должна принимать все таблетки, входящие в состав
L01
серии, просуммируйте их вес и вычислите, если количествоactive_substance
, а также примеси соответствуют заданным показателям. Он должен вернутьсяTrue
если оба условия соблюдены иFalse
если какой-либо из них не выполняется.В
False
Результат должен означать, что все переданные параметры верны, но либо количество активного вещества, либо количество примесей неправильное. В случае, если series_id вообще отсутствует в данных или в случае какого-либо неправильного параметра, функция должна выдатьValueError
. Пожалуйста, подумайте, какой может быть крайний случай в таком сценарии.Пример:
>>> my_drug_data = [ ... ['L01-10', 1000.02, 102.88, 1.00100], ... ['L01-06', 999.90, 96.00, 2.00087], ... ['G02-03', 1000, 96.50, 3.00100], ... ['G03-06', 989.01, 119.00, 4.00004] ... ] >>> my_analyzer = DrugAnalyzer(my_drug_data) >>> my_analyzer.verify_series(series_id = 'L01', act_subst_wgt = 100, act_subst_rate = 0.05, allowed_imp = 0.001) False >>> // The overall active_substances weight would be 198.88, which is within the given rate of 0.05 for 200 mg (2 * act_subst_wgt). >>> // However, the sum of impurities would be 3.00187, which is more than 0.001*1999.92 (allowed_imp_rate * (1000.02 + 999.90). >>> my_analyzer.verify_series(series_id = 'L01', act_subst_wgt = 100, act_subst_rate = 0.05, allowed_imp = 0.0001) True >>> my_analyzer.verify_series(series_id = 'B03', act_subst_wgt = 100, act_subst_rate = 0.05, allowed_imp = 0.001) Traceback (the most recent call is displayed as the last one): File "<stdin>", line 1, in <module> ValueError: B03 series is not present within the dataset.
мой код прошел все тесты
После моей первой неудачи я попытался сделать код как можно более компактным и легким, используя понимание списков и исключая ненужные переменные, хотя это увеличило мою оценку всего на 5%. Может ли кто-нибудь сказать мне, что не так с моим кодом и как я могу написать лучший код?
мой последний код
(оценка: 58%)
class DrugAnalyzer:
def __init__(self, data):
self.data = data
def __add__(self, data):
if len(data) == 4:
if all(isinstance(i, float) for i in data[1:]) and isinstance(data[0], str):
self.data = self.data + [data]
return self
else:
raise ValueError('Improper type on list added')
else:
raise ValueError('improper length on list added')
def verify_series(
self,
series_id: str,
act_subst_wgt: float,
act_subst_rate: float,
allowed_imp: float,
) -> bool:
pills = [pill for pill in self.data if series_id in pill[0]]
if pills:
return act_subst_wgt*len(pills)-(act_subst_wgt * len(pills) * act_subst_rate) < sum([i[2] for i in pills]) < act_subst_wgt*len(pills)+(act_subst_wgt * len(pills) * act_subst_rate) and sum([i[3] for i in pills]) < allowed_imp*sum([i[1] for i in pills])
else:
raise ValueError(f'There is no {series_id} series in database')
Предыдущая версия
(оценка: 55%)
class DrugAnalyzer():
def __init__(self,data):
self.data = data
def __add__(self,data1):
if len(data1) < 4:
raise ValueError('improper lenght of list added')
if type(data1[0]) == str:
if type(data1[1]) and type(data1[2]) and type(data1[3]) == float:
self.data = self.data + [data1]
return self
else:
raise ValueError('Improper type on list added')
else: raise ValueError('Improper type on list added')
def verify_series(
self,
series_id: str,
act_subst_wgt: float,
act_subst_rate: float,
allowed_imp: float,
) -> bool:
serie = []
for pill in self.data:
if series_id in pill[0]:
serie.append(pill)
active = []
imp = []
weight = []
print(serie)
for pill in serie:
weight.append(pill[1])
active.append(pill[2])
imp.append(pill[3])
if serie == 0:
raise ValueError('the serie isnt in list')
dif = (act_subst_wgt*len(serie))*act_subst_rate
if act_subst_wgt*len(serie)-dif < sum(active) < act_subst_wgt*len(serie)+dif and sum(imp) < allowed_imp*sum(weight):
return True
else:
return False
2 ответа
Я не знаю, как оценивается код, но плоский лучше, чем вложенный. Если вы хотите вызвать исключение, сделайте это заранее. Рассматривать
def __add__(self, data):
if len(data) != 4:
raise ValueError('improper length on list added')
if not all(isinstance(i, float) for i in data[1:]) or not isinstance(data[0], str):
raise ValueError('Improper type on list added')
self.data = self.data + [data]
return self
Мне тоже непонятно, почему вы настаиваете на data[1:]
существование float
. Fraction
будет работать отлично, как и многие другие типы, пока +
и <
определены.
series_id
представляет собой строку длиной 3 символа, которая присутствует в начале каждогоpill_id
, перед-
знак
Я не понимаю, как это требование реализовано.
act_subst_wgt*len(pills)-(act_subst_wgt * len(pills) * act_subst_rate) < sum([i[2] for i in pills]) < act_subst_wgt*len(pills)+(act_subst_wgt * len(pills) * act_subst_rate) and sum([i[3] for i in pills]) < allowed_imp*sum([i[1] for i in pills])
совершенно нечитаемо.
Я не знаю, как рассчитываются эти оценки, поэтому не могу сказать, что могло бы улучшить ваш результат. Ваш код выглядит как нормальная реализация спецификации с два три детали «неправильные».
- В спецификации не сказано, что операция добавления должна изменять
self
, это фактически означает, что операция должна нет модифицироватьself
. «Добавление списка к объекту DrugAnalyzer должно вернуть новый экземпляр этого объекта с дополнительным элементом». Это согласуется с тем, как мы обычно думаем о+
;5+1
возвращается6
, но это не так изменять 5. if series_id in pill[0]
будет истинным для'L01'
и'G02-L01'
; ты хочешьstartswith
. (Обратите внимание, что это метод большую строку.)- Вы не учли пустой вызов конструктора.
Другие вещи:
- Вы используете подсказки типа; Замечательно! Убедитесь, что вы подтверждаете их с помощью MyPy, и используйте их для всего.
- пикодестиль поможет сохранить стандартное форматирование. Это довольно строго; Я не всегда им пользуюсь. Но ваша линия-23 действительно плохая!
- Я не могу себе представить, чтобы проверка Только происходит при добавлении к существующему объекту; он должен быть централизованным. Может быть, он может быть централизован в своем собственном классе? Таким образом у вас может быть согласованный «список объектов строк» вместо менее надежного «списка списков». Однако потребуется дополнительное оборудование для преобразования … В любом случае, я бы использовал классы данных за это.
- Сам код валидации, на мой взгляд, можно немного подправить.
- В спецификации говорится «… функция должна выдать ошибку ValueError. Подумайте, какой может быть крайний случай в таком сценарии».
Может, они имеют в виду отрицательные числа или что-то в этом роде; Я НЕ ЗНАЮ? Кто бы ни создавал этот API, плохо справляется, но это не в ваших руках! - Многие функции (например,
sum
) может принимать голые генераторы вместо списка. - Я думаю, что для проверки неравенств вы должны технически использовать версии «или равно», но я очень надеюсь, что это не имеет значения!
IDK, насколько полезной будет эта версия для вас. Если вы запустите его, вы обнаружите, что утверждения в конце не проходят; они основаны на примерах вывода оболочки в вашем вопросе, которые, кажется, имеют неправильные значения в последнем столбце вывода? Это может быть действительно важная деталь, которую вы упустили, но у меня нет времени. Сожалею!
import collections.abc as c # these are _classes_, not _types_.
from dataclasses import dataclass
from numbers import Real # again, a class, for typing we'll use float.
from typing import Any, Iterable, Sequence, List # these are _types_.
# You can just use List for everything; I like to split hairs.
@dataclass(frozen=True) # I think they should be frozen by default.
class PillRow:
pill_id: str
series: str
weight: float
active: float
impure: float
@staticmethod
def from_data(data: Any) -> "PillRow":
if isinstance(data, PillRow):
return data
try:
assert isinstance(data, c.Sized) and isinstance(data, c.Iterable), ("Could not parse data.", data)
assert 4 == len(data), f"Four columns needed; found {len(data)}."
pill_id, pill_weight, active_substance, impurities = data
assert isinstance(pill_id, str), ("Column One must be the pill id.", pill_id)
series_id = pill_id.split('-')[0]
assert 3 == len(series_id), ("Could not parse series id from the pill id.", pill_id)
assert isinstance(pill_weight, Real), ("Invalid pill weight.", pill_weight)
assert isinstance(active_substance, Real), ("Invalid active-substance weight.", active_substance)
assert isinstance(impurities, Real), ("Invalid impurities weight.", impurities)
return PillRow(pill_id=pill_id,
series=series_id,
weight=float(pill_weight),
active=float(active_substance),
impure=float(impurities))
except AssertionError as ae:
raise ValueError(ae)
def to_list(self) -> list:
return [self.pill_id, self.weight, self.active, self.impure]
class DrugAnalyzer:
def __init__(self, *datas: Iterable[Sequence]): # taking *args will make __add__ easier to write.
self._data = [PillRow.from_data(pill)
for data in datas
for pill in data]
self.data = [pill.to_list() for pill in self._data]
def __add__(self, data):
return DataAnalyzer(self._data, [data]) # that's what all the above work was for!
def verify_series(self,
series_id: str,
act_subst_wgt: float,
act_subst_rate: float,
allowed_imp: float) -> bool:
pills = [p for p in self._data if p.series == series_id]
if not pills:
raise ValueError(f'There is no {series_id} series in database')
else:
num_pills = len(pills)
target_active = act_subst_wgt * num_pills
margin_active = target_active * act_subst_rate
actual_active = sum(p.active for p in pills)
max_impure = allowed_imp * sum(p.weight for p in pills)
actual_impure = sum(p.impure for p in pills)
return (abs(target_active - actual_active) <= margin_active) and (actual_impure <= max_impure)
if __name__ == "__main__":
# tests constructors
my_drug_data = [
['L01-10', 1007.67, 102.88, 1.00100],
['L01-06', 996.42, 99.68, 2.00087],
['G02-03', 1111.95, 125.04, 3.00100],
['G03-06', 989.01, 119.00, 4.00004]
]
my_analyzer = DrugAnalyzer(my_drug_data)
assert tuple(map(tuple, my_analyzer.data)) == (
('L01-10', 1007.67, 102.88, 0.001),
('L01-06', 996.42, 99.68, 0.00087),
('G02-03', 1111.95, 125.04, 0.00100),
('G03-06', 989.01, 119.00, 0.00004)
), tuple(map(tuple, my_analyzer.data))
assert tuple(DrugAnalyzer().data) == (), DrugAnalyzer().data
# tests plus
my_new_analyzer = my_analyzer + ['G03-01', 789.01, 129.00, 0.00008]
assert tuple(map(tuple, my_new_analyzer.data)) == (
('L01-10', 1007.67, 102.88, 0.001),
('L01-06', 996.42, 99.68, 0.00087),
('G02-03', 1111.95, 125.04, 0.00100),
('G03-06', 989.01, 119.00, 0.00004),
('G03-01', 789.01, 129.00, 0.00008)
), my_new_analyzer.data
thrown = False
try:
my_new_analyzer = my_analyzer + ['G03-01', 129.00, 0.00008]
except ValueError:
thrown = True
assert thrown, my_new_analyzer
# tests verify
my_drug_data = [['L01-10', 1000.02, 102.88, 1.00100],
['L01-06', 999.90, 96.00, 2.00087],
['G02-03', 1000, 96.50, 3.00100],
['G03-06', 989.01, 119.00, 4.00004]]
my_analyzer = DrugAnalyzer(my_drug_data)
assert not my_analyzer.verify_series(series_id='L01',
act_subst_wgt=100,
act_subst_rate=0.05,
allowed_imp=0.001)
assert my_analyzer.verify_series(series_id='L01',
act_subst_wgt=100,
act_subst_rate=0.05,
allowed_imp=0.0001)
thrown = False
try:
my_analyzer.verify_series(series_id='B03',
act_subst_wgt=100,
act_subst_rate=0.05,
allowed_imp=0.001)
except ValueError:
thrown = True
assert thrown
```
Утверждение, улавливание утверждения и повторное повышение как
ValueError
разбивать исходную стопку … не здорово? Лучше бы вам заменить утверждения отдельными повышениями вашего собственного типа ошибки. Даже если вы сохраните свой текущий паттерн утверждение-ререйз, вы, возможно, захотитеraise ValueError(str(ae)) from ae
чтобы сохранить исходную стопку.— Райндериен
Это нет здорово. Спецификация запрашивает конкретный тип ошибки, и я действительно думаю, что нужно иметь отдельный
if...raise...
по каждой причине слишком многословен. Утверждение / ловля / повторный рейз плохо пахнет, но дает понять суть. С использованиемraise...from
сохраняет некоторую дополнительную информацию, что хорошо, но в данном случае это действительно затрудняет чтение сообщений об ошибках, поэтому я чувствую себя противоречивым. В идеале / интуитивно понятно, что предложение об ошибкеassert...
может быть фактическим исключением, которое вы хотите вызвать, но python этого не делает 🙁— ShapeOfMatter
Я полагаю, что всегда есть опасность, что кто-то запустит это с
-O
установлен флаг …— ShapeOfMatter
Ваше второе замечание о
float
можно улучшить, сказав использоватьnumbers.Real
вместо этого вisinstance
чек об оплате.— Пейлонрайз♦