Что такое утка?

429

Я сталкивался с понятием « Утиная печать», когда читал случайные темы о программном обеспечении в Интернете, и не совсем понял его.

Что такое «типирование утки»?

сушил бхарвани
источник
1
@ Митч, я попробовал и получил кое-что как некоторую форму наследования. Но не мог много следовать. Извините, если я задал неправильный вопрос.
Сушил Бхарвани
3
@sushil bharwani: нет, не злой. Но люди ожидают, что первым портом захода (т. Е. Первым, что вы делаете) является попытка поиска перед публикацией здесь.
Mitch Wheat
104
Принимая во внимание приведенные выше аргументы, кажется, что stackoverflow действительно необходим, поскольку я уверен, что почти на каждый вопрос, о котором можно подумать, есть ответ где-то в Интернете, и если бы не ответ, его можно было бы получить легче и без критики, отправив электронное письмо знающий друг. Я думаю, что многие из вас упустили смысл стека overoverflow.
Роди
41
Я уверен, что где-то читал, что SO был задуман как «хранилище канонических вопросов», и я уверен, что вы не можете получить более канонический, чем этот.
Хелтонбайкер

Ответы:

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. String Typing не означает, что не будет Duck Typing.
alanjds
8
Я понижаю это. Утиная утка не имеет никакого отношения к силе шрифта, просто возможность использовать любой объект, имеющий метод, независимо от того, реализует он интерфейс или нет.
E-удовлетворительно
209

Утиная печать означает, что операция делает формально не определяет требования, которым должны соответствовать ее операнды, а просто пробует ее с тем, что ей дано.

В отличие от того, что говорили другие, это не обязательно связано с динамическими языками или проблемами наследования.

Пример задачи: вызов метода Quackдля объекта.

Без использования duck-typing функция, fвыполняющая эту задачу, должна заранее указать, что ее аргумент должен поддерживать некоторый метод Quack. Распространенным способом является использование интерфейсов

interface IQuack { 
    void Quack();
}

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

Вызов f(42)не удался, но f(donald)работает до тех пор, пока donaldявляется экземпляром IQuack-subtype.

Другим подходом является структурная типизация, но опять же, метод 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 поддержку a Quack(), все нормально, если нет, то во время выполнения произойдет сбой.

Но типизирование утиной утилитой вообще не подразумевает динамическую типизацию - на самом деле, существует очень популярный, но полностью статичный подход типизированной утиной типизации, который также не предъявляет никаких требований:

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

Функция ни в коем случае не говорит, что она хочет xчто- то, что может Quack, поэтому вместо этого она просто пытается во время компиляции, и если все работает, это нормально.

Dario
источник
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).
PProteus
124

Простое объяснение (без кода)

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

Duck Typing

(«Если он ходит как утка и крякает как утка, значит, это утка».) - ДА! Но что это значит??! Это лучше всего иллюстрируется примером:

Примеры функциональности Duck Typing:

Представь, что у меня есть волшебная палочка. У него есть особые полномочия. Если я взмахну палочкой и скажу "Драйв!" к машине, ну тогда она ездит!

Работает ли это на других вещах? Не уверен: поэтому я пробую это на грузовике. Ух ты - это тоже диски! Затем я пробую это на самолетах, поездах и 1 Вудсе (это тип гольф-клуба, который люди используют, чтобы «водить» мяч для гольфа). Они все ездят!

Но будет ли это работать, скажем, чашка? Ошибка: KAAAA-BOOOOOOM! это не сработало так хорошо. ====> Чашки не могут водить! Дух !?

Это в основном концепция утиной печати. Это система " попробуй, прежде чем купить" . Если это работает, все хорошо. Но если он потерпит неудачу, как граната в твоей руке, он взорвется тебе в лицо.

Другими словами, нас интересует, что может сделать объект , а не то, чем он является .

Пример: статически типизированные языки

Если нас интересовало, что это за объект на самом деле , то наш магический трюк будет работать только на заранее заданных, разрешенных типах - в данном случае автомобилях, но не будет работать на других объектах, которые могут ездить : грузовиках, мопедах, тук-туках и т. Д. Он не будет работать на грузовиках, потому что наша волшебная палочка ожидает, что он будет работать только на автомобили .

Другими словами, в этом случае, магия очень близко палочка смотрит на то , что объект является (это автомобиль?) , А не то , что объект может сделать (например , является ли автомобили, грузовики и т.д. могут ездить).

Единственный способ заставить грузовик ехать, если вы каким-то образом можете заставить волшебную палочку рассчитывать как на грузовики, так и на автомобили (возможно, путем «реализации общего интерфейса»). Если вы не знаете, что это значит, просто проигнорируйте это на данный момент.

Резюме: вынос ключа

Что важно при наборе утки, так это то, что на самом деле может делать объект , а не то, чем он является .

BKSpurgeon
источник
Я нахожу интересным предположение о том, что вы больше заботитесь о поведении, вот и все. Без сомнения, BDD настолько успешен в таких языках, как рубин.
Пабло Олмос де Агилера С.
27

