Którego stylu należy użyć dla nieużywanych parametrów zwracanych w wywołaniu funkcji Python

30

Czy istnieje jakiś zalecany / ogólnie akceptowany styl kodowania do obsługi sytuacji, w których funkcja zwraca krotkę wartości, ale później używana jest tylko jedna z tych wartości (zwróć uwagę, że jest to głównie przeznaczone dla funkcji bibliotecznych, których nie mogę zmienić - pisząc opakowanie połączenie jest prawdopodobnie trochę przesadzone…)? Zamiast robić

a, b, c = foo()

a następnie po prostu nie używam bi c, który z poniższych wariantów powinien być preferowany (czy jest inny?):

Wariant 1 (podkreślenie)

a, _, _ = foo()

(co jest bardzo jasne i proste, ale może kolidować z _ = gettext.gettextużywanym w wielu aplikacjach korzystających z tłumaczenia)

Wariant 2 (nazwa manekina)

a, unused, unused = foo()

(myślę, że niezbyt pociągające, to samo dotyczy innych nazw, takich jak dummy)

Wariant 3 (indeks)

a = foo()[0]

(dla mnie ()[0]wygląda to nie pytonicznie…)

użytkownik49643
źródło
W przeciwieństwie do mojej poprzedniej odpowiedzi, co dzieje się z wariantem 3, kiedy chcesz teraz odwoływać się do 2/3 zwracanych wartości? Usunąłem swoją odpowiedź, ponieważ zdałem sobie sprawę, że nie wiem wystarczająco dużo o Pythonie, aby to uzasadnić.
Craige,
@Craige: Nie widziałem twojej odpowiedzi, zanim ją usunąłeś ... Czy pytasz, czy a, b = foo()[0:2]zadziała? Jeśli tak: tak, to robi :)
user49643
Właśnie o to prosiłem. Jeśli to zadziała (tak jak powiedziałeś), użyłbym wariantu 3. Ponownie uruchamiam swoją odpowiedź, biorąc pod uwagę te informacje.
Craige
3
Nawiasem mówiąc, w dzisiejszych czasach możesz użyć dość pięknej składni do „rozszerzonego” rozpakowywania : a, *_ = foo()odrzuci wszystkie wartości oprócz pierwszej.
Benjamin Hodgson
duplikat stackoverflow.com/questions/431866/…
Trevor Boyd Smith

Odpowiedzi:

17

Użycie znaku podkreślenia dla nieużywanych zmiennych jest zdecydowanie dopuszczalne. Ostrzegamy jednak, że w niektórych bazach kodów nie jest to opcja, ponieważ ten identyfikator jest zarezerwowany jako skrót gettext. Jest to najczęstszy sprzeciw wobec tego stylu (choć, jak sądzę, nie stanowi to problemu dla większości). Nadal go polecam i zawsze używam go sam.

Nazwy lubią dummylub unuseddrażnią mnie osobiście, i nie widzę ich zbyt często (w Pythonie, to znaczy - znam bazę kodów Delphi, która używa dummyswobodnie, i przeciekała również do skryptów związanych z danym programem). Odradzałbym ci to.

Wystarczy wyodrębnić jeden element ze zwróconej krotki. Oszczędza to również kłopotów z prawidłową liczbą nieużywanych wartości. Pamiętaj jednak, że ma on dwie potencjalne wady:

  • Nie wysadza w powietrze, jeśli liczba wartości jest inna niż oczekiwana. Może to być przydatne do wykrywania pomyłek i literówek.
  • Działa tylko wtedy, gdy zwracana wartość jest sekwencją (najczęściej krotki i listy, ale bądźmy ogólni). Z drugiej strony, znam jedną klasę w moim kodzie (wektor 2D), która jest iterowalna i daje stałą liczbę wartości (a zatem może być używana w zadaniach rozpakowywania), ale nie jest indeksowalna.

źródło
I czy rzeczywiście wspomnieć gettext w moje pytanie :) W każdym razie, ja przyjąłem odpowiedź - wydaje się, że nie ma wyraźnego pojedynczy Zaakceptowany stylu, ale odpowiedź podniósł kilka interesujących punktów.
user49643,
Aby uniknąć problemów z gettextem (lub aby uniknąć podobnych problemów w tłumaczu), możesz użyć podwójnych znaków podkreślenia (aka dunderów) zamiast jednego.
Zim
21

