Orden de los argumentos por defecto y no por defecto

En Python, entiendo que los argumentos predeterminados vienen al final y que los argumentos no predeterminados no pueden seguir un argumento predeterminado. Eso está bien. Como por ejemplo:

>>> def foo(x=0, y): return x, y SyntaxError: non-default argument follows default argument 

Eso está bien como se esperaba.

Sin embargo, ¿qué pasa con el caso cuando quiero que el primer argumento sea el predeterminado ? Como por ejemplo, como se desprende del código anterior, x tiene que ser el primer argumento y debe tener un valor predeterminado de 0.

¿Es posible hacer esto? Lo pregunto porque incluso en la función de range , supongo que es algo como esto:

 def range(start=0, end): pass 

Entonces, ¿cómo se hace esto y, si no es posible, cómo se implementa por range ? Tenga en cuenta que estoy insistiendo en que el primer argumento sea el predeterminado, ese es todo el punto. Estoy usando el range como ejemplo porque encaja perfectamente con mi problema. Por supuesto, uno podría implementar el range como range def range(end, start=0) , pero ese no es el punto.

    Bueno, el range es el código C que puede hacer esto un poco mejor. De todas formas, puedes hacer esto:

     def range(start, stop=None): if stop is None: # only one arg, treat stop as start ... stop = start start = 0 ... 

    y documentar la función en consecuencia.

    No se implementa por range . Puede usar *args o **args y tratar la tupla o el dict como desee. Por ejemplo:

     def f (* args):
       si len (args) == 1:
          imprimir "asumiendo que el primero es predeterminado"
       elif len (args) == 2:
          imprimir "se pasaron dos argumentos"
       más:
          Imprimir "Quejándose"
    

    Hay un par de enfoques. El primero sería cambiar los argumentos en la función, si algunos de los argumentos son “Ninguno”. Eso funcionaría así.

     def range1(value, end=None): if end == None: end = value value = 0 return _generate_range_values(value, end) 

    El otro método primario sería que su función obtenga una lista de todos los argumentos que recibe. Entonces puede decidir qué hacer, según el número de argumentos.

     def range2(*args): if len(args) == 1: start = 0 end = int(args[0]) elif len(args) == 2: start = int(args[0]) end = int(args[1]) return _generate_range_values(start, end) 

    El tercero sería animar a los usuarios a pasar argumentos con nombre a su función, lo que hace que el orden sea menos importante.

     def range3(end, start=0): return _generate_range_values(start, end) 

    Luego, los usuarios lo llamarían con el argumento de inicio con nombre cuando quisieran algo más que 0. (Aunque el argumento con nombre no sería necesario, mantiene el código claro).

     for i in range3(55, start=12) 

    Puedes manejar las Excepciones tú mismo si realmente quieres eso.

     def Range(start=0, end=None): if end is None: raise AttributeError("end value not specified") pass 

    No tengo el código para el rango, pero estoy seguro de que realiza este tipo de truco:

     def range(start, stop=None, step=1): if stop is None: start, stop = 0, start ... 

    Edición: Código corregido según el comentario de martineau.