Типирование утки и (Java) интерфейс концепции - PullRequest
24 голосов
/ 05 июля 2011

Я только что прочитал статью в Википедии о вводе утки , и мне кажется, что я упускаю важный момент в концепции интерфейса, которую я использовал в Java:

"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."


class Duck:
    def quack(self):
        print("Quaaaaaack!")
    def feathers(self):
        print("The duck has white and gray feathers.")
    def swim(self):
        print("Swim seamlessly in the water")

class Person:
    def quack(self):
        print("The person imitates a duck.")
    def feathers(self):
        print("The person takes a feather from the ground and shows it.")
    def name(self):
        print("John Smith")

def in_the_forest(duck):
    duck.quack()
    duck.feathers()

def game():
    donald = Duck()
    john = Person()
    in_the_forest(donald)
    in_the_forest(john)

game()

что если,в in_the_forest я пишу:

  • это quack как утка?да
  • у него есть утка feathers?да
  • отлично, у нас есть утка!

и позже, потому что я знаю, что это утка, я хочу, чтобы она была swim?john утонет!

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

Ответы [ 3 ]

21 голосов
/ 05 июля 2011

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

Функция in_the_forest была написана разработчиком, который думал об утках. Он был разработан для работы на Duck. A Duck может quack и feathers, поэтому кодер использовал эти функции, чтобы выполнить работу под рукой. В этом случае тот факт, что Duck может также swim не использовался и не был необходим.

В статическом языке, таком как Java, было бы объявлено, что in_the_forest принимает Duck. Когда позже кодер обнаружил, что у них есть Person (который также может quack и feathers), и он хочет повторно использовать функцию, им не повезло. Является ли Person подклассом Duck? Нет, это не совсем уместно. Есть ли QuacksAndFeathers интерфейс? Может быть, если нам повезет. В противном случае нам нужно будет сделать один, изменить Duck, чтобы реализовать его, и изменить in_the_forest, чтобы взять QuacksAndFeathers вместо Duck. Это может быть невозможно, если Duck находится во внешней библиотеке.

В Python вы просто передаете свою персону на in_the_forest, и это работает. Поскольку оказывается, что in_the_forest не нужен Duck, ему просто нужен "подобный утке" объект, и в этом случае Person достаточно похож на утку.

game хотя, требуется другое определение "утиный", которое немного сильнее. Здесь Джону Смиту не повезло.

Теперь правда, что Java поймал бы эту ошибку во время компиляции, а Python - нет. Это можно рассматривать как недостаток. Аргумент счетчика про-динамической типизации говорит о том, что любой существенный объем кода, который вы пишете, всегда будет содержать ошибки, которые компилятор no может поймать (и, честно говоря, Java даже не является особенно хорошим примером компилятор с сильными статическими проверками, чтобы поймать много ошибок). Так что вам нужно проверить свой код, чтобы найти эти ошибки. И если вы тестируете эти ошибки, вы легко найдете ошибки, в которых вы передали Person функции, которая нуждается в Duck. Учитывая это, говорит динамический машинист, язык, который соблазняет вас на , а не тестирование, потому что он находит некоторые ваших тривиальных ошибок, на самом деле плохо . И вдобавок ко всему, он мешает вам делать действительно полезные вещи, такие как повторное использование функции in_the_forest в Person.

Лично я разрываюсь в двух направлениях. Мне действительно нравится Python с его гибкой динамической типизацией. И мне действительно нравятся Haskell и Mercury за их мощные системы статического типа. Я не большой поклонник Java или C ++; по моему мнению, у них есть все плохие биты статической типизации с несколькими хорошими битами.

5 голосов
/ 05 июля 2011

Не говорит на других языках, но в python недавно (v2.6) был представлен модуль Abstract Base Classes (ABC) .

Если вы прочтете обоснование его введения ( PEP 3119 ), вы быстро поймете, что одной из причин было «спасти Джона от верной смерти» или, другими словами, облегчить проверку на Дело в том, что когда вы программируете интерфейс, все методы интерфейса будут там. Из связанного ПКП:

ABC - это просто классы Python, которые добавляются в наследование объекта дерево, чтобы сигнализировать определенные особенности этот объект для внешнего инспектора. Тесты выполняются с использованием isinstance () и наличие определенного азбуки означает что испытание прошло. К тому же, Азбука определяет минимальный набор методы, которые устанавливают характерное поведение типа. Код, который различает объекты на основе на их тип ABC можно доверять, что те, методы всегда будут присутствовать.

Как правило, вы можете применить тот же шаблон для вашего собственного кода . Например: вы можете создать класс BasePlugin со всеми методами, необходимыми для работы плагина, и затем вы можете создать несколько различных плагинов, создав его подклассы. В зависимости от того, определены ли эти методы в каждом плагине , или , могут , вы можете определить BasePlugin методы для передачи без вывода сообщений (плагины могут определять эти методы) или вызвать исключение (плагины должны определить эти методы / переопределить BasePlugin).

РЕДАКТИРОВАТЬ: В ветке комментариев ниже, мне было предложено включить в ответ этот бит обсуждения:

Подобные функции - по крайней мере, в python - не реализованы ради программиста-человека (python никогда не игнорирует ошибку, поэтому там уже достаточно отзывов), а скорее ради собственной возможности самоанализа python ( что облегчает написание динамической загрузки, кода метапрограммирования и т. д.). Другими словами: я знаю, что Джон не может летать ... но я хочу, чтобы интерпретатор python знал это тоже! :)

4 голосов
/ 05 июля 2011

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

Это проблема динамической типизации в целом. В статически типизированном языке, таком как Java, компилятор проверяет во время компиляции, реализует ли Person IDuck или нет. В динамически типизированном языке, таком как Python, вы получаете ошибку во время выполнения, если Person пропускает какую-то конкретную функцию утки (например, swim). Цитирую другую статью из Википедии ( «Система типов», раздел «Динамическая типизация» ):

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

Динамическая типизация имеет свои недостатки (вы упомянули один) и свои преимущества. Краткое сравнение можно найти в другом разделе статьи о системе типов в Википедии: Статическая и динамическая проверка типов на практике .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...