dlaczego potrzebne są puste funkcje


9

Zacząłem uczyć się pytona i zastanawiam się, dlaczego puste funkcje są potrzebne w języku programowania

np. w python:

def empty_func():
    pass

nawet w skryptach powłoki dostępne są puste funkcje puste funkcje.

Moje zrozumienie i pytanie:

  1. Dlaczego język programowania potrzebuje pustych funkcji? Czy to po prostu zabawa z językiem programowania, czy nic innego, co naprawdę ma znaczenie?

  2. Jeśli ma to jakiś cel, czy ktoś może opisać przypadek użycia lub podać prawdziwy przykład użycia pustych funkcji?

  3. Czy jest jakaś tradycja języków programowania umożliwiająca puste funkcje?


EDYCJA (rzeczy, które otrzymałem po przeczytaniu twoich odpowiedzi):

  • Do szkicowania algorytmów lub z funkcjami abstrakcyjnymi
  • W przypadku przesyłania formularzy bez konieczności wykonywania czynności
  • Symbol zastępczy dla niektórych operacji obowiązkowych

1
mogą być używane jako STUBY, jeśli wykonanie programu spodziewa się ich znaleźć / użyć, jednak nie chcesz przez nich niczego zmieniać.
G.Rassovsky

Odpowiedzi:


5

W językach powłoki rodziny Bourne :polecenie, które w ogóle nic nie robi, jest zwykle używane w dwóch sytuacjach:

  • Symbol zastępczy, gdy coś wymaga obowiązkowego polecenia, np

    while some_condtion
    do :
    done
    

    ponieważ dowymaga co najmniej jednego polecenia.

  • Odrzucanie argumentów, ale wywoływanie efektów ubocznych na liście argumentów, np

    : ${myvar=foo}

Jestem pewien, że są prawdopodobnie inne aplikacje, które znają eksperci od powłoki :)

W Pythonie (i innych językach) jest rzadziej używany. Może działać jako argument funkcji wyższego rzędu, gdy tak naprawdę nie chcesz nic robić. Załóżmy na przykład, że masz funkcję, która przesyła formularz i pozwala na wywoływanie funkcji asynchronicznie po zakończeniu przesyłania:

def submit(callback=empty_func):
    ...

W ten sposób, jeśli callbacknie zostanie podany, przesyłanie będzie nadal realizowane, ale nie zostaną wykonane żadne dalsze działania. Jeśli użyłeś Nonejako wartości domyślnej, musisz jawnie sprawdzić, czy wywołanie zwrotne było None, dodając bałagan do kodu.


1

Zależy to od miejsca, w którym stoisz w cyklu programowania, ale czasami szkicując algorytm, chcesz dokonać abstrakcji na temat złożonych bloków bez ich natychmiastowego wdrożenia.

def full_algo():
  init_stuff()
  process_stuff()
  ...

Wiesz, jak init_stuffbędzie działać, jest to dość proste w twojej głowie, ale tak naprawdę nie potrzebujesz go od razu, więc deklarujesz, że jest to pusta funkcja. Pozwoli to twojemu kodowi na kompilację i uruchomienie bez zawracania sobie głowy krwawymi szczegółami.

Kolejnym zastosowaniem zwolnionych aplikacji jest dziedziczenie. Załóżmy, że masz dużą klasę, która określa zachowanie kodu specyficznego dla platformy. Może pojawić się logika podobna do tej:

init_filesystem();
access_files();
release_filesystem();

Ten kod będzie działał na wielu platformach, ale niektóre platformy mogą nie wymagać inicjalizacji systemu plików. Wtedy twoje dziedzictwo będzie wyglądać tak (wirtualny z = 0 w C ++ oznacza po prostu, że klasy pochodne MUSZĄ implementować te metody):

class FileSystem{
  virtual void init_filesystem() = 0;
  virtual void access_files() = 0;
  virtual void release_filesystem() = 0;
};

