Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

103
Vistas
Count how many arguments passed as positional

If I have a function

def foo(x, y):
    pass

how can I tell, from inside the function, whether y was passed positionally or with its keyword?

I'd like to have something like

def foo(x, y):
    if passed_positionally(y):
        print('y was passed positionally!')
    else:
        print('y was passed with its keyword')

so that I get

>>> foo(3, 4)
y was passed positionally
>>> foo(3, y=4)
y was passed with its keyword

I realise I didn't originally specify this, but is it possible to do this whilst preserving type annotations? The top answer so far suggests using a decorator - however, that does not preserve the return type

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

You can create a decorator, like this:

def checkargs(func):
    def inner(*args, **kwargs):
        if 'y' in kwargs:
            print('y passed with its keyword!')
        else:
            print('y passed positionally.')
        result = func(*args, **kwargs)
        return result
    return inner

>>>  @checkargs
...: def foo(x, y):
...:     return x + y

>>> foo(2, 3)
y passed positionally.
5

>>> foo(2, y=3)
y passed with its keyword!
5

Of course you can improve this by allowing the decorator to accept arguments. Thus you can pass the parameter you want to check for. Which would be something like this:

def checkargs(param_to_check):
    def inner(func):
        def wrapper(*args, **kwargs):
            if param_to_check in kwargs:
                print('y passed with its keyword!')
            else:
                print('y passed positionally.')
            result = func(*args, **kwargs)
            return result
        return wrapper
    return inner

>>>  @checkargs(param_to_check='y')
...: def foo(x, y):
...:     return x + y

>>> foo(2, y=3)
y passed with its keyword!
5

I think adding functools.wraps would preserve the annotations, following version also allows to perform the check over all arguments (using inspect):

from functools import wraps
import inspect

def checkargs(func):
    @wraps(func)
    def inner(*args, **kwargs):
        for param in inspect.signature(func).parameters:
            if param in kwargs:
                print(param, 'passed with its keyword!')
            else:
                print(param, 'passed positionally.')
        result = func(*args, **kwargs)
        return result
    return inner

>>>  @checkargs
...: def foo(x, y, z) -> int:
...:     return x + y

>>> foo(2, 3, z=4)
x passed positionally.
y passed positionally.
z passed with its keyword!
9

>>> inspect.getfullargspec(foo)
FullArgSpec(args=[], varargs='args', varkw='kwargs', defaults=None, 
kwonlyargs=[], kwonlydefaults=None, annotations={'return': <class 'int'>})
                                             _____________HERE____________
over 4 years ago · Santiago Trujillo Denunciar

0

At the end, if you are going to do something like this:

def foo(x, y):
    if passed_positionally(y):
        raise Exception("You need to pass 'y' as a keyword argument")
    else:
        process(x, y)

You can do this:

def foo(x, *, y):
    pass

>>> foo(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes 1 positional argument but 2 were given

>>> foo(1, y=2) # works

Or only allow them to be passed positionally:

def foo(x, y, /):
    pass

>>> foo(x=1, y=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got some positional-only arguments passed as keyword arguments: 'x, y'

>>> foo(1, 2) # works

See PEP 570 and PEP 3102 for more.

over 4 years ago · Santiago Trujillo Denunciar

0

Adapted from @Cyttorak 's answer, here's a way to do it which maintains the types:

from typing import TypeVar, Callable, Any, TYPE_CHECKING

T = TypeVar("T", bound=Callable[..., Any])

from functools import wraps
import inspect

def checkargs() -> Callable[[T], T]:
    def decorate(func):
        @wraps(func)
        def inner(*args, **kwargs):
            for param in inspect.signature(func).parameters:
                if param in kwargs:
                    print(param, 'passed with its keyword!')
                else:
                    print(param, 'passed positionally.')
            result = func(*args, **kwargs)
            return result
        return inner
    return decorate

@checkargs()
def foo(x, y) -> int:
    return x+y

if TYPE_CHECKING:
    reveal_type(foo(2, 3))
foo(2, 3)
foo(2, y=3)

Output is:

$ mypy t.py 
t.py:27: note: Revealed type is 'builtins.int'
$ python t.py 
x passed positionally.
y passed positionally.
x passed positionally.
y passed with its keyword!
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda