Podczas pisania przypadkowych tematów na temat oprogramowania online natknąłem się na pisanie kaczek i nie do końca go rozumiałem.
Co to jest „pisanie kaczek”?
Podczas pisania przypadkowych tematów na temat oprogramowania online natknąłem się na pisanie kaczek i nie do końca go rozumiałem.
Co to jest „pisanie kaczek”?
Odpowiedzi:
Jest to termin używany w dynamicznych językach , które nie mają silnego pisania .
Chodzi o to, że nie potrzebujesz typu, aby wywołać istniejącą metodę na obiekcie - jeśli metoda jest na nim zdefiniowana, możesz ją wywołać.
Nazwa pochodzi od wyrażenia „Jeśli wygląda jak kaczka i kwacze jak kaczka, to jest kaczka”.
Wikipedia ma znacznie więcej informacji.
Wpisywanie kaczki oznacza, że operacja nie określa formalnie wymagań, które muszą spełnić jej operandy, a jedynie wypróbowuje to, co podano.
W przeciwieństwie do tego, co powiedzieli inni, niekoniecznie dotyczy to dynamicznych języków lub problemów z dziedziczeniem.
Przykładowe zadanie: Wywołaj metodę Quack
na obiekcie.
Bez użycia kaczego typu funkcja f
wykonująca to zadanie musi z góry określić, że jej argument musi obsługiwać jakąś metodę Quack
. Powszechnym sposobem jest użycie interfejsów
interface IQuack {
void Quack();
}
void f(IQuack x) {
x.Quack();
}
Wywoływanie f(42)
kończy się niepowodzeniem, ale f(donald)
działa tak długo, jak długo donald
występuje instancja IQuack
podtypu.
Innym podejściem jest typowanie strukturalne - ale ponownie, metoda Quack()
formalnie określa wszystko, co nie może quack
z góry udowodnić, że spowoduje awarię kompilatora.
def f(x : { def Quack() : Unit }) = x.Quack()
Moglibyśmy nawet pisać
f :: Quackable a => a -> IO ()
f = quack
w Haskell, gdzie Quackable
krój pisma zapewnia istnienie naszej metody.
Cóż, jak powiedziałem, system pisania kaczek nie określa wymagań, ale po prostu próbuje, jeśli coś działa .
Zatem dynamiczny system typów, taki jak Python, zawsze używa pisania kaczego:
def f(x):
x.Quack()
Jeśli f
otrzyma x
wsparcie Quack()
, wszystko jest w porządku, jeśli nie, ulegnie awarii w czasie wykonywania.
Ale pisanie kaczką wcale nie oznacza dynamicznego pisania - w rzeczywistości istnieje bardzo popularne, ale całkowicie statyczne pisanie kaczki, które również nie stwarza żadnych wymagań:
template <typename T>
void f(T x) { x.Quack(); }
Ta funkcja nie mówi w żaden sposób, że chce takich, x
które mogą Quack
, więc zamiast tego próbuje po prostu skompilować czas, a jeśli wszystko działa, jest w porządku.
def f(x)
zamiast def f(IQuack x)
.
Dyskusja na temat semantyki pytania jest dość szczegółowa (i bardzo akademicka), ale oto ogólna idea:
Pisanie kaczek
(„Jeśli chodzi jak kaczka i kwacze jak kaczka, to jest to kaczka.”) - TAK! ale co to znaczy?Najlepiej ilustruje to przykład:
Przykłady funkcji Kaczego Pisania:
Wyobraź sobie, że mam magiczną różdżkę. Ma specjalne uprawnienia. Jeśli macham różdżką i powiem „Jedź!”do samochodu, więc jeździ!
Czy działa na inne rzeczy? Nie jestem pewien: próbuję na ciężarówce. Wow - to też prowadzi! Następnie próbuję na samolotach, pociągach i 1 lesie (to rodzaj klubu golfowego, którego ludzie używają do „prowadzenia” piłki golfowej). Oni wszyscy jeżdżą!
Ale czy to zadziała powiedzmy, filiżanka do herbaty? Błąd: KAAAA-BOOOOOOM! to nie wyszło tak dobrze. ====> Filiżanki nie mogą prowadzić !! duh !?
Jest to w zasadzie koncepcja pisania kaczek. To system wypróbowania przed zakupem . Jeśli to działa, wszystko jest dobrze. Ale jeśli zawiedzie, jak granat wciąż w dłoni, wybuchnie ci w twarz.
Innymi słowy, jesteśmy zainteresowani tym, co obiekt może zrobić , a nie tym tym, czym jest obiekt .
Przykład: języki wpisywane statycznie
Jeśli martwimy się o to, czym właściwie jest obiekt , wówczas nasza magiczna sztuczka będzie działać tylko na wcześniej ustalonych, autoryzowanych typach - w tym przypadku samochodach, ale zawiedzie na innych obiektach, które mogą prowadzić : ciężarówkach, motorowerach, tuk-tuksach itp. Nie działa na ciężarówkach, ponieważ nasza magiczna różdżka oczekuje, że będzie działać tylko na samochodach .
Innymi słowy, w tym scenariuszu magiczna różdżka bardzo dokładnie przygląda się temu, czym jest obiekt (czy to samochód?), A nie tym, co może zrobić obiekt (np. Czy samochody, ciężarówki itp. Mogą jeździć).
Jedynym sposobem, aby zmusić ciężarówkę do jazdy, jest zdobycie magicznej różdżki, której można oczekiwać zarówno od ciężarówek, jak i samochodów (być może poprzez „wdrożenie wspólnego interfejsu”). Jeśli nie wiesz, co to znaczy, po prostu zignoruj to na chwilę.
Podsumowanie: Kluczowe wynos
W pisaniu kaczek ważne jest to, co obiekt może faktycznie zrobić, a nie to, czym jest obiekt .
Rozważ, że projektujesz prostą funkcję, która pobiera obiekt typu Bird
i wywołuje jego walk()
metodę. Istnieją dwa podejścia, o których możesz pomyśleć:
Bird
, albo ich kod się nie skompiluje. Jeśli ktoś chce skorzystać z mojej funkcji, musi mieć świadomość, że akceptuję tylko Bird
sobjects
i po prostu wywołuję walk()
metodę obiektu . Więc jeśli object
puszka walk()
jest poprawna, jeśli nie, moja funkcja zawiedzie. Więc tutaj nie jest ważne, że obiekt jest Bird
cokolwiek innego, ważne jest, aby mógł walk()
(To jest pisanie kaczki )Należy wziąć pod uwagę, że pisanie kaczką może być przydatne w niektórych przypadkach, na przykład Python często używa pisania kaczego .
Wikipedia ma dość szczegółowe wyjaśnienie:
http://en.wikipedia.org/wiki/Duck_typing
typowanie dynamiczne to styl dynamicznego pisania, w którym bieżący zestaw metod i właściwości obiektu określa prawidłową semantykę, a nie jej dziedziczenie po określonej klasie lub implementacji określonego interfejsu.
Ważna uwaga jest prawdopodobna, że podczas pisania kaczką deweloper bardziej interesuje się częściami obiektu, które są konsumowane, niż faktycznym typem bazowym.
Widzę wiele odpowiedzi, które powtarzają stary idiom:
Jeśli wygląda jak kaczka i kwakanie jak kaczka, to jest kaczka
a następnie zapoznaj się z wyjaśnieniem, co możesz zrobić z pisaniem kaczek, lub przykładem, który wydaje się zaciemniać tę koncepcję.
Nie znajduję zbyt wiele pomocy.
To najlepsza próba zwykłej angielskiej odpowiedzi na temat pisania kaczką, którą znalazłem:
Wpisywanie kaczki oznacza, że obiekt jest zdefiniowany przez to, co może zrobić, a nie przez to, czym jest.
Oznacza to, że jesteśmy mniej zainteresowani klasą / typem obiektu, a bardziej tym, jakie metody można na nim wywoływać i jakie operacje można na nim wykonywać. Nie dbamy o jego typ, dbamy o to, co może zrobić .
Pisanie kaczek:
Jeśli mówi i chodzi jak kaczka, to jest to kaczka
Jest to zazwyczaj nazywa porwanie ( abductive rozumowanie lub zwany także retroduction , jaśniejszej definicji myślę):
z C (wniosek, co widzimy ) i R (zasada, co wiemy ), akceptujemy / decydujemy / zakładamy P (przesłanka, właściwość ), innymi słowy dany fakt
... podstawa diagnozy medycznej
z kaczkami: C = spacery, rozmowy , R = jak kaczka , P = to kaczka
Powrót do programowania:
obiekt o ma metodę / właściwość mp1, a interfejs / typ T wymaga / definiuje mp1
obiekt o ma metodę / właściwość mp2, a interfejs / typ T wymaga / definiuje mp2
...
Tak więc, więcej niż zwykłe zaakceptowanie mp1 ... dla dowolnego obiektu, o ile spełnia on jakąś definicję mp1 ..., kompilator / środowisko wykonawcze również powinno być w porządku z twierdzeniem o jest typu T
Cóż, czy tak jest w przypadku powyższych przykładów? Czy pisanie w Kaczce w ogóle nie polega na pisaniu? A może powinniśmy nazwać to niejawnym pisaniem?
Pomocne może być samo spojrzenie na język; często mi to pomaga (nie jestem rodzimym językiem angielskim).
W duck typing
:
1) słowo typing
to nie oznacza pisania na klawiaturze (tak jak utrzymywał się w moim umyśle obraz), oznacza określenie „ jaki rodzaj rzeczy to ta rzecz? ”
2) słowo duck
wyraża, w jaki sposób dokonuje się tego ustalenia; to rodzaj „luźnego” określenia, jak w: „ jeśli chodzi jak kaczka ... to jest kaczka ”. Jest „luźny”, ponieważ rzecz może być kaczką lub nie, ale to, czy tak naprawdę jest kaczką, nie ma znaczenia; ważne jest to, że mogę to zrobić, co mogę zrobić z kaczkami i oczekiwać zachowań wykazywanych przez kaczki. Mogę nakarmić to bułką tartą i rzecz może pójść w moją stronę, rzucić się na mnie lub się wycofać ... ale nie pochłonie mnie tak, jak zrobiłby to grizzly.
Wiem, że nie udzielam ogólnej odpowiedzi. W Ruby nie deklarujemy typów zmiennych ani metod - wszystko jest tylko rodzajem obiektu. Zatem reguła brzmi: „Klasy nie są typami”
W Ruby klasa nigdy nie jest (OK, prawie nigdy) typem. Zamiast tego typ obiektu jest bardziej określony przez to, co ten obiekt może zrobić. W Ruby nazywamy to pisaniem kaczek. Jeśli obiekt chodzi jak kaczka i mówi jak kaczka, tłumacz chętnie traktuje go jak kaczkę.
Na przykład możesz pisać procedurę dodawania informacji o utworze do ciągu. Jeśli pochodzisz z języka C # lub Java, możesz ulec pokusie napisania:
def append_song(result, song)
# test we're given the right parameters
unless result.kind_of?(String)
fail TypeError.new("String expected") end
unless song.kind_of?(Song)
fail TypeError.new("Song expected")
end
result << song.title << " (" << song.artist << ")" end
result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
Wpisz kaczkę Ruby Ruby, a napiszesz coś o wiele prostszego:
def append_song(result, song)
result << song.title << " (" << song.artist << ")"
end
result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
Nie musisz sprawdzać typu argumentów. Jeśli obsługują << (w przypadku wyniku) lub tytuł i wykonawcę (w przypadku piosenki), wszystko po prostu zadziała. Jeśli nie, Twoja metoda i tak zgłosi wyjątek (tak jak zrobiłby to, gdybyś sprawdził typy). Ale bez sprawdzenia twoja metoda nagle staje się znacznie bardziej elastyczna. Możesz przekazać mu tablicę, ciąg, plik lub dowolny inny obiekt, który dołącza się za pomocą <<, i to po prostu zadziała.
Wpisywanie kaczek nie jest typem podpowiedzi!
Zasadniczo, aby użyć „pisania kaczego”, nie będziesz kierować na konkretny typ, ale raczej na szerszy zakres podtypów (nie mówiąc o dziedziczeniu, kiedy mam na myśli podtypy, mam na myśli „rzeczy” pasujące do tych samych profili) za pomocą wspólnego interfejsu .
Możesz sobie wyobrazić system, który przechowuje informacje. Aby pisać / czytać informacje, potrzebujesz pewnego rodzaju pamięci i informacji.
Rodzaje przechowywania mogą być następujące: plik, baza danych, sesja itp.
Interfejs poinformuje Cię o dostępnych opcjach (metodach) niezależnie od rodzaju pamięci, co oznacza, że w tym momencie nic nie jest zaimplementowane! Innymi słowy, interfejs nie wie nic o tym, jak przechowywać informacje.
Każdy system pamięci masowej musi wiedzieć o istnieniu interfejsu, wdrażając jego bardzo te same metody.
interface StorageInterface
{
public function write(string $key, array $value): bool;
public function read(string $key): array;
}
class File implements StorageInterface
{
public function read(string $key): array {
//reading from a file
}
public function write(string $key, array $value): bool {
//writing in a file implementation
}
}
class Session implements StorageInterface
{
public function read(string $key): array {
//reading from a session
}
public function write(string $key, array $value): bool {
//writing in a session implementation
}
}
class Storage implements StorageInterface
{
private $_storage = null;
function __construct(StorageInterface $storage) {
$this->_storage = $storage;
}
public function read(string $key): array {
return $this->_storage->read($key);
}
public function write(string $key, array $value): bool {
return ($this->_storage->write($key, $value)) ? true : false;
}
}
Teraz za każdym razem, gdy musisz pisać / czytać informacje:
$file = new Storage(new File());
$file->write('filename', ['information'] );
echo $file->read('filename');
$session = new Storage(new Session());
$session->write('filename', ['information'] );
echo $session->read('filename');
W tym przykładzie używasz Duck Typing in Storage Konstruktor:
function __construct(StorageInterface $storage) ...
Mam nadzieję, że to pomogło;)
Myślę, że mieszanie pisania dynamicznego, pisania statycznego i pisania kaczego jest mylące. Pisanie kaczką jest niezależną koncepcją, a nawet statyczny język pisma, taki jak Go, może mieć system sprawdzania typów, który implementuje pisanie kaczych. Jeśli system typów sprawdzi metody (zadeklarowanego) obiektu, ale nie typ, można go nazwać językiem kaczym.
Staram się zrozumieć na swój sposób słynne zdanie: „Python nie obchodzi, czy przedmiot jest prawdziwą kaczką, czy nie. Wszystko zależy od tego, czy obiekt, pierwszy„ kwak ”, a drugi„ jak kaczka ”.
Jest dobra strona internetowa. http://www.voidspace.org.uk/python/articles/duck_typing.shtml#id14
Autor wskazał, że pisanie kaczych pozwala tworzyć własne klasy, które mają własną wewnętrzną strukturę danych - ale są dostępne przy użyciu normalnej składni Pythona.