Wówczas określona implementacja tej klasy (interfejsu) może nic nie zrobić dla niektórych z tych metod. Alternatywnie, klasa podstawowa mogłaby zadeklarować puste metody dla init / release zamiast deklarować je jako wirtualne.

Wreszcie (i wstyd) czasami utrzymujesz bardzo starą aplikację. Boisz się, że usunięcie metod spowoduje uszkodzenie. Dzieje się tak, gdy masz złożone dziedziczenie, które nie jest właściwie zrozumiane lub gdy masz wiele wskaźników funkcji (wywołania zwrotne). Po prostu usuwasz w nich kod, aby i tak były wywoływane, nie niszcząc niczego.


0

Rufflewind wykonał dobrą robotę, kiedy możesz użyć pustej funkcji w ukończonym programie. Z mojego doświadczenia wynika, że ​​jest on częściej używany w niedokończonych programach, więc możesz zaplanować, co napiszesz i nadal go kompilować, dopóki nie zaczniesz go faktycznie wdrażać. Innymi słowy, zwykle jest to tylko symbol zastępczy.

Jest to konieczne w przypadku Pythona, ponieważ wykorzystuje wcięcia do oznaczania bloków, w przeciwieństwie do języków podobnych do C, które używają nawiasów klamrowych {}. Oznacza to, że jeśli go nie masz pass, analizator składni nie może stwierdzić, czy chcesz go zostawić pusty, czy też zapomniałeś. Dołączenie passsprawia, że ​​parser jest znacznie prostszy i daje wygodne słowo do wyszukania, gdy szukasz niezaimplementowanych bloków.


1
W przypadku niedokończonego kodu, rzucanie NotImplementedErrorjest bardziej adekwatnym rozwiązaniem, ponieważ „Błędy nigdy nie powinny przechodzić w ciszy”, a wywołanie niezaimplementowanej funkcji w programie na żywo jest błędem.
ivan_pozdeev

To zależy. W przypadku wysyłanego kodu tak. Ale jeśli jest to kod, który spodziewasz się zaimplementować w ciągu godziny i po prostu pozostawiasz go niezaimplementowany podczas pracy nad testami jednostkowymi, po prostu brak implementacji może być lepszym rozwiązaniem. Często mój przepływ pracy to 1) metoda zapisu kodu z pass 2) test jednostki zapisu, który sprawdza wartość zwracaną 3) weryfikacja testu kończy się niepowodzeniem (ponieważ metoda zwraca wartość niezdefiniowaną) 4) implementacja metody 5) test weryfikacji teraz przechodzi
Gort the Robot

0

Chociaż nie jest to coś, czego można oczekiwać od Pythona, w świecie sterowników urządzeń są chwile, w których trzeba podać pustą funkcję, aby obsłużyć zdarzenie, o którym albo (a) wiesz, że się nie wydarzy, albo ( b) nie przejmuj się, nawet jeśli tak jest.

Widać to również w C z wywołaniami zwrotnymi. Czasami zobaczysz kod, który po prostu PRZYJMUJE, że dostarczyłeś wywołanie zwrotne, i próbuje go wywołać, ignorując ryzyko zerowego wskaźnika. W takim przypadku wystarczy podać pustą procedurę wywołania zwrotnego. (Tak, mam na myśli konkretnego dostawcę).


passDużo użyłem jako symbolu zastępczego podczas pisania kodu, szczególnie na zajęciach, kiedy chcę, aby coś można było uruchomić, zanim wszystko się skończy. Jest to tak naprawdę odpowiednik Pythona {}, którego nie może zrobić, ponieważ nie używa nawiasów klamrowych. W tym sensie wszystkie języki, o których wiem, na to pozwalają, tylko tyle, ile pythonwymaga słowa kluczowego. Nawet w dniach zgromadzeń mieliśmy NOP.
Gort the Robot

@StevenBurnap, zwykle kiedy robię coś takiego, dołączam lokalny odpowiednik printf („>>> procedura XXX o nazwie \ n”);
John R. Strohm
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.