Я сталкивался с понятием « Утиная печать», когда читал случайные темы о программном обеспечении в Интернете, и не совсем понял его.
Что такое «типирование утки»?
Я сталкивался с понятием « Утиная печать», когда читал случайные темы о программном обеспечении в Интернете, и не совсем понял его.
Что такое «типирование утки»?
Ответы:
Это термин, используемый в динамических языках, которые не имеют строгой типизации .
Идея состоит в том, что вам не нужен тип для вызова существующего метода объекта - если метод определен для него, вы можете вызвать его.
Название происходит от фразы «Если это похоже на утку и крякает как утка, то это утка».
В Википедии гораздо больше информации.
Утиная печать означает, что операция делает формально не определяет требования, которым должны соответствовать ее операнды, а просто пробует ее с тем, что ей дано.
В отличие от того, что говорили другие, это не обязательно связано с динамическими языками или проблемами наследования.
Пример задачи: вызов метода 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
, поэтому вместо этого она просто пытается во время компиляции, и если все работает, это нормально.
def f(x)
вместо def f(IQuack x)
.
Обсуждение семантики вопроса довольно нюансировано (и очень академично), но вот общая идея:
Duck Typing
(«Если он ходит как утка и крякает как утка, значит, это утка».) - ДА! Но что это значит??! Это лучше всего иллюстрируется примером:
Примеры функциональности Duck Typing:
Представь, что у меня есть волшебная палочка. У него есть особые полномочия. Если я взмахну палочкой и скажу "Драйв!" к машине, ну тогда она ездит!
Работает ли это на других вещах? Не уверен: поэтому я пробую это на грузовике. Ух ты - это тоже диски! Затем я пробую это на самолетах, поездах и 1 Вудсе (это тип гольф-клуба, который люди используют, чтобы «водить» мяч для гольфа). Они все ездят!
Но будет ли это работать, скажем, чашка? Ошибка: KAAAA-BOOOOOOM! это не сработало так хорошо. ====> Чашки не могут водить! Дух !?
Это в основном концепция утиной печати. Это система " попробуй, прежде чем купить" . Если это работает, все хорошо. Но если он потерпит неудачу, как граната в твоей руке, он взорвется тебе в лицо.
Другими словами, нас интересует, что может сделать объект , а не то, чем он является .
Пример: статически типизированные языки
Если нас интересовало, что это за объект на самом деле , то наш магический трюк будет работать только на заранее заданных, разрешенных типах - в данном случае автомобилях, но не будет работать на других объектах, которые могут ездить : грузовиках, мопедах, тук-туках и т. Д. Он не будет работать на грузовиках, потому что наша волшебная палочка ожидает, что он будет работать только на автомобили .
Другими словами, в этом случае, магия очень близко палочка смотрит на то , что объект является (это автомобиль?) , А не то , что объект может сделать (например , является ли автомобили, грузовики и т.д. могут ездить).
Единственный способ заставить грузовик ехать, если вы каким-то образом можете заставить волшебную палочку рассчитывать как на грузовики, так и на автомобили (возможно, путем «реализации общего интерфейса»). Если вы не знаете, что это значит, просто проигнорируйте это на данный момент.
Резюме: вынос ключа
Что важно при наборе утки, так это то, что на самом деле может делать объект , а не то, чем он является .
Представьте, что вы разрабатываете простую функцию, которая получает объект типа Bird
и вызывает его walk()
метод. Есть два подхода, о которых вы можете подумать:
Bird
, иначе их код не скомпилируется. Если кто -то хочет использовать свою функцию, он должен знать , что я только прием Bird
Sobjects
и я просто вызываю метод объекта walk()
. Так что, если object
может, walk()
это правильно, если не может, моя функция не сработает. Так что здесь не важно, является ли объект Bird
или что-то еще, важно, что он может walk()
(это утка набирает )Следует учесть, что в некоторых случаях полезность утки может быть полезной, например, Python часто использует утку .
В Википедии есть довольно подробное объяснение:
http://en.wikipedia.org/wiki/Duck_typing
Утиная типизация - это стиль динамической типизации, при котором текущий набор методов и свойств объекта определяет действительную семантику, а не наследование от определенного класса или реализацию определенного интерфейса.
Важное замечание, вероятно, заключается в том, что при утилизации утки разработчик больше заботится о частях объекта, которые потребляются, а не о том, что представляет собой базовый тип.
Я вижу много ответов, которые повторяют старую идиому:
Если это похоже на утку и крякает как утка, это утка
а затем погрузитесь в объяснение того, что вы можете сделать с помощью утки, или пример, который, кажется, еще больше запутывает концепцию.
Я не нахожу такой большой помощи.
Это лучшая попытка найти простой английский ответ о наборе утки, который я нашел:
Duck Typing означает, что объект определяется тем, что он может делать, а не тем, чем он является.
Это означает, что мы меньше заботимся о классе / типе объекта и больше заботимся о том, какие методы могут быть вызваны для него и какие операции могут быть выполнены с ним. Мы не заботимся о его типе, мы заботимся о том, что он может сделать .
Утка набрав:
Если он говорит и ходит как утка, то это утка
Это обычно называется похищением ( абдуктивные рассуждения или также называется ретродукцией , я думаю, более четкое определение):
из C (заключение, что мы видим ) и R (правило, что мы знаем ), мы принимаем / решаем / принимаем P (Помещение, собственность ), другими словами, данный факт
... сама основа медицинского диагноза
с утками: C = гуляет, говорит , R = как утка , P = это утка
Вернуться к программированию:
объект o имеет метод / свойство mp1, а интерфейс / тип T требует / определяет mp1
объект o имеет метод / свойство mp2, а интерфейс / тип T требует / определяет mp2
...
Таким образом, более чем просто принимая mp1 ... для любого объекта, если он соответствует определению mp1 ..., компилятор / среда выполнения также должны быть в порядке с утверждением о типе T
И хорошо, это случай с примерами выше? Утиная печать по сути вообще не печатает? Или мы должны назвать это неявной типизацией?
Глядя на сам язык может помочь; это часто помогает мне (я не являюсь носителем английского языка).
В duck typing
:
1) слово typing
не означает печатать на клавиатуре (как это было в моем представлении), оно означает определение « что это за вещь? »
2) слово duck
выражает, как выполняется это определение; это своего рода «свободное» определение, например: « если он ходит как утка ... тогда это утка ». Это «свободно», потому что вещь может быть уткой или нет, но не имеет значения, действительно ли это утка; важно то, что я могу делать с этим то же самое, что я могу делать с утками и ожидать поведения, которое демонстрируют утки. Я могу кормить его хлебными крошками, и вещь может пойти ко мне, или напасть на меня, или отступить ... но это не сожрет меня, как гризли.
Я знаю, что не даю обобщенного ответа. В 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)"
Вам не нужно проверять тип аргументов. Если они поддерживают << (в случае результата) или заголовок и исполнителя (в случае песни), все будет работать. Если этого не произойдет, ваш метод все равно сгенерирует исключение (так же, как если бы вы проверили типы). Но без чека ваш метод внезапно становится намного более гибким. Вы можете передать ему массив, строку, файл или любой другой объект, который добавляется с помощью <<, и это будет просто работать.
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) ...
Надеюсь, это помогло;)
Я думаю, что это путает смешивать динамическую типизацию, статическую типизацию и типизацию по типу утки. Утиная типизация является независимой концепцией, и даже статический типизированный язык, такой как Go, может иметь систему проверки типов, которая реализует типизацию утиной типизации. Если система типов будет проверять методы (объявленного) объекта, но не типа, это можно назвать языком утиной типизации.
Я пытаюсь понять известное предложение по-своему: «Доза Python не заботится о том, является ли объект настоящей уткой или нет. Все, что его волнует, это то, является ли объект, первый« кряк », второй« как утка »».
Есть хороший сайт. http://www.voidspace.org.uk/python/articles/duck_typing.shtml#id14
Автор указал, что утиная типизация позволяет вам создавать свои собственные классы, которые имеют собственную внутреннюю структуру данных, но доступ к которой осуществляется с использованием обычного синтаксиса Python.