Що таке качка типу?


429

Я натрапив на термін « качка, коли я читав випадкові теми в Інтернеті та не зрозумів його повністю».

Що таке "качка набравши"?


1
@Mitch Я намагався отримати щось у спадок. Але не міг багато слідувати. Вибачте, якщо я задав неправильне запитання.
sushil bharwani

3
@sushil bharwani: ні, не сердиться. Але люди очікують, що як перший порт дзвінка (тобто перше, що ви робите) - спробувати здійснити пошук, перш ніж публікувати тут.
Мітч Пшеничний

104
Зважаючи на наведені вище аргументи, не здається, що stackoverflow насправді необхідний, оскільки я впевнений, що майже на кожне питання, про який можна було б придумати, можна відповісти десь в Інтернеті, а якщо ні, то відповідь, ймовірно, можна отримати простіше і без критики, надіславши електронний лист знаючий друг. Я думаю, що багато хто з вас пропустили точку потоку.
родовий

41
Я впевнений, що десь читав, що SO мав бути "сховищем канонічних питань", і я впевнений, що ви не можете отримати більше канонічного, ніж цей.
heltonbiker

Відповіді:


302

Це термін, що використовується в динамічних мовах , які не мають сильного введення тексту .

Ідея полягає в тому, що вам не потрібен тип, щоб викликати існуючий метод на об'єкті - якщо метод визначений на ньому, ви можете викликати його.

Назва походить від фрази "Якщо вона схожа на качку, і какає, як качка, це качка".

У Вікіпедії є набагато більше інформації.


25
Будьте обережні, використовуючи Сильний набір тексту. Це не так добре визначено. Ні качка не пише. Google Go або Ocaml - це статично набрані мови зі структурною структурою підтипів. Це ці мови на качках?
Я даю відповіді на решти

7
кращою фразою для набору качок є: "Якщо кажуть, це качка. ну це для мене досить добре". дивіться pyvideo.org/video/1669/keynote-3 28:30 або youtube.com/watch?v=NfngrdLv9ZQ#t=1716
tovmeod

7
Введення качок не обов'язково використовується лише в динамічних мовах. Objective-C не є динамічною мовою, і вона використовує типи качок.
eyuelt

12
І Python, і Ruby є типовими мовами, і в обох є Duck Typing. Набір тексту для рядків не означає відсутність набору тексту качки.
alanjds

8
Я це зволікаю. Каченя на качках не має нічого спільного з силою типу, просто здатність використовувати будь-який об'єкт, що має метод, за його допомогою реалізувати інтерфейс чи ні.
e-satis

209

Введення качки означає, що операція формально не визначає вимоги, яким повинні відповідати її операнди, а просто випробовує її з тим, що задано.

На відміну від сказаних іншими, це не обов'язково стосується динамічних мов чи питань успадкування.

Приклад завдання: Викликати якийсь метод Quackна об'єкт.

Не використовуючи набору качок, функція, яка fвиконує це завдання, повинна заздалегідь вказати, що її аргумент повинен підтримувати якийсь метод Quack. Поширений спосіб - використання інтерфейсів

interface IQuack { 
    void Quack();
}

void f(IQuack x) { 
    x.Quack(); 
}

Виклик f(42)не вдається, але f(donald)працює до тих пір, donaldпоки є екземпляром IQuackпідтипу.

Інший підхід - це структурна типізація - але, знову ж таки, метод Quack()формально визначає будь-що, що не може довести його quackзаздалегідь, спричинить збій компілятора.

def f(x : { def Quack() : Unit }) = x.Quack() 

Ми навіть могли написати

f :: Quackable a => a -> IO ()
f = quack

в Haskell, де Quackableклас класу забезпечує існування нашого методу.


То як же типи качок змінюють це?

Ну, як я вже сказав, система набору качок не визначає вимог, а просто намагається, якщо щось працює .

Таким чином, система динамічного типу, як Python, завжди використовує введення качок:

