Mam funkcję, która przyjmuje zmienną (T) i kilka innych parametrów. Po tym, jak mam to teraz, czuję, że funkcja jest źle napisana i zastanawiam się, czy jest sposób, aby to poprawić. Zasadniczo uważam, że moje użycie * args nie jest tak jasne, jak mogłoby być, ale nie jestem pewien, jak można przejść przez listę parametrów i wykonać obliczenia bez wyraźnego przypisywania zmiennych w procesie indeksowania. Kod działa w tej chwili, ale myślę, że jest lekcja do nauczenia się z użyciem * args dla innych czytelników.

ovi_params = [4.6, 0.10, 27.8, 3.1, 0.368, 0.0052]
egg_params = [7.0, 0.02, 30.1, 4.4, 0.256, 0.0237]
L1_params  = [3.6, 0.1, 29.3, 3.8, 0.240, 0.01082]

def rate_curve(T,*params):
    Tb = params[0][0]
    deltab = params[0][1]
    Tm = params[0][2]
    deltam = params[0][3]
    omega = params[0][4]
    psi = params[0][5]


    # Component 1 
    c1 = math.exp( omega*(T-Tb) )
    # Component 2
    c2 = (Tm - T)/(Tm - Tb)
    # Component 3
    c3 = math.exp( -omega*( (T-Tb)/deltab) )
    # Component 4
    c4 = (T-Tb)/(Tm-Tb)
    # Component 5
    c5 = math.exp( omega*(Tm-Tb) - ((Tm-T)/deltam) )
    # Component 6
    c6 = psi*(c1 - c2*c3 - c4*c5)

    return c6
0
GrayLiterature 19 listopad 2019, 17:19
2
Dlaczego nie możesz po prostu podać parametrów funkcji i wykonać przypisania zmiennych przed wywołaniem funkcji? Myślę, że twoja funkcja wygląda dość przejrzyście, może dodaj ciąg dokumentacyjny dla czytelności, chociaż działałoby to lepiej z osobnymi parametrami niż z bieżącym *params.
 – 
Francisca Concha-Ramírez
19 listopad 2019, 17:23
1
Jeśli chcesz zachować te wartości w jednym pakiecie, gorąco polecam użycie namedtuple.
 – 
0x5453
19 listopad 2019, 17:29
1
Czy jest też powód, dla którego używasz *params zamiast tylko params? Masz dostęp tylko do pierwszego zestawu parametrów na liście, więc równie dobrze możesz po prostu przekazać ten sam.
 – 
0x5453
19 listopad 2019, 17:30

1 odpowiedź

To prawdopodobnie lepiej pasuje do wymiany stosów CodeReview, ale oto moje przemyślenia: problem (jeśli chcesz to tak nazwać) z obecną funkcją polega na tym, że zachowuje się ona także elastycznie, kosztem przejrzystości.

Po pierwsze, zmienne parametrów (ovi, egg i L1) to listy, które sugerują czytelnikowi, że mogą wymagać różnych rozmiarów lub mogą zostać zmodyfikowane przez rate_curve w jakiś sposób (np. wezwanie do pop() lub append()). Jednak tak nie jest, ponieważ znamy je w całości od samego początku. Musisz także przeczytać rate_curve, aby zrozumieć, jakie wartości są w jakiej kolejności na liście. Jak zauważyli komentatorzy, NamedTuple omija te kwestie.

Po drugie, funkcja używa specjalnego parametru funkcji *args. Jak na pewno wiesz, pozwala to funkcji na przyjmowanie zmiennej liczby argumentów. Innymi słowy, możesz zadzwonić rate_curve(t_value), rate_curve(t_value, param1, ..., param20) lub rate_curve(t_value, params). Ponownie, nie daje to użytkownikowi funkcji żadnej wskazówki co do tego, co powinno zostać przekazane, gdy wie dokładnie, co chce przekazać.

Oto dwie opcje, które używają NamedTuple. Wolę drugą i podejrzewam, że być może autor pierwotnej funkcji do niej dążył, a potem trochę się pogubił.

import math
from collections import namedtuple

Parameters = namedtuple("Parameters", "Tb deltab Tm deltam omega psi")
ovi_params = Parameters(4.6, 0.10, 27.8, 3.1, 0.368, 0.0052)
egg_params = Parameters(7.0, 0.02, 30.1, 4.4, 0.256, 0.0237)
L1_params = Parameters(3.6, 0.1, 29.3, 3.8, 0.240, 0.01082)
# if you manually indent whitespace, you have too much time on your hands


def rate_curve(T, params):
    Tb = params.Tb  # use the member variable names
    deltab = params.deltab
    Tm = params[2]  # or stick to using the indices
    deltam = params[3]
    omega = params[4]
    psi = params[5]

    # Component 1
    c1 = math.exp(omega * (T - Tb))
    # Component 2
    c2 = (Tm - T) / (Tm - Tb)
    # Component 3
    c3 = math.exp(-omega * ((T - Tb) / deltab))
    # Component 4
    c4 = (T - Tb) / (Tm - Tb)
    # Component 5
    c5 = math.exp(omega * (Tm - Tb) - ((Tm - T) / deltam))
    # Component 6
    c6 = psi * (c1 - c2 * c3 - c4 * c5)

    return c6


print(rate_curve(1, ovi_params))  # pass in the named tuple
print(rate_curve(1, egg_params))
print(rate_curve(1, L1_params))


def rate_curve2(T, Tb, deltab, Tm, deltam, omega, psi):
    # Component 1
    c1 = math.exp(omega * (T - Tb))
    # Component 2
    c2 = (Tm - T) / (Tm - Tb)
    # Component 3
    c3 = math.exp(-omega * ((T - Tb) / deltab))
    # Component 4
    c4 = (T - Tb) / (Tm - Tb)
    # Component 5
    c5 = math.exp(omega * (Tm - Tb) - ((Tm - T) / deltam))
    # Component 6
    c6 = psi * (c1 - c2 * c3 - c4 * c5)

    return c6


print(rate_curve2(1, *ovi_params))  # expand the named tuple into normal parameters
print(rate_curve2(1, *egg_params))  # it would work with a normal tuple too
print(rate_curve2(1, *L1_params))
0
FiddleStix 19 listopad 2019, 18:45