2009-12-31 3 views
160

Я не могу понять, почему у Python нет функции sign. У него есть abs встроенный (который я считаю сестрой sign), но не sign.Почему у Python нет знаковой функции?

В python 2.6 есть функция copysignmath), но нет знака. Зачем писать copysign(x,y), когда вы могли бы просто написать sign, а затем получить copysign прямо с abs(x) * sign(y)? Последнее было бы намного яснее: x со знаком y, тогда как с copysign вы должны помнить, если это x со знаком y или y со знаком x!

Очевидно, что sign(x) не содержит ничего больше, чем cmp(x,0), но было бы гораздо читабельнее, что это тоже (и для очень читаемого языка, такого как python, это было бы большим плюсом).

Если бы я был дизайнером python, я был бы другим способом: no cmp builtin, но sign. Когда вам нужно , вы можете просто сделать sign(x-y) (или, что еще лучше, для не численного материала, просто x> y - конечно, это должно было потребовать sorted, принимающего логическое значение вместо целочисленного компаратора). Это было бы также более ясно: положительно, когда x>y (тогда как с cmp вы должны помнить соглашение положительное, когда первый - больше, но это может быть наоборот). Конечно, cmp имеет смысл по своему усмотрению по другим причинам (например, при сортировке нецифровых вещей или если вы хотите, чтобы сортировка была стабильной, что невозможно использовать с помощью простого логического)

Итак, вопрос: почему разработчик (ы) Python решил оставить функцию sign за пределами языка? Почему чертовски беспокоиться с copysign, а не его родителем sign?

Я что-то упустил?

EDIT - после комментария Питера Хансена. Достаточно честный, чтобы вы его не использовали, но вы не сказали, для чего вы используете python. Через 7 лет, когда я использовал python, мне нужно было много раз, а последняя - соломинкой, которая сломала спину верблюда!

Да, вы можете передать cmp вокруг, но 90% времени, которое мне нужно было передать, было в такой же идиоме, как lambda x,y: cmp(score(x),score(y)), которая бы хорошо работала со знаком.

Наконец, я надеюсь, вы согласны с тем, что sign был бы более полезен, чем copysign, так что даже если бы я купил ваше мнение, зачем беспокоиться об определении этого в математике вместо того, чтобы подписывать? Как copysign может быть так полезен, как знак?

+1

«Я что-то упускаю?» Если ничего другого, то факт, что использование 'sign (x-y)' требует выражения, оценивается, что означает, что вы не можете передать его в качестве обратного вызова, если вы не перенесите его сначала «частичным». С помощью cmp(), как определено, вы просто передаете ссылку на 'cmp'. Также «невероятно полезно»? Я сомневаюсь в этом. Мне нужно было это раз в 10 лет писать Python. Если бы это было действительно так полезно, я думаю, что это будет ... –

+26

@dmazzoni: не будет ли этот аргумент работать на все вопросы на этом сайте? просто закройте stackoverflow и задайте каждый вопрос в соответствующем списке рассылки dev или пользователя! – Davide

+2

@ davide: Сначала я согласен, функция signum является общей и полезной. Во-вторых, я согласен с dmazzoni в том, что ваш вопрос лучше задавать на python-dev. Одно дело спросить, как использовать понимание списка для xyz: мы можем сделать хорошую работу, ответив на это здесь. Но спрашивать, почему Гвидо Ван Россум отказался от подписки, сложно для всех, кто здесь может ответить. –

ответ

171

EDIT:

Действительно был patch который включал sign() в math, но он не был принят, потому что они не согласны на what it should return in all the edge cases (+/- 0, +/- нан и т.д.)

Таким образом, они решили реализовать только copysign, который (хотя и более подробный) может быть used to delegate to the end user the desired behavior for edge cases - который sometimes might require the call to cmp(x,0).


Я не знаю, почему это не встроенный, но у меня есть мысли.

copysign(x,y): 
Return x with the sign of y. 

Самое главное, что copysign является надстройкой sign! Вызов copysign с x = 1 совпадает с функцией sign. Поэтому вы можете просто использовать copysign и забыли об этом.

>>> math.copysign(1, -4) 
-1.0 
>>> math.copysign(1, 3) 
1.0 

