2015-04-25 36 views
11

Chciałbym napisać funkcję w Pythonie, która przyjmuje segment jako parametr. Idealnie użytkownik będzie w stanie wywołać funkcję w następujący sposób:Jak napisać funkcję, która zajmuje plasterek?

foo(a:b:c) 

Niestety, ta składnia nie jest dozwolone przez Python - wykorzystanie a:b:c jest dozwolone tylko w [] nie ().

dlatego widzę trzy możliwości mojej funkcji:

  1. wymagać od użytkownika, aby użyć slice "konstruktor" (gdzie s_ działa jak the version provided by numpy):

    foo(slice(a, b, c)) 
    foo(s_[a:b:c]) 
    
  2. umieścić logikę moja funkcja w metodzie __getitem__:

    foo[a:b:c] 
    
  3. Porzuć próbuje wziąć kawałek i wziąć start, stop i krok indywidualnie:

    foo(a, b, c) 
    

Czy istnieje sposób, aby uzyskać oryginalny składnia do pracy? Jeśli nie, to która z metod składniowych byłaby preferowana? Czy jest jeszcze inna, lepsza opcja?

+3

użyłbym dzięki start, stop, i krok ustawienie domyślne wartości –

+2

W jaki sposób opiera się to głównie na opiniach? To jest całkowicie poprawne pytanie. –

+0

Nie widzę żadnej wady użycia 'foo (a, b, c)' zamiast 'foo (a: b: c)'. – TigerhawkT3

Odpowiedz

10

Nie zaskakuj swoich użytkowników.

Jeśli używasz składni krojenia zgodnie z tym, czego programista oczekuje od składni podziału, ten sam programista będzie oczekiwał operacji w nawiasach kwadratowych, tj. Metody __getitem__().

Jeśli zamiast tego zwracany obiekt nie jest jakoś plastrem oryginalnego obiektu, ludzie będą zdezorientowani, jeśli pozostaniemy przy rozwiązaniu __getitem__(). Użyj wywołania funkcji foo(a, b, c), nie wymieniaj w ogóle plasterków i opcjonalnie przypisz wartości domyślne, jeśli ma to sens.

0

Użycie [a:b:c] jest, jak zauważasz, rzeczą składniową. Interpreter od razu podnosi syntax error dla (a:b:c), zanim twój kod będzie miał szansę coś zrobić z tymi wartościami. Nie ma sposobu obejścia tej składni bez przepisywania interpretera.

Warto pamiętając, że tłumacz przekłada foo[a:b:c] do

foo.__getitem__(slice(a,b,c)) 

Sam obiekt slice nie jest bardzo skomplikowane. Ma po prostu 3 atrybuty (start, step, stop) i metodę indices. Jest to metoda getitem, która ma sens tych wartości.

np.s_ i inne funkcje/klas w np.lib.index_tricks są dobrymi przykładami jak __getitem__ i slice może być stosowany w celu rozszerzenia (lub uproszczenie) indeksowanie. Na przykład, są równoważne:

np.r_[3:4:10j] 
np.linspace(3,4,10) 

Co do składni foo(a,b,c), bardzo powszechne np.arange() używa.Podobnie jak range i xrange. Więc ty i twoi użytkownicy powinniście się z tym dobrze zapoznać.

Ponieważ wszystkie alternatywy kończą się dając trio wartości start/step/stop, są funkcjonalnie równoważne (w prędkości). Wybór sprowadza się do preferencji użytkownika i znajomości.

Podczas gdy funkcja nie może wziąć a:b:c zapis bezpośrednio, może być napisany do obsługi różnych wejść - kawałek, 3 pozycyjne argumenty, krotki, krotki plastrów (od s_) lub argumentów kluczowych. I po podstawowym indeksowaniu numpy można było rozróżnić krotki i listy.

2

Plastry mają większy sens, gdy są wyrażone jako kawałek czegoś. Zatem inną alternatywą jest bycie bardziej zorientowanym na obiekt: utwórz tymczasowy obiekt, który reprezentuje twój kawałek czegoś, i umieść swoją funkcję jako jego metodę.

Na przykład, jeśli funkcja jest naprawdę:

foo(bar, a:b:c) 

lub

bar.foo(a:b:c) 

czym można zastąpić ten z:

bar[a:b:c].foo() 

Jeśli bar[a:b:c] ma już inne znaczenie, następnie wymyślić inną nazwę baz i wykonaj:

bar.baz[a:b:c].foo() 

Trudno podać przekonujących przykładów bez rzeczywistym kontekście, ponieważ starasz się nazwy związane z nazwami rzeczy, które sprawiają, że intuicyjne poczucie, pozwalają pisać kod jednoznacznej i są stosunkowo krótkie.

Jeśli jesteś naprawdę tylko pisanie funkcji na własną działających na plaster, wówczas:

  1. Twoja funkcja modyfikuje kawałek, wracając inny kawałek:

    bar[foo(a:b:c)] 
    

    Jeśli tak jest w przypadku, każda wybrana składnia będzie wyglądać trochę myląco. Prawdopodobnie nie chcesz używać plasterków, jeśli chcesz dotrzeć do szerokiego grona programistów Pythona.

  2. Twoja funkcja naprawdę działa na kawałek liczb całkowitych, więc można zrobić, że jednoznaczne z tymczasowego obiektu:

    the_integers[a:b:c].foo() 
    
Powiązane problemy