¿Python tiene una función "o igual" como || = en Ruby?

82

Si no es así, ¿cuál es la mejor forma de hacerlo?

Ahora mismo estoy haciendo (para un proyecto de django):

if not 'thing_for_purpose' in request.session:
    request.session['thing_for_purpose'] = 5

pero es bastante incómodo. En Ruby sería:

request.session['thing_for_purpose'] ||= 5

que es mucho mejor.

Sean W.
fuente
23
Tenga en cuenta que estos dos bits de código son en realidad muy diferentes: la versión de Python lo establece en 5 si no está en el dict en absoluto, mientras que la versión de Ruby también lo establece en 5 si se establece en cualquier valor falso.
Glenn Maynard
2
@Glenn, no muy diferente, pero sí bastante diferente. Dado que devuelve un valor hash no inicializado nil(falso en contexto booleano), este modismo se usa a menudo exactamente para el propósito que Sean lo haya usado. Sin embargo, el idioma solo funciona si, por supuesto, nily falseno son valores legítimos en el hash (lo cual es muy a menudo cierto, por lo que el idioma está bien)
horseyguy
8
@banister: No sé dónde se podría trazar la línea entre "muy" y "bastante", pero el punto es que estas no son declaraciones equivalentes, y es importante entender la diferencia. (False es muy a menudo un valor válido en un hash, que lo morderá cuando desee establecer un valor predeterminado para un campo booleano en verdadero; es una deficiencia significativa del idioma de Ruby).
Glenn Maynard
Sí, el ejemplo de Python es más parecido a definido-o , como el //=que se encuentra en perl . Creo que ||=también se origina en Perl.
draegtun
Ah, buenos puntos, todos. No había notado esta diferencia.
Sean W.

Respuestas:

186

La respuesta aceptada es buena para dictados, pero el título busca un equivalente general al operador || = de Ruby. Una forma común de hacer algo como || = en Python es

x = x or new_value
Joseph Sheedy
fuente
36
Esta respuesta es probablemente lo que buscan las personas que vienen aquí desde una búsqueda en Google.
Graduado
Esta es realmente la mejor respuesta.
AdamC
6
Tenga en cuenta que, a diferencia de Ruby, este código solo funcionará si xse ha definido previamente (por ejemplo, establecido en Noneen un método de inicialización de clase).
oli
3
Es cierto que esto no se comporta exactamente como Ruby || =. Otra cosa a tener en cuenta es la "veracidad" de x. Si x == 0 en Python, obtendrá new_value ya que 0 se considera falso, pero en Ruby obtendrá 0.
Joseph Sheedy
1
¿Es esto tan eficiente como || =? || = no hace una asignación a menos que sea nula, pero esto siempre hace una asignación. ¿El compilador de Python es más inteligente de lo que creo?
jsarma
17

dicttiene setdefault().

Entonces, si request.sessiones un dict:

request.session.setdefault('thing_for_purpose', 5)
Jon-Eric
fuente
Vagamente similar pero de ninguna manera el equivalente de Ruby || =.
AdamC
1
Es significativamente diferente en que la expresión predeterminada se evalúa incluso si la clave ya está configurada. No hay forma de resolver la pregunta original sin evaluar el valor predeterminado o acceder a la clave más de una vez, o ambos.
slinkp
10

Establecer un valor predeterminado tiene sentido si lo está haciendo en un middleware o algo así, pero si necesita un valor predeterminado en el contexto de una solicitud:

request.session.get('thing_for_purpose', 5) # gets a default

bonificación: aquí se explica cómo hacer realmente una ||=en Python.

def test_function(self, d=None):
    'a simple test function'
    d = d or {}

    # ... do things with d and return ...
Brian Hicks
fuente
10

Respuesta precisa: No. Python no tiene un solo operador incorporado opque pueda traducirse x = x or yen x op y.

Pero casi lo hace. El bit a bit o es igual operador ( |=) voluntad función como se describe anteriormente si ambos operandos están siendo tratados como booleanos, con una advertencia. (¿Cuál es la advertencia? La respuesta está a continuación, por supuesto).

Primero, la demostración básica de funcionalidad:

x = True
x    
Out[141]: True

x |= True
x    
Out[142]: True

x |= False
x    
Out[143]: True

x &= False
x    
Out[144]: False

x &= True
x    
Out[145]: False

x |= False
x    
Out[146]: False

x |= True
x   
Out[147]: True

La advertencia es que Python no se escribe estrictamente y, por lo tanto, incluso si los valores se tratan como booleanos en una expresión, no se cortocircuitarán si se les da a un operador bit a bit. Por ejemplo, supongamos que tenemos una función booleana que borra una lista y devuelve Truesi hay elementos eliminados:

def  my_clear_list(lst):
    if not lst:
        return False
    else:
        del lst[:]
        return True

Ahora podemos ver el comportamiento en cortocircuito así:

x = True
lst = [1, 2, 3]
x = x or my_clear_list(lst)
print(x, lst)

Output: True [1, 2, 3]

Sin embargo, cambiar ora bit a bit o ( |) elimina el cortocircuito, por lo que la función se my_clear_listejecuta.

x = True
lst = [1, 2, 3]
x = x | my_clear_list(lst)
print(x, lst)

Output: True []

Arriba, x = x | my_clear_list(lst)es equivalente a x |= my_clear_list(lst).

Apollys apoya a Monica
fuente
1

En general, puede utilizar dict[key] = dict.get(key, 0) + val.

curtissv
fuente