Если вы заболели прохождения целых два аргумента, вы можете реализовать sign таким образом, и это все еще будет совместим с IEEE материала, упомянутого другими:

>>> sign = functools.partial(math.copysign, 1) # either of these 
>>> sign = lambda x: math.copysign(1, x) # two will work 
>>> sign(-4) 
-1.0 
>>> sign(3) 
1.0 
>>> sign(0) 
1.0 
>>> sign(-0.0) 
-1.0 
>>> sign(float('nan')) 
-1.0 

Во-вторых, как правило, когда вы хотите признак чего-то, вы просто в конечном итоге умножаете его на другое значение. И, конечно, это в основном то, что делает copysign.

Таким образом, вместо того, чтобы:

s = sign(a) 
b = b * s 

Вы можете просто сделать:

b = copysign(b, a) 

И да, я удивлен вы используете Python в течение 7 лет, и думаю, что cmp может быть так легко снимается и заменяется на sign! Вы никогда не применяли класс с методом __cmp__? Вы никогда не звонили cmp и не указали пользовательскую функцию компаратора?

В общем, я тоже нашел функцию sign, но copysign с первым аргументом 1 будет работать нормально. Я не согласен с тем, что sign был бы более полезен, чем copysign, поскольку я показал, что это всего лишь подмножество той же функциональности.

+24

Использование '[int (copysign (1, ноль)) для нуля в (0, 0.0, -0.0)] 'дает' [1, 1, -1] '. Это должно быть '[0, 0, 0]' согласно http://en.wikipedia.org/wiki/Sign_function – user238424

+1

У вас неправильный порядок. [int (copysign (ноль, 1)) для нуля в (0, 0.0, -0.0)] дает [0,0,0], который вы ищете. –

+11

@Andrew - вызов пользователя user238424 правильный. 'copysign (a, b)' возвращает a со знаком b - b - переменный ввод, a - значение для нормализации с знаком b. В этом случае комментатор иллюстрирует, что copysign (1, x) в качестве замены для знака (x) терпит неудачу, поскольку он возвращает 1 для x = 0, тогда как знак (0) будет оцениваться равным 0. – PaulMcG

-6

Причина «знак» не включена в то, что если бы мы включили каждый полезный однострочный в список встроенных функций, Python было бы нелегко и практично работать с ним больше. Если вы часто используете эту функцию, почему бы вам не рассказать об этом сами? Это не так, как будто это трудно или даже утомительно.

+4