def f(x):
    x.Quack()

Якщо fотримує , xщо підтримує Quack()все нормально, якщо немає, то він буде врізатися під час виконання.

Але введення качок взагалі не передбачає динамічного набору тексту - насправді існує дуже популярний, але повністю статичний підхід набору качки, який також не пред'являє жодних вимог:

template <typename T>
void f(T x) { x.Quack(); } 

Функція жодним чином не говорить про те, що хоче щось, xщо може Quack, тому натомість просто намагається під час компіляції, і якщо все працює, це добре.


5
ви не мали на увазі: void f (IQuak x) {x.Quak (); } (замість K.Quack), тому що параметром функції f є IQuack x не Iquack k, дуже маленька помилка, але я відчув, що це потрібно виправити :)
dominicbri7

Згідно з Вікіпедією, ваш останній приклад - це "структурний набір текстури", а не "набору качок".
Brilliand

Ну, мабуть, для цього обговорення є окреме питання: stackoverflow.com/questions/1948069/…
Brilliand

1
Отже, якщо я розумію, що ви сказали, різниця між мовою, яка підтримує введення качок, і тією, яка не є, полягає лише в тому, що при наборі качок вам не доведеться вказувати тип об'єктів, які приймає функція? def f(x)замість def f(IQuack x).
ППротей

124

Просте пояснення (без коду)

Обговорення семантики питання є досить нюансованим (і дуже академічним), але ось загальна ідея:

Введення качки

("Якщо вона ходить, як качка, і какає, як качка, то це качка.") - ТАК! але що це означає ??! Це найкраще проілюструє приклад:

Приклади функціональності друку качок:

Уявіть, у мене є чарівна паличка. Він має особливі повноваження. Якщо я помахую паличкою і скажу "Зажену!до машини, ну тоді їде!

