Угловой случай с позиционными параметрами в Python 3.8? - PullRequest
0 голосов
/ 30 октября 2019

Я возился с позиционными параметрами, указанными в PEP 570 и представлен с Python 3.8 , и мне просто интересно узнать конкретный угловой случай.

Допустим, я определяю функцию следующим образом (независимо от того, является ли это хорошим дизайном или имеет какой-либо смысл):

def func(p1, p2=None, p3=None, /): 
    print(p1, p2, p3)

Итак, есть один обязательный параметр (p1), за которым следуют дванеобязательные параметры (p2 и p3). Я могу вызвать функцию с помощью p1, p1 и p2 или p1 и p2 и p3:

func(1)       # 1, None, None
func(1, 2)    # 1, 2, None
func(1, 2, 3) # 1, 2, 3

Но я не могу просто вызватьэто с p1 и аргументом для p3 при сохранении значения по умолчанию для p2, поскольку я не могу предоставить аргументы для ключевых слов:

func(1, p3=3)

Это, конечно, вызовет TypeError:

TypeError: func() got some positional-only arguments passed as keyword arguments: 'p3'

Я не смог найти ни одного обсуждения или примеров по этому делу, поскольку все примеры в PEP 570 просто охватывают один необязательный параметр как часть аргументов только для позиции:

def name(p1, p2, /, p_or_kw, *, kw):
def name(p1, p2=None, /, p_or_kw=None, *, kw):
def name(p1, p2=None, /, *, kw):
def name(p1, p2=None, /):
def name(p1, p2, /, p_or_kw):
def name(p1, p2, /):

Таким образом, мой вопрос таков: является ли это предполагаемым поведением, когда вызывающая сторона предоставляет несколько необязательных аргументов слева направо, переопределяя их в принудительном порядке? Это на самом деле особенность позиционных аргументов?

Ответы [ 2 ]

0 голосов
/ 31 октября 2019

Хорошим примером точно описанного поведения, которое меня смутило, является eval Builtin, который имеет два необязательных аргумента только для позиционирования:

eval(source, globals=None, locals=None, /)
    Evaluate the given source in the context of globals and locals.

    The source may be a string representing a Python expression
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.

Так что нет способа указать только locals:

>>> eval("1 + 2 + x", locals={'x': 3})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: eval() takes no keyword arguments

Позиционно, globals должен быть предоставлен первым (если locals опущено, они по умолчанию globals в любом случае):

>>> eval("1 + 2 + x", {'x': 3}) # globals = locals
6

... или, если они должны отличаться:

>>> eval("1 + 2 + x", {'x': 3}, {'x': 4})
7

Итак, чтобы ответить на вопрос: это именно то, что и предполагалось.

0 голосов
/ 30 октября 2019

Является ли это предполагаемым поведением, когда вызывающая сторона предоставляет несколько необязательных аргументов слева направо, переопределяя их в принудительном порядке? Это на самом деле особенность позиционных аргументов?

Мало того, что это «предполагаемое поведение» позиционных аргументов, это в значительной степени его определение.

func(1, p3=3) прямо противоречит использованию / в сигнатуре функции, так как она предоставляет ключевое слово аргумент функции, которая принимает только позиционные аргументы. Тот факт, что p2 имеет значение по умолчанию, не имеет значения (хотя, как вы обнаружили, он довольно бесполезен).

Я буду продолжать искать явное объяснение в документации, но его может и не быть. Это в основном прямое следствие использования /.

Однако , PEP570 включает в себя этот пример :

def name(positional_only_parameters, /, positional_or_keyword_parameters,
         *, keyword_only_parameters):

Что предполагает, что мы можем переписатьfunc as:

def func(p1,p3=None, /, p2=None):
    print(p1, p2, p3)

Тогда оба из них работают:

func(1, 3)
func(1, 3, 2)

Вывод

1 None 3
1 2 3

Попробуйте онлайн

...