Ну, я бы купил это, только если 'abs()' также был опущен. 'sign()' и 'abs()' часто используются вместе, 'sign()' является наиболее полезным из двух (IMO), и ни один из них не является удаленным или утомительным для реализации (хотя он подвержен ошибкам, см., как этот ответ ошибается: http://stackoverflow.com/questions/1986152/why-python-doesnt-have-a-sign-function/1986343#1986343) – Davide

+1

Дело в том, что численный результат 'sign()' само редко бывает полезным. То, что вы делаете большую часть времени, - это использовать разные кодовые пути на основе того, является ли переменная положительной или отрицательной, и в этом случае более легко читать условие явно. –

+3

abs() используется гораздо чаще, чем знак(). И я указал вам на Tracker NumPy, который показывает, как может быть реализован жесткий знак(). Что должно значить (-3 + 4j)? В то время как abs (-3 + 4j) составляет 5,0. Вы делаете утверждения, что sign() и abs() часто видны вместе. Стандартная библиотека C не имеет функции «знака», так где вы получаете свои данные? –

51

«copysign» определяется стандартом IEEE 754 и частью спецификации C99. Вот почему он находится на Python. Функция не может быть полностью реализована символом abs (x) * (y) из-за того, как она должна обрабатывать значения NaN.

>>> import math 
>>> math.copysign(1, float("nan")) 
1.0 
>>> math.copysign(1, float("-nan")) 
-1.0 
>>> math.copysign(float("nan"), 1) 
nan 
>>> math.copysign(float("nan"), -1) 
nan 
>>> float("nan") * -1 
nan 
>>> float("nan") * 1 
nan 
>>> 

Это делает функцию copysign() более полезной, чем sign().

Что касается конкретных причин, по которым знак IEEE (x) недоступен в стандартном Python, я не знаю. Я могу делать предположения, но это было бы угадать.

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

static double 
m_atan2(double y, double x) 
{ 
     if (Py_IS_NAN(x) || Py_IS_NAN(y)) 
       return Py_NAN; 
     if (Py_IS_INFINITY(y)) { 
       if (Py_IS_INFINITY(x)) { 
         if (copysign(1., x) == 1.) 
           /* atan2(+-inf, +inf) == +-pi/4 */ 
           return copysign(0.25*Py_MATH_PI, y); 
         else 
           /* atan2(+-inf, -inf) == +-pi*3/4 */ 
           return copysign(0.75*Py_MATH_PI, y); 
       } 
       /* atan2(+-inf, x) == +-pi/2 for finite x */ 
       return copysign(0.5*Py_MATH_PI, y); 

Там вы можете ясно видеть, что copysign() является более эффективной, чем функция функции трехзначной знак().

Вы писали:

Если бы я был дизайнером питона, я бы был другой путь arond: нет CMP() встроенная, а знак()

Это означает, что вы не» t знает, что cmp() используется для вещей помимо чисел. cmp («Это», «Это») не может быть реализовано с помощью функции sign().

Edit собирать мои дополнительные ответы в других:

Вы основываете свои оправдания о том, как ABS() и знак() часто видели вместе. Поскольку стандартная библиотека C не содержит функции «sign (x)» любого типа, я не знаю, как вы оправдываете свои взгляды. Есть абс (int) и fabs (double) и fabsf (float) и fabsl (long), но не упоминается знак. Существует «copysign()» и «signbit()», но они применяются только к номерам IEEE 754.

С комплексными числами, что бы подписать (-3 + 4j) в Python, было ли это реализовано? abs (-3 + 4j) return 5.0. Это явный пример того, как abs() можно использовать в местах, где знак() не имеет смысла.

Предположим, что знак (x) был добавлен в Python в качестве дополнения к abs (x). Если «x» - это экземпляр пользовательского класса, который реализует метод __abs __ (self), тогда abs (x) вызовет x .__ abs __(). Для правильной работы, чтобы обрабатывать abs (x) таким же образом, тогда Python должен будет получить знак (x).

Это излишне для относительно ненужной функции. Кроме того, почему знак (x) существует и неотрицательный (x) и неположительный (x) не существует? Мой фрагмент из реализации математического модуля Python показывает, как copybit (x, y) можно использовать для реализации неотрицательных(), которые простой знак (x) не может сделать.

Python должен поддерживать лучшую поддержку математической функции IEEE 754/C99. Это добавит функцию signbit (x), которая будет делать то, что вы хотите, в случае float. Он не будет работать для целых чисел или сложных чисел, а тем более строк, и у него не будет имени, которое вы ищете.

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

И что это за пределами StackOverflow. Возьмите его вместо одного из списков Python.

+5

Ну, я не хочу, если это сделает вас счастливыми, но Python 3 не имеет ни 'cmp()', ни 'sign()' :-) –

+3

запись функции good sign(), которая бы корректно работала с IEEE 754, тривиальный. Это было бы хорошим моментом, чтобы включить его в язык, а не оставить его, хотя я не уточнил этот вопрос в вопросе – Davide

+2

Ваш комментарий о том, как «если вы хотите, чтобы сортировка была стабильной» означает, что вы также Не знаю, как работает стабильная сортировка. Ваше утверждение о том, что copysign и sign эквивалентны, показывают, что вы не знали много о математике IEEE 754 перед этим сообщением. Должен ли Python реализовывать все математические функции 754 в ядре? Что нужно делать для компиляторов, не относящихся к C99? Не-754 платформы? «isnonnegative» и «isnonpositive» также являются полезными функциями. Должен ли Python включать эти? abs (x) отклоняется на x .__ abs __(), поэтому следует подписать (x) отложить до знака x .__ __()? Для этого мало потребности или необходимости, так почему это должно застрять в ядре? –

28

Еще один лайнер для знака()

sign = lambda x: (1, -1)[x<0] 

Если вы хотите, чтобы вернуть 0 при х = 0:

sign = lambda x: x and (1, -1)[x<0] 
+0

почему? Сам вопрос признает, что 'cmp (x, 0)' равнозначно 'sign', а' lambda x: cmp (x, 0) 'более читаемо, чем вы предлагаете. – ToolmakerSteve

+5

Вы не можете прочитать 'cmp (x, 0)' и знать, что он делает. – dansalmo

+1

Действительно, я был неправ.Я предположил, что «cmp» был указан для возврата -1,0, + 1, но я вижу, что спецификация не гарантирует этого. – ToolmakerSteve

17

С cmp было removed, вы можете получить ту же функциональность,

def cmp(a, b): 
    return (a > b) - (a < b) 

def sign(a): 
    return (a > 0) - (a < 0) 

Он работает для float, int и даже Fraction. В случае float уведомление sign(float("nan")) равно нулю.

Python не требует, чтобы сравнения возвращают логическое значение, и поэтому принуждая сравнения с BOOL() защищает от допустимого, но редко реализации:

def sign(a): 
    return bool(a > 0) - bool(a < 0) 
6

NumPy имеет знаковую функцию, и дает вам бонус других функций. Итак:

import numpy as np 
x = np.sign(y) 

Просто будьте осторожны, что результат является numpy.float64:

>>> type(np.sign(1.0)) 
<type 'numpy.float64'> 

Для таких вещей, как JSON, это имеет значение, так как JSON не знает, как сериализовать типы numpy.float64. В этом случае вы можете сделать:

float(np.sign(y)) 

, чтобы получить регулярный поплавок.

1

Вам не нужно, вы можете просто использовать:

If not number == 0: 
    sig = number/abs(number) 
else: 
    sig = 0 
+2

Следует отметить, что 'x/abs (x)' занимает немного больше времени, чем просто цепочка 'if/else', чтобы проверить, какая сторона 0 включена или, если на то пошло, с помощью slimy-yet-satisfying' return (x> 0) - (x <0) 'для вычитания значений' bool' и возврата 'int' – 2015-02-01 21:20:04

+0

Python рассматривает' True' и 'False' как' 1' и '0', вы можете это сделать абсолютно и получить '1',' 0' или '-1'. 'def sign (x): return (x> 0) - (x <0)' не вернет 'bool', он вернет' int' - если вы передадите '0', вы получите' 0 'back – 2015-03-31 18:37:42

8

Попробуйте запустить это, где х любое число

int_sign = bool(x > 0) - bool(x < 0) 

Принуждение к BOOL() обрабатывает possibility что оператор сравнительного не возвращает логическое значение.

+0

Хорошая идея, но я думаю, что вы имеете в виду: int_sign = int (x> 0) - int (x <0) – yucer

+0

Я имею в виду: int_sign = lambda x: (x> 0) - (x <0) – yucer

2

Да, правильная функция sign() должна быть как минимум в математическом модуле - так как она находится в numpy. Потому что его часто нужно для математического ориентированного кода.

Но math.copysign() также полезен независимо.

cmp() и obj.__cmp__() ... обычно в целом высокий важность независимо. Не только для математического ориентированного кода. Рассмотрите возможность сравнения/сортировки кортежей, объектов даты, ...

Девы аргументов в http://bugs.python.org/issue1640 относительно опущения math.sign() являются странно, потому что:

  • Там нет отдельного -NaN
  • sign(nan) == nan без беспокойства (как exp(nan))
  • sign(-0.0) == sign(0.0) == 0 без беспокойства
  • sign(-inf) == -1 без беспокойства

- как в NumPy

1

В Python 2, CMP() возвращает целое число: нет требования, чтобы быть результатом -1, 0 или 1, так что знак (х) не является таким же, как CMP (х, 0).

В Python 3, cmp() был удален в пользу богатого сравнения. Для имп(), Python 3 предполагает (https://docs.python.org/3/whatsnew/3.0.html):

def cmp(a, b): 
    return (a > b) - (a < b) 

, который отлично подходит для имп(), но опять-таки не может быть использован для знака(), так как операторы сравнения не должны возвращать булевы (https://docs.python.org/3/reference/datamodel.html#object.lt).

Чтобы справиться с этой возможностью, результаты сравнения должны быть принуждены к булевым:

def sign(a): 
    return bool(x > 0) - bool(x < 0) 

Это работает для любого типа, который полностью упорядоченный (включая специальные значения, как NaN или бесконечности).

Смежные вопросы