Хорошая практика - написать функцию, которая может возвращать boolean или json в python? [dубликат]

Бит Twiddling Hacks предлагает отличную коллекцию, er, bit twiddling hacks, с обсуждением производительности / оптимизации. Мое любимое решение вашей проблемы (с этого сайта) - «умножить и искать»:

unsigned int v; // find the number of trailing zeros in 32-bit v int r; // result goes here static const int MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];

Полезные ссылки:

"Использование последовательностей Bruijn для индексации 1 в компьютере Word "- Объяснение, почему работает вышеуказанный код. «Представление совета директоров> Битовые биты> BitScan» - Детальный анализ этой проблемы с уделением особого внимания программированию шахмат
20
задан SilentGhost 3 December 2009 в 12:34
поделиться

7 ответов

Почему функции возвращают значения согласованного типа? Для соблюдения следующих двух правил:

Правило 1 - функция имеет «тип» - входы, отображаемые на выходы. Он должен возвращать согласованный тип результата, или он не является функцией. Это беспорядок.

Математически мы говорим, что некоторая функция F является отображением из области D в диапазон R. F: D - & gt; R . Область и диапазон образуют «тип» функции. Типы ввода и тип результата столь же важны для определения функции, как имя или тело.

Правило 2 - когда у вас есть «проблема» или не удается вернуть правильный результат , вызывать исключение.

  def x (foo): if 'bar' в foo: return (foo, 'bar') raise Exception ("oh, dear me.")  

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

«Не было бы дешевле памяти мудрый, чтобы вернуть Нет? Неверный вопрос.

Точка not предназначена для оптимизации памяти за счет ясного, понятного и очевидного кода.

25
ответ дан S.Lott 16 August 2018 в 08:09
поделиться
  • 1
    +1 для последнего предложения. Поддержание работоспособности - это настоящая стоимость, которую следует учитывать с 99% кода. – Andrzej Doyle 3 December 2009 в 12:39
  • 2
    Тем не менее, вы действительно не дали никаких оснований для этого вывода. Ваши причины, по которым функция должна возвращать один и тот же тип, включают в себя: 1) «любая функция, которая в противном случае является беспорядок» и 2), она должна возвращать тот же тип, чтобы соответствовать математическому определению функции. Первая - это чистая риторика, а вторая - вопрос. Я не согласен. Я с готовностью откладываю ваше мастерство на python. Но могли бы вы дать некоторые практические причины, почему люди должны соблюдать эту практику? Например, нарушение этой практики, конечно, не будет всегда & lt; i & gt; всегда & lt; / i & gt; сделать код нечитаемым. – twneale 3 December 2009 в 15:01
  • 3
    -1: Когда слово «функция» используется в контексте этого вопроса, разумно предположить, что это означает функцию Python, а не математическую функцию. Вполне нормально, что функции Python не всегда возвращают один и тот же тип (например, copy.copy () ). В вашем правиле 1 говорится, что некоторые функции Python не являются действительно функциями, что является семантическим беспорядком. – Scott Griffiths 3 December 2009 в 15:58
  • 4
    Функция copy.copy () по-прежнему является функцией. Подпись (домен и диапазон) фиксированы. Для данного значения домена диапазон предсказуем. Да, есть много комбинаций, но комбинации тривиально предсказуемы. Это функция с простой, предсказуемой сигнатурой в математическом смысле. Это не самопроизвольно возвращает непредсказуемый тип. – S.Lott 3 December 2009 в 16:22
  • 5
    – Scott Griffiths 3 December 2009 в 18:03

Я лично считаю, что это нормально для функции возврата кортежа или None. Однако функция должна возвращать не более двух разных типов, а вторая должна быть None. Функция никогда не должна возвращать строку и список, например.