Чи працює це на інші речі? Не впевнений: тому я спробую це на вантажівці. Нічого собі - теж їздить! Потім я спробую це в літаках, поїздах та на 1 Вудсі (це тип гольф-клубу, який люди використовують для 'водіння' м'яча для гольфу). Вони всі їздять!

Але чи попрацювало б це, скажімо, чашка? Помилка: KAAAA-BOOOOOOM! це не вийшло так добре. ====> Чайники не можуть їздити !! так !?

Це в основному концепція набору качок. Це спроба перед покупкою . Якщо це працює, все добре. Але якщо вона вийде з ладу, як граната, яка все ще знаходиться в руці, вона підірветься вам в обличчя.

Іншими словами, нас цікавить, що може робити об’єкт , а не те, що є об’єктом .

Приклад: статично набрані мови

Якщо ми переймалися тим, що насправді був об’єктом , то наш магічний трюк буде працювати лише на заздалегідь встановлених, дозволених типах - у цьому випадку на автомобілях, але не вдасться до інших об'єктів, які можуть їздити : вантажівки, мопеди, тук-тюки тощо. Він не буде працювати на вантажівках, тому що наша чарівна паличка очікує, що вона працюватиме лише на автомобілях .

Іншими словами, в цьому випадку, магія дуже близько паличка дивиться на те , що об'єкт є (це автомобіль?) , А не те , що об'єкт може зробити (наприклад , є чи автомобілі, вантажівки і т.д. можуть їздити).

Єдиний спосіб, як можна змусити вантажівку керувати, це якщо ви зможете якось отримати чарівну паличку, щоб очікувати як вантажівки, так і автомобілі (можливо, "впровадивши загальний інтерфейс"). Якщо ви не знаєте, що це означає, тоді просто ігноруйте це.

Резюме: Ключовий винос

Що важливо в качиної типізації є те , що об'єкт може реально зробити, а не те , що об'єкт знаходиться .


Мені здається цікавим припущення про те, що ти більше дбаєш про поведінку, ось це визначення. Без сумніву, BDD настільки успішний у таких мовах, як рубін.
Пабло Олмос де Агілера C.

27

Розглянемо, що ви проектуєте просту функцію, яка отримує об'єкт типу Birdі викликає його walk()метод. Можна придумати два підходи:

  1. Це моя функція, і я повинен бути впевнений, що він лише приймає Bird, або їх код не буде компілюватися. Якщо хтось хоче використовувати мою функцію, він повинен усвідомлювати, що я приймаю лише Birds
  2. Моя функція отримує будь-яку, objectsі я просто називаю walk()метод об'єкта . Отже, якщо objectвміє walk()правильно, якщо не може, то моя функція не спрацює. Тож тут не важливо, об'єкт є Birdчи що-небудь інше, важливо, що він може walk() (Це введення качки )

Потрібно враховувати, що типізація качок може бути корисною в деяких випадках, наприклад, Python багато використовує качки, набираючи качок .


Корисне читання


1
Приємне пояснення: Які переваги є?
sushil bharwani

2
Ця відповідь проста, зрозуміла і, мабуть, найкраща для початківців. Прочитайте цю відповідь разом з відповіддю над нею (або якщо вона рухається, відповіддю, що говорить про машини та чашки)
DORRITO

18

У Вікіпедії є досить детальне пояснення:

http://en.wikipedia.org/wiki/Duck_typing

типи качок - це стиль динамічного введення тексту, в якому поточний набір методів і властивостей об'єкта визначає дійсну семантику, а не її успадкування з певного класу чи реалізацію певного інтерфейсу.

Важлива примітка, ймовірно, що при наборі качок розробник стосується більше тих частин об'єкта, які споживаються, а не того, що є фактичним базовим типом.


13

Я бачу багато відповідей, які повторюють стару ідіому:

Якщо він схожий на качку і трясе як качка - це качка

а потім зануритися в пояснення того, що ти можеш зробити з набором качок, або приклад, який, здається, ще більше заплутує концепцію.

Я не знаходжу такої великої допомоги.

Це найкраща спроба англійської відповіді про введення качки, яку я знайшов:

Введення качки означає, що об’єкт визначається тим, що він може робити, а не тим, що він є.

Це означає, що ми менше переймаємось класом / типом об’єкта і більше переймаємось тим, які методи можна викликати на ньому та які операції на ньому можна виконувати. Нас не хвилює його тип, ми дбаємо про те, що він може робити .


3

Введення качки:

Якщо вона розмовляє і ходить, як качка, то це качка

Це , як правило , називають відведенням ( абдуктівним міркуванням або також називають retroduction , більш чітким визначенням я думаю):

  • з C (висновок, що ми бачимо ) і R (правило, те , що ми знаємо ), ми приймаємо / вирішуємо / припускаємо P (приміщення, властивість ), іншими словами, даний факт

    ... сама основа медичної діагностики

    з качками: C = гуляє, розмовляє , R = як качка , P = це качка

Повернутися до програмування:

  • Об'єкт o має метод / властивість mp1, а інтерфейс / тип T вимагає / визначає mp1

  • Об'єкт o має метод / властивість mp2 та інтерфейс / тип T вимагає / визначає mp2

  • ...

Отже, більш ніж просто прийняття mp1 ... на будь-якому об'єкті, поки він відповідає деякому визначенню mp1 ..., компілятор / час виконання також повинен відповідати твердженню o типу T

І добре, чи справа в наведених вище прикладах? Невже типізація качки насправді взагалі не вводить текст? Або ми повинні називати це неявним введенням тексту?


3

Погляд на саму мову може допомогти; це часто допомагає мені (я не є носієм англійської мови).

В duck typing:

1) слово typingне означає введення на клавіатурі (як це було стійке зображення в моїй свідомості), воно означає визначення " який тип речі - це річ? "

2) слово duckвиражає, як робиться таке визначення; це щось на кшталт "розкутого" визначення, як у: " якщо воно ходить як качка ... то це качка ". Це "вільно", тому що річ може бути качкою, а може і ні, але чи це насправді качка, не має значення; Що важливо, це те, що я можу з ним робити те, що я можу робити з качками, і очікувати поведінки, яку демонструють качки. Я можу його нагодувати хлібними крихтами, і річ може піти назустріч мені або зарядити мене або відмовитися ... але це не пожирає мене так, як хотілося б грізлі.


2

Я знаю, що не даю узагальненої відповіді. У Ruby ми не оголошуємо типи змінних чи методів - все це лише якийсь об’єкт. Отже, правило - "Класи не є типами"

У Ruby клас ніколи не буває (добре, майже ніколи). Натомість тип об'єкта визначається більше тим, що може зробити цей об’єкт. У Рубі ми називаємо цю качку типізацією. Якщо предмет ходить як качка і розмовляє, як качка, то перекладач із задоволенням ставиться до нього так, ніби до качки.

Наприклад, ви можете написати процедуру, щоб додати інформацію про пісні до рядка. Якщо ви походили з C # або Java, ви можете спробувати написати це:

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)"

Обійміть качку Рубі, і ви напишете щось набагато простіше:

def append_song(result, song)
    result << song.title << " (" << song.artist << ")"
end

result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"

Не потрібно перевіряти тип аргументів. Якщо вони підтримують << (у випадку результату) або назву та виконавця (у випадку пісні), все просто працюватиме. Якщо цього не зробити, ваш метод все одно викине виняток (так само, як це було б, якби ви перевірили типи). Але без перевірки ваш метод раптом набагато гнучкіший. Ви можете передати йому масив, рядок, файл або будь-який інший об'єкт, який додається за допомогою <<, і він би просто працював.


2

Введення качок не є натяком типу!

В основному, щоб використовувати "типи качок", ви не орієнтуєтесь на конкретний тип, а на ширший діапазон підтипів (не кажучи про спадщину, коли я маю на увазі підтипи, я маю на увазі "речі", що підходять до одних і тих же профілів), використовуючи загальний інтерфейс .

Можна уявити систему, яка зберігає інформацію. Для того, щоб писати / читати інформацію, вам потрібні певні сховища та інформація.

Типи сховища можуть бути: файл, база даних, сеанс тощо.

Інтерфейс повідомить вам про доступні параметри (методи) незалежно від типу зберігання, це означає, що на даний момент нічого не реалізовано! Іншими словами, Інтерфейс нічого не знає, як зберігати інформацію.

Кожна система зберігання даних повинна знати існування інтерфейсу, застосовуючи дуже однакові методи.

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;
    }
}

Отже, щоразу, коли потрібно писати / читати інформацію:

$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');

У цьому прикладі ви використовуєте Duck Typing в конструкторі Storage:

function __construct(StorageInterface $storage) ...

Сподіваюся, це допомогло;)


2

Обхід дерева з технікою набору качки

def traverse(t):
    try:
        t.label()
    except AttributeError:
        print(t, end=" ")
    else:
        # Now we know that t.node is defined
        print('(', t.label(), end=" ")
        for child in t:
            traverse(child)
        print(')', end=" ")

0

Я думаю, що це заплутано в поєднанні динамічного набору тексту, статичного набору тексту та качки. Введення качок - це незалежна концепція і навіть статична мова, що набирається типу, як Go, може мати систему перевірки типів, яка реалізує введення качок. Якщо система типів перевірятиме методи (оголошеного) об'єкта, а не тип, це може бути названо мовою набору качки.


-1

Я намагаюся зрозуміти відоме речення своїм способом: "Доза Пітона не піклується про об’єкт - це справжня качка чи ні. Все, що це хвилює, чи є об'єкт, перший" крякання ", другий" як качка ".

Є хороший веб-сайт. http://www.voidspace.org.uk/python/articles/duck_typing.shtml#id14

Автор вказав, що введення качок дозволяє створювати власні класи, які мають власну внутрішню структуру даних, але доступ до них використовується за допомогою звичайного синтаксису Python.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.