Есть ли способ установить параметр по умолчанию, равный другому значению параметра?

Например, у меня есть основной метод, который возвращает список перестановок.

import itertools
def perms(elements, set_length=elements):
    data=[]
    for x in range(elements):
        data.append(x+1)
    return list(itertools.permutations(data, set_length))

Теперь я понимаю, что в своем текущем состоянии этот код не будет выполняться, потому что вторые elements не определены, но есть ли и элегантный способ выполнить то, что я пытаюсь сделать здесь? Если это все еще не ясно, я хочу сделать значение setLength по умолчанию равным первому переданному аргументу. Спасибо.

Ответ 1

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

Установите значение по умолчанию None и определите, что:

def perms(elements, setLength=None):
    if setLength is None:
        setLength = elements

Если вам нужно указать None в качестве аргумента, используйте другое значение часового:

_sentinel = object()

def perms(elements, setLength=_sentinel):
    if setLength is _sentinel:
        setLength = elements

Теперь вызывающие абоненты могут установить setLength в None, и он не будет считаться значением по умолчанию.

Ответ 2

Из-за того, как Python обрабатывает привязки и параметры по умолчанию...

Стандартный способ:

def perms(elements, setLength=None):
    if setLength is None:
        setLength = elements

И еще один вариант:

def perms(elements, **kwargs):
    setLength = kwargs.pop('setLength', elements)

Хотя для этого требуется явно использовать perms(elements, setLength='something else'), если вы не хотите по умолчанию...

Ответ 3

Вы должны сделать что-то вроде:

def perms(elements,setLength=None):
    if setLength is None:
        setLength = elements

Ответ 4

Ответ 1:

Решение сверху выглядит так:

def cast_to_string_concat(a, b, c=None):
  c = a if c is None else c

  return str(a) + str(b) + str(c)

Хотя такой подход решит множество потенциальных проблем (и, возможно, ваших)! Я хотел написать функцию, в которой возможный ввод для переменной " c " действительно является единственным None, поэтому мне пришлось больше копаться.

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

A='A'
B='B'
my_var = None

Урожайность:

cast_to_string_concat(A, B, my_var):
>>>'ABA'

В то время как пользователь может ожидать, что, поскольку он вызвал функцию с тремя переменными, он должен напечатать три переменные, например:

cast_to_string_concat(A, B, my_var):
>>> 'ABNone' # simulated and expected outcome

Таким образом, эта реализация игнорирует третью переменную, даже когда она была объявлена, поэтому это означает, что функция больше не может определять, была ли определена переменная " c ".

Таким образом, для моего варианта использования значение по умолчанию None не совсем подходит.

Для ответов, которые предлагают это решение, прочитайте эти:


Но если это не работает для вас, то, возможно, продолжайте читать!


Комментарий в первой ссылке выше упоминает использование _sentinel определенного object().

Таким образом, это решение исключает использование None и заменяет его на object() посредством использования подразумеваемого частного sentinel.


Ответ 2:

_sentinel = object()
def cast_to_string_concat(a, b, c=_sentinel):
  c = a if c == _sentinel else c

  return str(a) + str(b) + str(c)
A='A'
B='B'
C='C'

cast_to_string_append(A,B,C)
>>> 'ABC'

cast_to_string_concat(A,B)
>>> 'ABA'

Так что это довольно круто! Это правильно обрабатывает вышеупомянутый крайний случай! Посмотреть на себя:


A='A'
B='B'
C = None

cast_to_string_concat(A, B, C)
>>> 'ABNone'

Итак, мы закончили, верно? Есть ли вероятный способ, что это может не сработать? Хм... наверное нет! Но я сказал, что это был ответ из трех частей, так что вперед! ;)


Для полноты картины, давайте представим, что наша программа работает в пространстве, где каждый возможный сценарий действительно возможен. (Это может быть неоправданным предположением, но я полагаю, что можно получить значение _sentinel с достаточным количеством информации об архитектуре компьютера и реализации выбора объекта. Итак, если вы хотите, давайте предположим, что это действительно так. возможно, и давайте представим, что мы решили проверить эту гипотезу, ссылающуюся на _sentinel как определено выше.


_sentinel = object()
def cast_to_string_concat(a, b, c=_sentinel):
  c = a if c == _sentinel else c

  return str(a) + str(b) + str(c)
A='A'
B='B'
S = _sentinel

cast_to_string_append(A,B,S)
>>> 'ABA'

Подождите минуту! Я ввел три аргумента, поэтому я должен увидеть объединение строк из трех из них вместе!


* очередь на въезд в страну непредвиденных последствий *

Я имею в виду, не на самом деле. Ответ: "Это ничтожно малый край дела !!" или его подобие совершенно оправдано.

И это чувство правильно! В этом случае (и, вероятно, в большинстве случаев) об этом действительно не стоит беспокоиться!

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

Упражнение, оставленное читателю:

Отказываясь от этой техники, вы можете прямо утверждать c=object(), однако, честно говоря, я не получил такой способ работать на меня. Мое исследование показало, что c == object() - False, а str(c) == str(object()) - также False, и именно поэтому я использую реализацию от Martin Pieters.


Хорошо, после этого долгого упражнения мы вернулись!

Напомним, что цель состоит в том, чтобы написать функцию, которая потенциально может иметь n входов, и только если одна переменная не указана - тогда вы скопируете другую переменную в положение i.

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

Поэтому, если вы ищете решение, которое не ставит под угрозу потенциальные входные данные, где допустимым входным значением может быть None, object() или _sentinel... тогда (и только тогда), на данный момент, я думаю, мое решение будет полезно. Источником вдохновения для этой техники послужила вторая часть ответа Джона Клемента.


Ответ 3:

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

По шагам:

  1. Переименуйте функцию в нечто подобное
  2. Удалите настройки по умолчанию для вашего необязательного параметра
  3. Начните создавать новую функцию чуть выше и добавьте оригинальную функцию в.
 def cast_to_string_concat(*args):
  1. Определите арность вашей функции - (я нашел это слово в моем поиске... это число параметров, переданных в данную функцию)
  2. Используйте внутри себя инструкцию case, которая определяет, правильно ли вы ввели правильное количество переменных, и внесите соответствующие коррективы!
def cast_to_string_append(*args):

    def string_append(a, b, c):
        # this is the original function, it is only called within the wrapper
        return str(a) + str(b) + str(c)

    if len(args) == 2:
        # if two arguments, then set the third to be the first
        return string_append(*args, args[0])

    elif len(args) == 3:
        # if three arguments, then call the function as written
        return string_append(*args)

    else:
        raise Exception(f'Function: cast_to_string_append() accepts two or three arguments, and you entered {len(args)}.')

# instantiation

A='A'
B='B'
C='C'
D='D'

_sentinel = object()
S = _sentinel

N = None
""" Answer 3 Testing """

# two variables

cast_to_string_append(A,B)

>>> 'ABA'


# three variables

cast_to_string_append(A,B,C)

>>> 'ABC'


# three variables, one is _sentinel

cast_to_string_append(A,B,S)

>>>'AB<object object at 0x10c56f560>'


# three variables, one is None

cast_to_string_append(A,B,N)

>>>'ABNone'


# one variable

cast_to_string_append(A)

>>>Traceback (most recent call last):
>>>  File "<input>", line 1, in <module>
>>>  File "<input>", line 13, in cast_to_string_append
>>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 1.

# four variables

cast_to_string_append(A,B,C,D)

>>>Traceback (most recent call last):
>>>  File "<input>", line 1, in <module>
>>>  File "<input>", line 13, in cast_to_string_append
>>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 4.


# ten variables

cast_to_string_append(0,1,2,3,4,5,6,7,8,9)

>>>Traceback (most recent call last):
>>>  File "<input>", line 1, in <module>
>>>  File "<input>", line 13, in cast_to_string_append
>>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 10.


# no variables

cast_to_string_append()

>>>Traceback (most recent call last):
>>>  File "<input>", line 1, in <module>
>>>  File "<input>", line 13, in cast_to_string_append
>>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 0.

""" End Answer 3 Testing """

Итак, в итоге:

  • Ответ 1 - самый простой ответ и работает в большинстве случаев.
def cast_to_string_concat(a, b, c=None):
  c = a if c is None else c

  return str(a) + str(b) + str(c)
  • Ответ 2 - используйте, если None фактически не обозначает пустой параметр, переключаясь на object() через _sentinel.
_sentinel = object()
def cast_to_string_concat(a, b, c=_sentinel):
  c = a if c == _sentinel else c

  return str(a) + str(b) + str(c)
  • Ответ 3 ищет общее решение, использующее функцию-обертку с произвольной арностью, используя *args, и обрабатывает допустимые случаи внутри:
def cast_to_string_append(*args):

    def string_append(a, b, c):
        # this is the original function, it is only called within the wrapper
        return str(a) + str(b) + str(c)

    if len(args) == 2:
        # if two arguments, then set the third to be the first
        return string_append(*args, args[0])

    elif len(args) == 3:
        # if three arguments, then call the function as written
        return string_append(*args)

    else:
        raise Exception(f'Function: cast_to_string_append() accepts two or three arguments, and you entered {len(args)}.')

Используйте то, что работает для вас! Но для меня я буду использовать вариант 3;)