Контрінтуїтивна поведінка int () у python


83

Це чітко зазначено в документації , що Int (число) є перетворення типів підлогових покриттів:

int(1.23)
1

і int (рядок) повертає int тоді і тільки тоді, коли рядок є цілочисельним літералом.

int('1.23')
ValueError

int('1')
1

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

Відповіді:


123

Особливої причини немає . Python просто застосовує свій загальний принцип не виконувати неявних перетворень, які є добре відомими причинами проблем, особливо для новачків, на таких мовах, як Perl та Javascript.

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

Для того, int("3.1459")щоб повернути 3інтерпретатору, слід неявно перетворити рядок у плаваючу. Оскільки Python не підтримує неявних перетворень, він вирішує натомість викликати виняток.


type(3)повертається <type int>. Однак python не скаржиться float("3"). Хіба python не імпліцитно перетворює рядок на int, а потім на float?
franksands

Ні. "3" - це допустиме значення з плаваючою комою, хоча як літерал програми воно буде інтерпретовано як ціле число. Не потрібно перетворення цілих чисел.
holdenweb

75

Це майже напевно випадок застосування трьох принципів з дзен Пітона :

Явний краще неявний.

[...] практичність перевершує чистоту

Помилки ніколи не повинні проходити мовчки

Деякий відсоток часу, хтось робить int('1.23'), називає неправильну конверсію для свого випадку використання і хоче щось на зразок floatабо decimal.Decimalзамість цього. У цих випадках їм явно краще негайно отримати помилку, яку вони можуть виправити, а не мовчки давати неправильне значення.

У разі, якщо ви дійсно хочете зрізаний , що до Міжнар, це тривіально явно зробити це шляхом пропускання її через floatпершу, а потім викликати одну з int, round, trunc, floorабо ceilв залежності від обставин. Це також робить ваш код більш самодокументіруемим, захищаючи від більш пізньої модифікації «коректує» гіпотетичний беззвучно-усічення intвиклику float, зробивши його ясно , що округлене значенням є те, що ви хочете.


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

17

Іноді може бути корисним думковий експеримент.

  • Поведінка A: int('1.23')помилка з помилкою. Це вже існуюча поведінка.
  • Поведінка B: int('1.23')виробляє 1без помилок. Це те, що ви пропонуєте.

Щодо поведінки A, це просто і тривіально отримати ефект від поведінки B: використовуйте int(float('1.23'))замість цього.

З іншого боку, з поведінкою В отримання ефекту поведінки А значно складніше:

def parse_pure_int(s):
    if "." in s:
        raise ValueError("invalid literal for integer with base 10: " + s)
    return int(s)

(і навіть з наведеним вище кодом я не впевнений у тому, що не існує жодного кутового випадку, коли він неправильно поводиться).

Отже, поведінка A є виразнішим, ніж поведінка B.

Ще одне, що слід врахувати: '1.23'це рядкове представлення значення з плаваючою комою. Конвертація '1.23'в ціле число концептуально передбачає два перетворення (рядок для переміщення у ціле число), але int(1.23)і int('1')кожне передбачає лише одне перетворення.


Редагувати:

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


Пояснити: я б не пропонував поведінку Б, оскільки це просто небезпечно, як заявляли ви та інші. Я не впевнений, що краще рішення, ніж поточне, навіть існує. Одним із варіантів було б присвоєння функціям різних назв, але це просто більше матеріалів для набору. Очевидне рішення того, що int (1.23) не вдається, і лише int (float-with-no-no-decimal-places) повертає ціле число, не має сенсу в мові, що динамічно набирається.
StefanS

1
Кутовий футляр може бути int('123E-2')або int('1L').
Jared Goguen

11

Простими словами - це не однакові функції.

  • int (десятковий) поводиться як "нижній, тобто збиває десяткову частину і повертає як int"
  • int (рядок) поводиться як "цей текст описує ціле число, перетворює його та повертає як int".

Це 2 різні функції з однаковим іменем, які повертають ціле число, але це різні функції.

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

Це не означає, що вони надають однакову або комбіновану функціональність, вони просто мають однакову назву і повертають один і той же тип. Їх так само легко назвати 'floorDecimalAsInt' і 'convertStringToInt', але вони вибрали 'int', оскільки це легко запам'ятати, (99%) інтуїтивно і плутанина рідко трапляються.

Розбір тексту як цілого числа для тексту, який включав десяткову крапку, наприклад "4.5", призведе до помилки в більшості комп'ютерних мов, і, як очікується, більшість програмістів спричинить помилку , оскільки текстове значення не представляє ціле число і означає вони надають помилкові дані


2
Тоді чому дві "різні функції" мають однакову назву? Звучить як порушення якоїсь дзен-нісенітниці.
hobbs

оскільки назва має сенс для 2 різних функцій і є стислою. Int-ify десятковий (підлога), перетворити рядок на int (перетворення)

Технічно може допомогти пам’ятати, що intце тип (і вбудований при цьому). Його творець ( __new__) бере ряд можливих типів аргументів. Його поведінка для кожного типу чітко визначена.
holdenweb

Ця відповідь просто неправильна, як зазначено. intнасправді є не функцією, а типом, чиї __new__та __init__методи беруть рядок або плаваючий аргумент, обробляючи кожну належним чином. Точніше було б сказати, що тип обробляє два типи аргументів по-різному, але існує лише один int.
holdenweb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.