Pylint nauczył mnie robić to w ten sposób:

widget, _parent, _children = f()

Oznacza to, że nieużywane wyniki mają opisową nazwę poprzedzoną przez _. Pylint uważa miejscowych z przedrostkiem _ za nieużywane, a globale lub atrybuty z przedrostkiem _ jako prywatne.

Vincent Povirk
źródło
3

Jeśli w całym swoim projekcie robisz to tylko raz lub bardzo rzadko, aby napisać konkretną funkcję, użyłbym wariantu 1, jeśli wiesz, że gettextnie jest problemem w tym module, a inaczej wariant 3.

Z drugiej strony, jeśli często to robisz - a zwłaszcza jeśli za każdym razem chcesz innego podzbioru wartości zwracanych (tworzenie opakowania po prostu zwracającego te, na których Ci zależy, to nieopłacalne), może być korzystne napisanie opakowania który umieszcza wyniki w nazwanej krotce lub instancji innej klasy opisowej, co pozwoli ci wykonać:

bar = foo()

A potem pracuj z bar.a, bar.bi bar.c.

lvc
źródło
2

Jak powiedzieli inni, podkreślenie ( _) jest standardem. Ale jeśli do tłumaczenia używa się podkreślenia, myślę, że podwójny podkreślenie jest najlepszą alternatywą.

var, __, value = "VAR=value".partition('=')

Lepsze niż te:

var, unused, value = "VAR=value".partition('=')

var, unused_del, value = "VAR=value".partition('=')

var, _del, value = "VAR=value".partition('=')

Rick Mohr
źródło
1

Jestem programistą niebędącym Pythonem, ale dla mnie trzeci wariant jest najbardziej sensowny.

W wariancie 3 masz absolutną jasność, z jakimi wartościami chcesz się zmierzyć. W wariancie 1 i 2 wartości są przypisywane z powrotem do zmiennych, dzięki czemu można ich użyć. Być może nazwałeś je niejasno, ale złe nazewnictwo nie jest tak naprawdę rozwiązaniem żadnego problemu.

Oprócz przejrzystości, dlaczego chcesz przypisać nieużywane wartości do gniazda w pamięci (jak w wariantach 1 i 2)? To byłoby złe rozwiązanie w zakresie zarządzania pamięcią.

Craige
źródło
Jeśli zawsze umieszczasz ją na tej samej zmiennej, czy problem pamięci nie byłby w danym momencie tylko wielkości jednej zmiennej? Nie sądzę, że powinien to być problem.
Michael McQuade
-2

Oto ogólna zasada: jeśli zostanie użyta tylko 1 ze zwróconych wartości, dlaczego po prostu nie zwrócić tej 1 wartości? W przypadku, gdy flaga jest wywoływana z wielu miejsc, zachowaj flagę dla tej samej wartości i zwróć wartości zgodnie z flagą.

EDYTOWAĆ:

Gdybym był w twojej sytuacji i nie napisałem funkcji, być może wybrałbym wariant 2. Zachowałbym tam nazwy manekinów, aby w razie potrzeby móc użyć parametrów. Krojenie listy będzie trochę kosztowne. Być może możesz zaoszczędzić kilka bajtów, aby otrzymać niechciane dane.

c0da
źródło
-1 Zgaduję, że nie zdefiniował funkcji i dlatego nie może zmienić tego, co ona zwraca. Myślę, że odnosi się do tego, kiedy funkcja zwraca 3 wartości, ale obchodzi go tylko około 1 w bieżącym przypadku użycia.
Craige,
@Craige: Tak, miałem na myśli funkcje, których nie mogłem zmienić - dodałem notatkę do mojego pytania wyjaśniającą to.
user49643
Czy jesteś pewien kosztów ogólnych? Próbowałem bardzo prostego testu ipython: w %timeit a, _, _ = foo()porównaniu %timeit a = foo()[0]do 123ns vs. 109ns, tzn. Krojenie wydaje się być rzeczywiście szybsze niż rozpakowywanie wartości. A może miałeś na myśli jakąś konkretną sytuację?
user49643,
2
Innym możliwym scenariuszem jest to, że nie potrzebujesz wszystkich wartości dla niektórych połączeń.
Keith Thompson