5
ответ дан Echo 16 August 2018 в 08:09
поделиться
  • 1
    Кроме того, функция, которая возвращает либо 3-uple, либо 0-uple, как в примере, выглядит плохой вкус для меня. & quot; return None & quot; также является моим предпочтительным выбором в этом примере. – user192472 4 January 2010 в 15:45

Лучшая практика в том, что должна возвращать функция, сильно варьируется от языка к языку и даже между различными проектами Python.

Для Python в целом я согласен с предпосылкой, что возвращение None является плохим, если ваша функция обычно возвращает итерабельность, потому что повторение без тестирования становится невозможным. Просто верните пустой итерабельный в этом случае, он все равно будет проверять False, если вы используете стандартное тестирование истинности Python:

  ret_val = x (), если ret_val: do_stuff (ret_val)  

и по-прежнему разрешить вам перебирать его без тестирования:

  для дочернего элемента в x (): do_other_stuff (child)  

Для функций которые, вероятно, вернут одно значение, я думаю, что возвращение None является совершенно приемлемым, просто документируйте, что это может произойти в вашей docstring.

9
ответ дан Jeffrey Harris 16 August 2018 в 08:09
поделиться
  • 1
    Мне очень нравится аргумент итеративного и единственного значения, который вы сделали, но я думаю, что его нужно немного изменить - что, если iterable является кортежем? Обычный python-ism для возвращаемых по tuple функций - это a, b = x () , что приведет к возникновению исключения TypeError , если None был возвращен. Кроме того, строки являются итерируемыми и одним значением. Возможно, аргумент должен быть динамической длиной итерабельнее против одного значения? Или, в более общем плане, возможно, mutable vs. non-mutable? – CivFan 6 September 2016 в 19:58
  • 2
    Если вы возвращаете кортеж с фиксированным размером, то да, я согласен, возврат Нет - это плохая идея, но это просто особый случай «если вызывающие пользователи хотели бы разрушить ваш ответ без тестирования», который уже распространяется на «quot , не возвращайте None, если он повторяется & quot ;. Что касается строк, я думаю, что обычно лучше возвращать пустую строку, чем None, но very иногда пустая строка, а None - разные значения, и в этом случае вы должны снова документировать это. – Jeffrey Harris 7 September 2016 в 15:24
  • 3
    ... ', который уже покрыт «не возвращать None, если он повторяется». О, правильно. Мой ум, должно быть, промахнулся. Ну, я думаю, что строка остается по-прежнему, спасибо за это. – CivFan 7 September 2016 в 20:02

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

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

2
ответ дан Pontus Gagge 16 August 2018 в 08:09
поделиться

Вот мои мысли обо всем этом, и я попытаюсь также объяснить, почему я считаю, что принятый ответ в основном неверен.

Прежде всего функции программирования! = математические функции .

  • Функции не обязательно должны иметь вход
  • Функции не должны иметь выход
  • Функции не должны отображать входные данные на выходе (из-за двух предыдущих точек пули)

Функция в плане программирования для просмотра просто как блок памяти с началом (точка входа функции), тело (пустое или иное) и точка выхода (одна или несколько в зависимости от реализации), все из которых существуют для повторного использования кода, который вы написали. Даже если вы не видите этого, функция всегда «возвращает» что-то. Это что-то на самом деле является адресом следующего оператора сразу после вызова функции. Это то, что вы увидите во всей своей славе, если вы сделаете какое-то действительно низкоуровневое программирование на языке ассемблера (я смею пойти на лишнюю милю и сделать какой-то машинный код вручную, как Линус Торвальдс, который так часто упоминает об этом во время его семинары и интервью: D). Кроме того, вы также можете внести некоторый вклад, а также вывести какой-то вывод. Вот почему

  def foo (): pass  

- совершенно правильная часть кода.

Итак, почему бы возвращать несколько типы будут плохими? Ну ... Это совсем не так, если ты не злоупотребляешь им. Это, конечно, проблема с плохими навыками программирования и / или незнанием того, что может использовать язык, который вы используете.

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

