Назначение значений по умолчанию в вызове функции - это запах кода.
def a(b=[]):
pass
Это сигнатура функции, которая не подходит. Не только из-за проблем, описанных в других ответах. Я не буду вдаваться в это.
Эта функция направлена на то, чтобы сделать две вещи. Создайте новый список и выполните функциональность, скорее всего, в указанном списке.
Функции, которые делают две вещи, являются плохими функциями, поскольку мы учимся на чистых практиках кода.
Атака на эту проблему с полиморфизмом мы будем расширять список python или переносить его в класс, а затем выполнять свою функцию на нем.
Но подождите, пока вы скажете, мне нравятся мои однострочные.
Ну , Угадай, что. Код - это не просто способ управления поведением оборудования. Это способ:
Не оставляйте бомбы замедленного действия для себя
Отделяя эту функцию от двух вещей, которые она делает, нам нужен класс
class ListNeedsFives(object):
def __init__(self, b=None):
if b is None:
b = []
self.b = b
def foo():
self.b.append(5)
Выполнено
a = ListNeedsFives()
a.foo()
a.b
И почему это лучше, чем слияние всего вышеописанного кода с одной функцией.
def dontdothis(b=None):
if b is None:
b = []
b.append(5)
return b
Почему бы не сделать это?
Если вы не сработаете в своем проекте, ваш код будет жить. Скорее всего, ваша функция будет делать больше, чем это. Правильный способ создания поддерживаемого кода состоит в том, чтобы разделить код на атомные части с должным образом ограниченным объемом.
Конструктор класса является очень общепризнанным компонентом для всех, кто сделал объектно-ориентированное программирование. Размещение логики, которая обрабатывает экземпляр списка в конструкторе, делает когнитивную нагрузку понимания того, что делает код меньше.
Метод foo()
не возвращает список, почему бы и нет?
При возврате отдельного списка вы можете предположить, что безопасно делать то, что вам хочется. Но это может быть не так, поскольку он также разделяется объектом a
. Заставляя пользователя ссылаться на него как a.b
, он напоминает, где находится список. Любой новый код, который хочет изменить a.b
, естественно, будет помещен в класс, где он принадлежит.
Функция подписи def dontdothis(b=None):
не имеет ни одного из этих преимуществ.
Проблема заключается в том, что NaN не равен самому себе, как определено в стандарте IEEE для чисел с плавающей запятой:
>>> float("nan") == float("nan")
False
Когда словарь ищет ключ, он примерно делает это:
is
, ключ был найден. б. Если первая проверка не удалась, проверьте равенство, используя оператор __eq__
. Первый пример завершается успешно, так как np.nan
и np.nan
являются одним и тем же объектом, поэтому это не имеет значения они не сравниваются с равными:
>>> numpy.nan is numpy.nan
True
Во втором случае np.float64(np.nan)
и np.float64(np.nan)
не являются одним и тем же объектом - два вызова конструктора создают два разных объекта:
>>> numpy.float64(numpy.nan) is numpy.float64(numpy.nan)
False
Поскольку объекты также не сравниваются одинаково, словарь завершает, что ключ не найден и выбрасывает KeyError
.
Вы даже можете сделать это:
>>> a = float("nan")
>>> b = float("nan")
>>> {a: 1, b: 2}
{nan: 1, nan: 2}
В заключение, кажется, более разумная идея избегать NaN в качестве словарного ключа.
Обратите внимание, что это не так в Python 3.6:
>>> d = float("nan")
>>> d
nan
>>> c = {"a": 3, d: 4}
>>> c["a"]
3
>>> c[d]
4
Как я понимаю:
d = объект nan c - словарь, который содержит 3, связанных с «а» и «4» связаны с наном, но, поскольку внешний вид Python 3.6 в словарях изменился, теперь он сравнивает два указателя, и если они указывают на один и тот же объект, они считают, что это равенство сохраняется.
Это означает, что хотя:
>>> d == d
False
Из-за того, как IEEE754 указывает, что NAN не равен самому себе, при поиске словаря сначала учитываются указатели и потому, что они указывают на тот же nan-объект, который он возвращает 4.
Обратите также внимание на то, что:
>>> e = float("nan")
>>> e == d
False
>>> c[e]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: nan
>>> c[d]
4
Таким образом, не каждый нанок указывает на 4, поэтому сохраняется какой-то IEEE754. Это было реализовано, потому что соблюдение стандарта, что нан никогда не совпадает с самим собой, снижает эффективность, а не игнорирует стандарт. Именно потому, что вы храните что-то в словаре, к которому вы не можете получить доступ в предыдущих версиях.
float('nan')
имеют одинаковое расположение памяти, т. Е.float('nan')
является одноэлементным? Без него даже использование plainfloat('nan')
- плохая идея. Тот же вопрос оnp.nan
. – max 17 April 2015 в 22:44float('nan')
создает новый экземплярfloat
, так как каждый вызовfloat(1)
создает новый экземплярfloat
. Это не само по себе плохо.np.nan
является глобальным именем в модуле NumPy и указывает на тот же объект, если вы его не переназначаете, поэтому при нормальных обстоятельствахnp.nan
- это одно значение. (Я бы не назвал его singleton, так как это имя используется для класса, который разрешает только один экземпляр, напримерNoneType
.) – Sven Marnach 20 April 2015 в 14:15nan
иdict
, если и хранилище, и поиск выполняются с помощьюnp.nan
. "Дикий"nan
s, как и созданныеfloat('nan')
илиfloat('inf') - float('inf')
, не будут работать как словарные ключи. – max 21 April 2015 в 03:21