Asterisco vazio em argumentos de função?

241

O que faz um asterisco simples nos argumentos de uma função?

Quando olhei para o módulo pickle , vi o seguinte:

pickle.dump(obj, file, protocol=None, *, fix_imports=True)

Eu sei sobre um asterisco único e duplo precedendo argumentos (para número variável de argumentos), mas isso não precede nada. E tenho certeza que isso não tem nada a ver com picles. Provavelmente é apenas um exemplo disso. Eu só aprendi o nome dele quando enviei para o intérprete:

>>> def func(*):
...     pass
...
  File "<stdin>", line 1
SyntaxError: named arguments must follow bare *

Se isso importa, estou no python 3.3.0.

Eric
fonte

Respostas:

221

Nua *é usada para forçar o chamador a usar argumentos nomeados - portanto, você não pode definir uma função *como argumento quando não possui os seguintes argumentos de palavra-chave.

Veja esta resposta ou a documentação do Python 3 para obter mais detalhes.

Kimvais
fonte
3
Observe que todos os argumentos posicionais (sem nome), inclusive *args, devem ocorrer antes do nada *.
usar o seguinte
4
Observe também que existe uma espécie de contraparte, /, que marca o final dos argumentos apenas de posição ( stackoverflow.com/questions/28243832/… ).
Stephen
2
@BallpointBen: *está no lugar de *argse vice-versa; eles não podem coexistir em uma assinatura. Por isso eles escolheram *; anteriormente, *argsera a única maneira de forçar argumentos puramente posicionais, e marcava o final dos argumentos que poderiam ser passados ​​posicionalmente (uma vez que reunia todos os argumentos posicionais restantes, eles podiam alcançar os seguintes argumentos nomeados). *significa que os mesmos "argumentos posicionais não podem ir além daqui", mas a falta de um nome significa "mas eu não os aceitarei, porque escolhi não fornecer um local para colocá-los".
ShadowRanger 17/01
70

Enquanto a resposta original responde a pergunta completamente, basta adicionar um pouco de informações relacionadas. O comportamento do asterisco único deriva de PEP-3102. Citando a seção relacionada:

The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:

    def compare(a, b, *, key=None):
        ...

Em inglês simples, significa que, para passar o valor da chave, você precisará explicitamente passá-lo como key="value".

mu 無
fonte
Oh, isso torna as coisas muito mais claras. Portanto, na verdade, ter um argumento * é como ter um argumento args *, mas como você não o nomeou, seu único efeito é provavelmente devorar silenciosamente todos os argumentos posicionais restantes, a fim de forçar os argumentos restantes a serem palavras-chave -só.
Stephen
11
@ Stephen Eu também pensei originalmente, o efeito do bare *é devorar os argumentos posicionais restantes, mas esse não é o caso. Passar argumentos posicionais extras do que a função espera, gera um erro desse tipo:foo() takes exactly 1 positional argument (2 given)
Ajay M
19
def func(*, a, b):
    print(a)
    print(b)

func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb

o exemplo acima com ** kwargs

def func(*, a, b, **kwargs):
    print(a)
    print(b)
    print(kwargs)

func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}
laycat
fonte
6

Semanticamente, significa que os argumentos a seguir são apenas de palavra-chave; portanto, você receberá um erro se tentar fornecer um argumento sem especificar seu nome. Por exemplo:

>>> def f(a, *, b):
...     return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3

Pragmaticamente, isso significa que você deve chamar a função com um argumento de palavra-chave. Geralmente é feito quando seria difícil entender o objetivo do argumento sem a dica dada pelo nome do argumento.

Compare, por exemplo, sorted(nums, reverse=True)vs. se você escreveu sorted(nums, True). O último seria muito menos legível, então os desenvolvedores do Python escolheram fazer com que você escrevesse da maneira anterior.

kaya3
fonte
4

Suponha que você tenha uma função:

def sum(a,key=5):
    return a + key 

Você pode chamar esta função de duas maneiras:

sum(1,2) ou sum(1,key=2)

Suponha que você queira que a função sumseja chamada apenas usando argumentos de palavra-chave.

Você adiciona *à lista de parâmetros da função para marcar o final dos argumentos posicionais.

Então, a função definida como:

def sum(a,*,key=5):
    return a + key 

pode ser chamado apenas usando sum(1,key=2)

rok
fonte
-1

Eu encontrei o seguinte link para ser muito útil explicando *, *argse **kwargs:

https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/

Essencialmente, além das respostas acima, aprendi no site acima (crédito: https://pythontips.com/author/yasoob008/ ) o seguinte:

Com a função de demonstração definida primeiro abaixo, existem dois exemplos, um com *argse outro com**kwargs

def test_args_kwargs(arg1, arg2, arg3):
    print "arg1:", arg1
    print "arg2:", arg2
    print "arg3:", arg3

# first with *args
>>> args = ("two", 3,5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5

# now with **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two","arg1":5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3

Portanto, *argspermite criar dinamicamente uma lista de argumentos que serão obtidos na ordem em que são alimentados, enquanto **kwargspodem permitir a passagem de argumentos NAMED e podem ser processados ​​por NAME de acordo (independentemente da ordem em que eles são alimentados) .

O site continua, observando que a ordem correta dos argumentos deve ser:

some_func(fargs,*args,**kwargs)
lb_so
fonte
2
Esta resposta não tem quase nada a ver com a pergunta. Ele está usando uma versão obsoleta do python que não possui o recurso.
Antti Haapala