Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

279
Visualizações
Cuente cuántos argumentos pasaron como posicionales

Si tengo una funcion

 def foo(x, y): pass

¿Cómo puedo saber, desde dentro de la función, si y se pasó posicionalmente o con su palabra clave?

me gustaría tener algo como

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

para que yo consiga

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

Me doy cuenta de que originalmente no especifiqué esto, pero ¿es posible hacer esto mientras se conservan las anotaciones de tipo? La respuesta principal hasta ahora sugiere usar un decorador; sin embargo, eso no conserva el tipo de retorno

over 4 years ago · Santiago Trujillo
6 Respostas
Responde à pergunta

0

Puede engañar al usuario y agregar otro argumento a la función como este:

 def foo(x,y1=None,y=None): if y1 is not None: print('y was passed positionally!') else: print('y was passed with its keyword')

no recomiendo hacerlo pero si funciona

over 4 years ago · Santiago Trujillo Relatório

0

Puedes crear un decorador, así:

 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

Por supuesto, puede mejorar esto permitiendo que el decorador acepte argumentos. Por lo tanto, puede pasar el parámetro que desea verificar. Que sería algo como esto:

 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

Creo que agregar functools.wraps preservaría las anotaciones, la siguiente versión también permite realizar la verificación de todos los argumentos (usando 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 Relatório

0

Al final, si vas a hacer algo como esto:

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

Puedes hacerlo:

 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

O solo permita que se pasen posicionalmente:

 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

Consulte PEP 570 y PEP 3102 para obtener más información.

over 4 years ago · Santiago Trujillo Relatório

0

Normalmente no es posible. En cierto sentido: el lenguaje no está diseñado para permitirle distinguir ambas formas.

Puede diseñar su función para tomar diferentes parámetros: posicionales y con nombre, y verificar cuál se pasó, de la siguiente manera:

 def foo(x, y=None, /, **kwargs): if y is None: y = kwargs.pop(y) received_as_positional = False else: received_as_positional = True

El problema es que, aunque al usar solo parámetros posicionales como se indicó anteriormente, podría obtener y en ambos sentidos, no se mostraría ni una sola vez para un usuario (o IDE) que inspeccione la firma de la función.

Tengo la sensación de que solo quiere saber esto por el simple hecho de saberlo. Si realmente tiene la intención de diseñar una API, le sugiero que reconsidere su API. No debería haber ninguna diferencia en el comportamiento, a menos que ambos lo sean. parámetros inequívocamente diferentes desde el punto de vista del usuario.

Dicho esto, el camino a seguir sería inspeccionar el marco de la persona que llama y verificar el código de bytes alrededor del lugar donde se llama a la función:

 In [24]: import sys, dis In [25]: def foo(x, y=None): ...: f = sys._getframe().f_back ...: print(dis.dis(f.f_code)) ...: In [26]: foo(1, 2) 1 0 LOAD_NAME 0 (foo) 2 LOAD_CONST 0 (1) 4 LOAD_CONST 1 (2) 6 CALL_FUNCTION 2 8 PRINT_EXPR 10 LOAD_CONST 2 (None) 12 RETURN_VALUE None In [27]: foo(1, y=2) 1 0 LOAD_NAME 0 (foo) 2 LOAD_CONST 0 (1) 4 LOAD_CONST 1 (2) 6 LOAD_CONST 2 (('y',)) 8 CALL_FUNCTION_KW 2 10 PRINT_EXPR 12 LOAD_CONST 3 (None) 14 RETURN_VALUE

Entonces, como puede ver, cuando y se llama como parámetro con nombre, el código de operación para la llamada es CALL_FUNCTION_KW , y el nombre del parámetro se carga en la pila inmediatamente antes.

over 4 years ago · Santiago Trujillo Relatório

0

En foo , puede pasar la pila de llamadas de traceback a positionally , que luego analizará las líneas, encontrará la línea donde se llama a foo y luego analizará la línea con ast para ubicar las especificaciones de parámetros posicionales (si corresponde):

 import traceback, ast, re def get_fun(name, ast_obj): if isinstance(ast_obj, ast.Call) and ast_obj.func.id == name: yield from [i.arg for i in getattr(ast_obj, 'keywords', [])] for a, b in getattr(ast_obj, '__dict__', {}).items(): yield from (get_fun(name, b) if not isinstance(b, list) else \ [i for k in b for i in get_fun(name, k)]) def passed_positionally(stack): *_, [_, co], [trace, _] = [re.split('\n\s+', i.strip()) for i in stack] f_name = re.findall('(?:line \d+, in )(\w+)', trace)[0] return list(get_fun(f_name, ast.parse(co))) def foo(x, y): if 'y' in passed_positionally(traceback.format_stack()): print('y was passed with its keyword') else: print('y was passed positionally') foo(1, y=2)

Producción:

 y was passed with its keyword

Notas:

  1. Esta solución no requiere ninguna envoltura de foo . Solo es necesario capturar el rastreo.
  2. Para obtener la llamada foo completa como una cadena en el rastreo, esta solución debe ejecutarse en un archivo, no en el shell.
over 4 years ago · Santiago Trujillo Relatório

0

Adaptado de la respuesta de @Cyttorak, aquí hay una forma de hacerlo que mantiene los tipos:

 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)

La salida es:

 $ 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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda