Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nadużywanie wartości domyślnych parametrów funkcji #3

Open
dybi opened this issue May 10, 2018 · 8 comments
Open

Nadużywanie wartości domyślnych parametrów funkcji #3

dybi opened this issue May 10, 2018 · 8 comments

Comments

@dybi
Copy link

dybi commented May 10, 2018

Oprócz tego, myślę, że warto byłoby napisać o NIE nadawaniu wartości domyślnych wszystkim argumentom w funkcjach. W kodzie mamy sporo funkcji typu:

def weird_fun(param1=None, param2=None):
    assert param1 is not None
    assert isinstance(param2, str)
   # some code 
@dybi dybi changed the title Dodać zasady korzystania z (nienadużywania) parametrów domyślnych w fumkcjach w "python coding style" Dodać zasady korzystania z (nienadużywania) parametrów domyślnych w funkcjach w "python coding style" May 10, 2018
@cameel
Copy link
Member

cameel commented May 10, 2018

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.

@cameel cameel changed the title Dodać zasady korzystania z (nienadużywania) parametrów domyślnych w funkcjach w "python coding style" Nadużywanie wartości domyślnych parametrów funkcji May 10, 2018
@igsj
Copy link

igsj commented May 10, 2018

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.

@cameel
Copy link
Member

cameel commented May 10, 2018

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.

@rwrzesien
Copy link

👍 z tym że

W kodzie mamy sporo funkcji typu:

Nie znalazłem ani jednej.

@dybi
Copy link
Author

dybi commented Jun 12, 2018

@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 ;)

@dybi
Copy link
Author

dybi commented Jun 12, 2018

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

@dybi
Copy link
Author

dybi commented Jun 12, 2018

Ok, propozycja do wiki:

  • Funkcje powinny zawierać wartości domyślne dla argumentów wtedy i tylko wtedy gdy ma to sens, tzn. gdy faktycznie możemy wywołać funkcję bez danego argumentu, bo wartość domyślna "załatwi sprawę".
  • Deklarowany typ argumentu musi być zgodny z wartością domyślną.
  • W przypadku wartości domyślnych None (jakaś wartość może wystąpić lub nie) używamy deklaracji Optional[Typ].

@rwrzesien
Copy link

@dybi 👍

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants