Каков корень различия в значении равенства между C++ и Python?

По-видимому, существует почти философское различие в значении "равенства" между C++ и Python. Я узнал об этом различии, пытаясь сделать на Python что-то, что довольно сложно в C++: различать два типа перечислений, когда они оба являются просто оболочкой для набора целых чисел, но проблема шире, чем перечисления, следовательно настоящий вопрос.

Если я пишу в коде C++, например, следующее

#include <iostream>

struct Foo {
    bool operator==(const Foo& foo) const { return this == &foo; }
};

struct Bar {};

int main() {
    Foo foo = Foo();
    Bar bar = Bar();
    if (foo == bar) std::cout << "ok" << std::endl;
}

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

И все же кажется, что " в Python существует небольшой (нет?) Прецедент для сопоставления равенств, вызывающих ошибки ".

и " если [объект] поднимается каждый раз, когда он сравнивается с [объектом другого типа], он разбивает любой контейнер, в который он добавляется ".

Действительно, писать

class Foo(object):
    pass


class Bar(object):
    pass


foo = Foo()
bar = Bar()

if (foo == bar):
    print("equal")

показывает, что нет никакой проблемы при сравнении объектов, которые в противном случае были бы несравнимы.

Что, философски, является корнем этого различия в смысле равенства между двумя языками?

Обновить

Часть моего недоумения в том, чтобы разобраться с Python, заключается в том, что до сих пор каждая функция, по-видимому, была разработана с намерением быть "естественным", "интуитивным", даже "человеческим", а не тем, что они могут быть определены в первую очередь.

Но подумайте, что вы находитесь в секции фруктов в продуктовом магазине и спрашиваете одного из первопроходцев: "Не могли бы вы рассказать мне, являются ли эти апельсины Fuji или Red Delicious?" Разумеется, никто не мог бы понять вопрос, чтобы венчать ответ так или иначе. Поэтому вопрос заключается в том, как обеспечить ответ "недоверчивым" в битах и байтах.

Обновление 2

(Слишком долго, чтобы быть комментарием к комментарию @GiacomoAlzetta) Я уважаю ваше мнение. Тем не менее, с этого момента я не буду уважать книгу о Python, которая не посвящает главу или, по крайней мере, раздел, указывая, что 3 < [1] является Истиной, и это объясняет предысторию (будь то историческая или философская ) за это. Быть динамически типизированным не означает, что человек настолько сильно загнан в угол (потому что, например, у одного есть только несколько доступных имен "a", "b" и "c"), чтобы повторно использовать имя для совершенно другого значения. В конечном счете, это даже не философская, а инженерная проблема: как вы удаляете или, по крайней мере, уменьшаете вероятность того, что один из нескольких людей, сотрудничающих с программным проектом, будет вводить ошибки в систему? Ошибки, которые остаются бездействующими (потому что язык динамически типизирован, и мы не можем предсказать пути вычислений) намного хуже ошибок, которые кричат "ошибка".

Ответы

Ответ 1

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

>>> 3 < [1]
True

В подобных случаях на самом деле это были строковые имена типов, которые были сопоставлены, и результат выше был из-за этого, как строки, "int" < "list". Это было обусловлено, в основном, ошибочной (в ретроспективе) попыткой наложить полный порядок на все объекты.

Заглядывая вперед в Python 3, намерение изменилось и начало реализовываться с использованием типов модулей datetime (которые были введены в Python 2): смешанные типизированные сравнения <, <=, > и >= которые делали скудный интуитивный смысл, были вместо этого вместо этого делаются исключения, а бессмысленный смешанный тип == всегда возвращает False и бессмысленный смешанный тип != always True.

>>> import datetime
>>> n = datetime.datetime.now()
>>> n < 3
Traceback (most recent call last):
  ...
TypeError: can't compare datetime.datetime to int
>>> n >= 3
Traceback (most recent call last):
  ...
TypeError: can't compare datetime.datetime to int
>>> n == 3
False
>>> n != 3
True

Python 3 стремится вести себя так, как обычно. Конечно, есть исключения. Например, при сравнении объектов разных числовых типов (например, целых чисел и поплавков) они обычно привязываются к общему типу под обложками.

Я был глубоко вовлечен в эти решения (и написал модуль datetime), поэтому вы можете мне доверять. Но что касается C++, вы должны спросить кого-то еще ;-)

Немного Дополнительная информация

Я должен добавить, что вплоть до введения "богатых сравнений" в Python 2 все сравнения выполнялись через протокол cmp(): в реализации CPython сравнение двух объектов возвращало одно из целых чисел в [-1, 0, 1]. __cmp__() не имели представления о том, какой из <, <=, == !=, > и >= был действительно желателен. Поэтому, хотя "сравнить любые две вещи, независимо от того, какой" первоначальный дизайн "не понравился на ранней стадии, было сложно сбиться с рук, пока в язык не были включены богатые сравнения. Тогда это стало простым (если утомительным).

Ответ 2

Это не столько "философская" проблема, сколько разница между динамическим языком, как Python, и сильно типизированным языком, например C++. Поскольку переменные могут содержать любой тип в Python, и этот тип может динамически изменяться, имеет смысл, чтобы значение равенства было более слабым на Python, чем что-то вроде C++. Вы не хотите, чтобы исключения были подняты, потому что вы задавали вопрос о том, равны ли две переменные: в принципе исключение должно возникать во время выполнения, поскольку Python интерпретируется, а на языке сценариев вы предпочитаете, чтобы сравнение возвращало false в этом обстоятельство. Точно так же вы можете сказать что-то вроде:

if (foo):
    [some code]

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

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

Ответ 3

Я вижу, что на стороне C++ никто не расширялся, так что вот мои два цента.

В C++ каждый объект (и переменная) имеет окончательный тип, который не может быть изменен, поэтому во время компиляции может быть обнаружено несоответствие типа. Поскольку результатом равенства между двумя несвязанными объектами является "duh, of course not", а C++ ставит большое значение для обнаружения ошибок на ранней стадии, он просто не компилируется вообще.