Представьте, что вы разрабатываете простую функцию, которая получает объект типа Birdи вызывает его walk()метод. Есть два подхода, о которых вы можете подумать:

  1. Это моя функция, и я должен быть уверен, что она только принимает Bird, иначе их код не скомпилируется. Если кто -то хочет использовать свою функцию, он должен знать , что я только прием BirdS
  2. Моя функция получает любое, objectsи я просто вызываю метод объекта walk(). Так что, если objectможет, walk()это правильно, если не может, моя функция не сработает. Так что здесь не важно, является ли объект Birdили что-то еще, важно, что он может walk() (это утка набирает )

Следует учесть, что в некоторых случаях полезность утки может быть полезной, например, Python часто использует утку .


Полезное чтение

Алиреза Фаттахи
источник
1
Хорошее объяснение, какие есть преимущества?
Сушил Бхарвани
2
Этот ответ прост, понятен и, вероятно, лучший для начинающих. Прочитайте этот ответ вместе с ответом над ним (или, если он движется, ответ, который говорит об автомобилях и чайных чашках)
DORRITO
18

В Википедии есть довольно подробное объяснение:

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

Утиная типизация - это стиль динамической типизации, при котором текущий набор методов и свойств объекта определяет действительную семантику, а не наследование от определенного класса или реализацию определенного интерфейса.

Важное замечание, вероятно, заключается в том, что при утилизации утки разработчик больше заботится о частях объекта, которые потребляются, а не о том, что представляет собой базовый тип.

Крис Бакстер
источник
13

Я вижу много ответов, которые повторяют старую идиому:

Если это похоже на утку и крякает как утка, это утка

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

Я не нахожу такой большой помощи.

Это лучшая попытка найти простой английский ответ о наборе утки, который я нашел:

Duck Typing означает, что объект определяется тем, что он может делать, а не тем, чем он является.

Это означает, что мы меньше заботимся о классе / типе объекта и больше заботимся о том, какие методы могут быть вызваны для него и какие операции могут быть выполнены с ним. Мы не заботимся о его типе, мы заботимся о том, что он может сделать .

Джерард Симпсон
источник
3

Утка набрав:

Если он говорит и ходит как утка, то это утка

Это обычно называется похищением ( абдуктивные рассуждения или также называется ретродукцией , я думаю, более четкое определение):

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

    ... сама основа медицинского диагноза

    с утками: C = гуляет, говорит , R = как утка , P = это утка

Вернуться к программированию:

  • объект o имеет метод / свойство mp1, а интерфейс / тип T требует / определяет mp1

  • объект o имеет метод / свойство mp2, а интерфейс / тип T требует / определяет mp2

  • ...

Таким образом, более чем просто принимая mp1 ... для любого объекта, если он соответствует определению mp1 ..., компилятор / среда выполнения также должны быть в порядке с утверждением о типе T

И хорошо, это случай с примерами выше? Утиная печать по сути вообще не печатает? Или мы должны назвать это неявной типизацией?

Djee
источник
3

Глядя на сам язык может помочь; это часто помогает мне (я не являюсь носителем английского языка).

В duck typing:

1) слово typingне означает печатать на клавиатуре (как это было в моем представлении), оно означает определение « что это за вещь? »

2) слово duckвыражает, как выполняется это определение; это своего рода «свободное» определение, например: « если он ходит как утка ... тогда это утка ». Это «свободно», потому что вещь может быть уткой или нет, но не имеет значения, действительно ли это утка; важно то, что я могу делать с этим то же самое, что я могу делать с утками и ожидать поведения, которое демонстрируют утки. Я могу кормить его хлебными крошками, и вещь может пойти ко мне, или напасть на меня, или отступить ... но это не сожрет меня, как гризли.

Арта
источник
2

Я знаю, что не даю обобщенного ответа. В Ruby мы не объявляем типы переменных или методов - все это просто какой-то объект. Таким образом, правило «Классы не являются типами»

В 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

Duck Typing - это не тип подсказки!

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

Вы можете представить себе систему, которая хранит информацию. Для записи / чтения информации вам нужно какое-то хранилище и информацию.

Типы хранения могут быть: файл, база данных, сессия и т. Д.

Интерфейс позволит вам узнать доступные опции (методы) независимо от типа хранилища, а это означает, что на данный момент ничего не реализовано! Другими словами, интерфейс ничего не знает о том, как хранить информацию.

Каждая система хранения данных должна знать о существовании интерфейса, применяя те же самые методы.

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 in Storage:

function __construct(StorageInterface $storage) ...

Надеюсь, это помогло;)

obinoob
источник
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, может иметь систему проверки типов, которая реализует типизацию утиной типизации. Если система типов будет проверять методы (объявленного) объекта, но не типа, это можно назвать языком утиной типизации.

ICEE
источник
-1

Я пытаюсь понять известное предложение по-своему: «Доза Python не заботится о том, является ли объект настоящей уткой или нет. Все, что его волнует, это то, является ли объект, первый« кряк », второй« как утка »».

Есть хороший сайт. http://www.voidspace.org.uk/python/articles/duck_typing.shtml#id14

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

Робин
источник