Open dybi opened 6 years ago
Taka drobna meta-uwaga: utworzyłem labele dla poszczególnych narzędzi (python, shell, git, markdown) i części naszego guide'a (coding style, workflow), więc nie trzeba tego pisać w nazwie issue. Korzystajmy z tych labeli.
Jeśli ktoś definiuje def fun(param=None)..
mówiąc przez to brak param jest ok i może być None i zaraz potem dodaje assert param is not None
to w Coding Style przyda się sugestia, żeby wyjść na krótki spacer od czasu do czasu. Bo chyba dodanie reguły Nie pisz kodu, który sam sobie zaprzecza w dwóch kolejnych linijkach wydaje się mało empatyczne.
Hehe :), no ten przykład jest na tyle ekstremalny że można by go uznać za buga.
Ale ogólnie moim zdaniem jest wiele mniej oczywistych sytuacji i istnieje tendencja by na siłę dawać domyślną wartość dla wygody. Według mnie reguła powinna mówić, że unikamy nadawania wartości domyślnej tylko dlatego, że jakaś wartość parametru jest częściej używana niż inna i nie chce nam się jej ciągle pisać. Nadajemy je raczej w sytuacjach gdy dany parametr jest po prostu opcjonalny. Stąd też wynika to zalecenie, które już tam jest - żeby unikać wartości domyślnych innych niż pusty string czy None
. Bo kiedy parametr jest opcjonalny to zwykle pomijamy go ustawiając None
.
:+1: z tym że
W kodzie mamy sporo funkcji typu:
Nie znalazłem ani jednej.
@rwrzesien , nie dokładnie takich funkcji, ale tego typu ;)
Żeby nie szukać daleko:
def update_subtask(
subtask: Subtask,
state: Subtask.SubtaskState,
next_deadline: int = None,
set_next_deadline: bool = False,
task_to_compute: message.TaskToCompute = None,
report_computed_task: message.ReportComputedTask = None,
ack_report_computed_task: message.AckReportComputedTask = None,
reject_report_computed_task: message.RejectReportComputedTask = None,
subtask_results_accepted: message.tasks.SubtaskResultsAccepted = None,
subtask_results_rejected: message.tasks.SubtaskResultsRejected = None,
):
"""
Validates and updates subtask and its data.
Stores related messages in StoredMessage table and adds relation to newly created subtask.
"""
assert isinstance(subtask, Subtask)
assert state in Subtask.SubtaskState
assert (state in Subtask.ACTIVE_STATES) == (next_deadline is not None)
assert (state in Subtask.PASSIVE_STATES) == (next_deadline is None)
next_deadline
jest deklarowany jako int
i przypisywana jest mu wartość domyślna None
.
Podobnie:
def store_or_update_subtask(
task_id: str,
subtask_id: str,
provider_public_key: bytes,
requestor_public_key: bytes,
state: Subtask.SubtaskState,
next_deadline: int = None,
set_next_deadline: bool = False,
task_to_compute: message.TaskToCompute = None,
report_computed_task: message.ReportComputedTask = None,
ack_report_computed_task: message.AckReportComputedTask = None,
reject_report_computed_task: message.RejectReportComputedTask = None,
subtask_results_accepted: message.tasks.SubtaskResultsAccepted = None,
subtask_results_rejected: message.tasks.SubtaskResultsRejected = None,
):
Messsage są zadeklarowane jako obiekty odpowiednich klas, a przypisywana jest im wartość domyślna None
.
Wniosek: Nie korzystamy w kodzie z adnotacji Optional[some_type]
i warto byłoby to zmienić ;)
EDIT: A dokłądnie takie widziałem w niektórych naszych concentowych PR-ach ;)
Kolejnym przykładem na nadużywanie wartości domyślnych jest funkcja:
def store_pending_message(
response_type = None,
client_public_key = None,
queue = None,
subtask = None,
payment_message = None,
):
Wszystkie jej argumenty są domyślnie Nonem
, a we wszystkich jej wywołaniach przekazywane są parametry: response_type
, queue
i client_public_key
Ok, propozycja do wiki:
None
(jakaś wartość może wystąpić lub nie) używamy deklaracji Optional[Typ]
.@dybi :+1:
store_pending_message
- tutaj z tego co pamiętam na początku nie wszędzie były, później zaczeliśmy uzupełniać.
update_subtask
i store_or_update_subtask
- tutaj akurat te asserty mają sens, ponieważ next_deadline może być None tylko dla pewnych stanów Subtaska.