Насколько я знаю - да, возвращая NoneType Объект был бы намного дешевле памяти. Вот небольшой эксперимент (возвращаемые значения - байты):

  & gt; gt;  sys.getsizeof (None) 16 & gt;  sys.getsizeof (()) 48  

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

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

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

Однако Python является объектно-ориентированным языком и как таковым наследованием, абстрактными классами и т. Д., И все это часть всех махинаций ООП. Это может привести даже к созданию классов «на лету», которые я обнаружил несколько месяцев назад, и был ошеломлен (никогда не видел этого на C / C ++).

Боковое примечание: Вы можете немного прочитать о метаклассах и динамических классах в этой хорошей обзорной статье с большим количеством примеров.

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

  • Duck typing - часто часть динамического набирать языки, которые Python является представителем
  • Factory шаблона проектирования метода - в основном это функция, которая возвращает различные объекты на основе ввода, который он получает.

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

5
ответ дан rbaleksandar 16 August 2018 в 08:09
поделиться

Не так ясно, что функция всегда должна возвращать объекты ограниченного типа или что возвращение None является неправильным. Например, re.search может возвращать объект _sre.SRE_Match или объект NoneType :

  import re match = re.search (  'a', 'a') type (match) # & lt; type '_sre.SRE_Match' & gt;  match = re.search ('a', 'b') type (match) # & lt; type 'NoneType' & gt;   

Разработанный таким образом, вы можете проверить соответствие с idiom

 , если совпадение: # do xyz  

Если разработчикам потребовался re.search для возврата объекта _sre.SRE_Match , тогда идиома должна была бы измениться на

 , если match.group (1)  None: # do xyz  

Не было бы никакого существенного усиления, требуя, чтобы re.search всегда возвращал объект _sre.SRE_Match .

] Итак, я думаю, что вы создаете функцию, которая должна зависеть от ситуации и, в частности, от того, как вы планируете использовать эту функцию.

Также обратите внимание, что оба _sre.SRE_Match и NoneType являются экземплярами объекта, поэтому в широком смысле они одного типа. Поэтому правило, что «функции всегда должны возвращать только один тип», совершенно бессмысленно.

Сказав это, есть прекрасная простота для функций, возвращающих объекты, все из которых имеют одни и те же свойства. (Утиная печать, а не статическая типизация, - это путь python!) Это может позволить вам объединить функции: foo (bar (baz))) и с уверенностью знать тип объекта, который вы получите на другом конце.

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

17
ответ дан unutbu 16 August 2018 в 08:09
поделиться
  • 1
    Я думаю, что это лучший ответ, поскольку он касается парадигмы Python. Возвращение Нет, как правило, отлично, независимо от того, что еще возвращает функция, так как обычно проверять возвращаемое значение. например x = foo (), если x: # делать что-либо. Все, кроме 0, (), "" или None, будет равно True. – BretD 1 September 2016 в 19:21
  • 2
    даже если re.search всегда будет возвращать объект соответствия, он все равно может быть проверен с помощью , если match - если была выполнена соответствующая функция __ ненулевой __ . – jakun 9 April 2017 в 17:03

Если x вызывается так

  foo, bar = x (foo)  

return None приведет к

  TypeError: объект «NoneType» не является итерируемым  

, если «bar» не в foo .

Пример

  def x (foo): если «bar» в foo: return (foo, 'bar'),  return no foo, bar = x (["foo", "bar", "baz"]) print foo, bar foo, bar = x (["foo", "NOT THERE", "baz"]) print foo,  bar  

В результате:

  ['foo', 'bar', 'baz'] bar Traceback (последний последний вызов): Файл "  f.py ", строка 9, в & lt; module & gt;  foo, bar = x (["foo", "NOT THERE", "baz"]) TypeError: объект «NoneType» не итерируется  
4
ответ дан user 16 August 2018 в 08:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: