Лучшее место для приведения / преобразования в нужный тип в Python - PullRequest
1 голос
/ 16 декабря 2009

Я все еще довольно плохо знаком с Python и пытаюсь привыкнуть к его динамической типизации. Иногда у меня есть функция или класс, который ожидает параметр определенного типа, но может получить значение другого типа, которое может быть принудительно применено к нему. Например, он может ожидать float, но вместо этого получить int или десятичное число. Или он может ожидать строку, но вместо этого получить объект, который определяет специальный метод __str__.

Какова наилучшая практика приведения аргумента к нужному типу (и причина этого)? Я делаю это в функции / классе или в вызывающей программе? Если в звонилке я тоже проверю это в функции? Например.

Альтернатива 1:

def myfunc(takes_float):
    myval = float(takes_float)

myfunc(5)

Альтернатива 2:

def myfunc(takes_float):
    myval = takes_float

myfunc(float(5))

Альтернатива 3:

def myfunc(takes_float):
    assert isinstance(takes_float, float)
    myval = takes_float

myfunc(float(5))

Я уже прочитал этот ответ и этот , и они говорят, что проверка типов в Python "плохая", но я не хочу тратить время на отслеживание очень простые ошибки, которые будут немедленно обнаружены компилятором на статически типизированном языке.

Ответы [ 3 ]

8 голосов
/ 16 декабря 2009

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

import math
def spc(x):
  math.sin(x) + math.cos(x)

Где вы должны "принуждать" x плавать? Ответ: нигде нет - грех и потому, что делают эту работу за вас, например ::1004

>>> spc(decimal.Decimal('1.9'))
0.62301052082391117

Итак, когда необходимо принуждать (как можно позже)? Например, если вы хотите вызывать строковые методы для аргумента, вы должны убедиться, что это строка - пытаясь вызвать, например, .lower для не-строки не будет работать, len может работать, но делать что-то другое, чем вы ожидаете, если аргумент, например ,. список (укажите количество элементов в списке, а не количество символов, которое займет его представление в виде строки) и т. д.

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

2 голосов
/ 16 декабря 2009

Это действительно зависит. Зачем вам нужно а float? int нарушит функцию? Если так, то почему?

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

Кто знает, может быть, кто-то найдет серьезную проблему с реализацией Python float и создаст библиотеку notbrokenfloat. Он может поддерживать все, что делает поплавок, исправляя какую-то экзотическую ошибку, но его объекты не будут иметь тип float. Приведение его к float вручную может удалить все преимущества этого изящного нового класса (или может сломаться сразу).

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

0 голосов
/ 16 декабря 2009

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

Отдел.

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

Если вы используете Python 2.x и случайно набрасываете / операторов, не задумываясь, вы можете - при некоторых общих обстоятельствах - в итоге поступить неправильно.

У вас есть несколько вариантов.

  1. from __future__ import division даст вам семантику Python 3 для деления.

  2. Запускайте с опцией -Qnew всегда, чтобы получить новую семантику деления.

  3. Используйте float вблизи / операций.

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

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


Чтобы быть более конкретным.

Там нет отладки "ожидал строку, но не получил строку". Это немедленно завершится с трассировкой. Нет путаницы. Нет времени на размышления. Если функция ожидает строку, то вызывающая сторона должна предоставить строку - это правило.

Альтернатива 2, приведенная выше, используется РЕАЛЬНО для исправления проблемы, когда у вас есть функция, которая ожидает строку И вы запутались и забыли предоставить строку. Эта ошибка случается редко и приводит к немедленному исключению типа.

...