2012-09-04 28 views
8

Estoy tratando de imitar el comportamiento de las funciones incorporadas (como getattr) que permiten al usuario especificar un valor de retorno "predeterminado". Mi intento inicial era hacer estopython - devolviendo un valor predeterminado

def myfunc(foo, default=None): 
    # do stuff 
    if (default is not None): 
     return default 
    raise SomeException() 

El problema es que si los usuarios quiere None ser su valor de retorno, esta función en lugar podría lanzar una excepción. segundo intento:

def myfunc(foo, **kwargs): 
    # do stuff 
    if ('default' in kwargs): 
     return kwargs['default'] 
    raise SomeException() 

Esto aborda la cuestión antedicha y permite al usuario especificar cualquier valor arbitrario, pero introduce una molestia en el que el usuario siempre debe especificar default=bar en su función llama; no pueden simplemente proporcionar bar al final. Del mismo modo, podría usarse *args, pero evita que los usuarios usen default=bar si prefieren esa sintaxis.

La combinación de *args y **kwargs proporciona una solución práctica, pero parece que esto va a hacer un gran esfuerzo. También potencialmente máscaras llamadas a funciones inadecuadas (por ejemplo bar = myfunc(foo, baz, default=qux))

def myfunc(foo, *args, **kwargs): 
    # do stuff 
    if (len(args) == 1): 
     return args[0] 
    if ('default' in kwargs): 
     return kwargs['default'] 
    raise SomeException() 

¿Existe una solución más simple? (Python 3.2 si lo que importa)

Respuesta

14

Es necesario utilizar un centinela para detectar que un valor predeterminado no se ha establecido:

sentinel = object() 

def func(someparam, default=sentinel): 
    if default is not sentinel: 
     print("You passed in something else!") 

Esto funciona porque una instancia de object() siempre tendrá su propio ID de memoria y por lo tanto is solo devolverá True si el valor exacto se dejó en su lugar. Cualquier otro valor no se registrará como el mismo objeto, incluido None.

Verá diferentes variantes del truco anterior en varios proyectos diferentes de python. Cualquiera de los siguientes centinelas también funcionaría:

sentinel = [] 
sentinel = {} 
+0

que es mucho más simple, gracias! – Steve

+0

¿Hay alguna forma de evitar que la gente pase centinela? –

+2

@hayden: por supuesto que no, esto es python. Generalmente lo llamas '_sentinel' o similar para marcarlo como privado, pero eso no impide que las almas emprendedoras rompan todo importándolo de todos modos. No tiene sentido hacerlo, por supuesto. –

Cuestiones relacionadas