Estoy tratando de sumar o restar de una variable definida, pero no puedo averiguar cómo sobrescribir el valor anterior con el nuevo.

a = 15

def test():
    a = a +10
    print ( a )

test()

Mensaje de error:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 4, in test
    a = a +10
UnboundLocalError: local variable 'a' referenced before assignment
28
user7351337 28 dic. 2016 a las 23:40
Es posible que necesite un espacio después del signo más. Swift también dará errores si los signos no están separados por un espacio.
 – 
J.A.R.
28 dic. 2016 a las 23:43
Agregue el código de la imagen como texto en la pregunta.
 – 
Antti Haapala
28 dic. 2016 a las 23:52
Posible duplicado de UnboundLocalError en Python
 – 
approxiblue
29 dic. 2016 a las 03:37
2
Esta pregunta incluye el comportamiento deseado, un problema o error específico y, a menos que estemos realmente dividiendo los pelos en una sola línea, el código más corto necesario para reproducirlo. Es innegable que es un engaño de la pregunta a la que hace referencia @approxiblue, pero probablemente una mejor, ya que esa pregunta usa asignación aumentada (para la cual el mensaje de error no tiene ningún sentido para un principiante), y la respuesta aceptada aconseja irresponsablemente solo el uso de global o nonlocal en lugar de evitar los globales en primer lugar.
 – 
Zero Piraeus
29 dic. 2016 a las 05:43
1
Ya he votado para reabrir. Si bien uno es ciertamente un engaño del otro, yo diría que la pregunta a la que se hace referencia debería marcarse como un engaño de esta, ya que es un poco menos clara, la respuesta aceptada es terrible, y la pregunta que actualmente está marcada como engañada es peor aún. Consulte meta.stackoverflow.com/questions/251938/… para conocer los antecedentes de esta justificación.
 – 
Zero Piraeus
29 dic. 2016 a las 05:51

8 respuestas

El error que obtiene cuando intenta ejecutar su código es:

UnboundLocalError: local variable 'a' referenced before assignment

… Lo cual, a primera vista, parece extraño: después de todo, la primera declaración en el código anterior (a = 15) es una asignación. Entonces, ¿qué está pasando?

En realidad, están sucediendo dos cosas distintas, y ninguna de ellas es obvia a menos que ya las sepa.

En primer lugar, en realidad tiene dos variables diferentes:

  • El a en su primera línea es una variable global (llamada así porque existe en el ámbito global, fuera de cualquier definición de función).

  • El a en las otras líneas es una variable local, lo que significa que solo existe dentro de su función test().

Estas dos variables no tienen ninguna relación entre sí, aunque tienen el mismo nombre.

Una variable es local a una función si hay una declaración que se le asigna dentro de esa función, por ejemplo, su línea a = a +10.

Aun así, el error todavía parece extraño; después de todo, lo primero que haces dentro de test() es asignar a a, entonces, ¿cómo se puede hacer referencia a él de antemano?

La respuesta es que, en una declaración de asignación, Python evalúa todo en el lado derecho del signo = antes de asignarlo al nombre en el lado izquierdo, por lo que aunque la asignación esté escrita primero en su código, a obtiene referenciado primero en ese lado derecho: a +10.

Hay dos formas de evitar esto. La primera es decirle a Python que realmente desea que el a dentro de test() sea el mismo a en el alcance global:

def test():
    global a
    a = a + 10
    print(a)

Esto funcionará, pero es una forma bastante mala de escribir programas. Alterar las variables globales dentro de las funciones se vuelve difícil de administrar muy rápido, porque generalmente tiene muchas funciones, y ninguna de ellas puede estar segura de que otra no esté jugando con la variable global de alguna manera que no esperaban.

Una mejor manera es pasar variables como argumentos a funciones, como esta:

a = 15

def test(x):
    x = x + 10
    print(x)

test(a)

Observe que el nombre no tiene que ser el mismo: su nueva definición de test() solo dice que acepta un valor y luego hace algo con él. Puede pasar lo que quiera, podría ser a, o el número 7, o algo más. De hecho, su código siempre será más fácil de entender si intenta evitar tener variables con el mismo nombre en diferentes ámbitos.

Si juegas con el código anterior, notarás algo interesante:

>>> a = 15
>>> test(a)
25
>>> a
15

… ¡a no cambió! Eso es porque aunque lo pasó a test() y se asignó a x, fue entonces x que se cambió, dejando el a original solo.

Si realmente desea cambiar a, debe devolver su x modificado de la función y luego reasignarlo a a en el exterior:

>>> a = 15
>>> 
>>> def test(x):
...     x = x + 10
...     print(x)
...     return x
... 
>>> a = test(a)
25
>>> a
25
35
Zero Piraeus 29 dic. 2016 a las 00:40
1
Antes de que alguien señale que x en realidad fue reemplazado en lugar de cambiado … lo sé. Sin embargo, ya tenía que cubrir un poco de terreno para responder correctamente a la pregunta, y decidí no abrir esa lata de gusanos en particular.
 – 
Zero Piraeus
29 dic. 2016 a las 00:37
Esta fue una gran respuesta, gracias. Entonces, para estar seguro, ¿realmente no hay forma de definir la función de prueba de manera que realmente cambie "a"? Es decir, ¿no hay forma de incorporar el paso de reasignación externa a = test (a) en la función en sí a través de un código más complicado?
 – 
user26866
23 dic. 2020 a las 22:21

Lo haría de esta manera:

def test(a):
    a = a +10
    return a

print(test(15))

Tenga en cuenta que en la versión que aquí se propone hay algunas cosas que difieren de la suya.

Primero, lo que anoté crearía una función que tenga como entrada el valor a (en este caso puesto a 15 cuando llamamos a la función -ya definida en la última línea-), luego asigna al objeto a el valor a (que era 15) más 10, luego devuelve a (que se ha modificado y ahora es 25) y, finalmente, imprime a gracias a la última línea de código:

print(test(15))

Tenga en cuenta que lo que hizo no fue en realidad una función pura, por así decirlo. Por lo general, queremos que las funciones obtengan un valor de entrada (o varios) y devuelvan un valor de entrada (o varios). En su caso, tenía un valor de entrada que en realidad estaba vacío y sin valor de salida (ya que no usó retorno ). Además, intentó escribir esta entrada a fuera de la función (que, cuando la llamó diciendo test(a) el valor a no se cargó y le dio el error -es decir, a los ojos de la computadora estaba "vacío").

Además, te animo a que te acostumbres a escribir return dentro de la función y luego a usar una impresión cuando la llames (tal como escribí en la última línea de codificación: print(test(15))) en lugar de usándolo dentro de la función. Es mejor usar la impresión solo cuando llama a la función y desea ver qué está haciendo realmente la función.

Al menos, esta es la forma en que me mostraron en las lecciones básicas de programación. Esto se puede justificar de la siguiente manera: si está utilizando el retorno dentro de la función, la función le dará un valor que luego se puede usar en otras funciones (es decir, la función retorno es algo con lo que puede trabajar ). De lo contrario, solo se mostrará un número en la pantalla con una impresión , pero la computadora no podría seguir trabajando con él.

PD Podrías hacer lo mismo haciendo esto:

def test(a):
    a +=10      
    return a

print(test(15))
3
americansanti 29 dic. 2016 a las 01:28
4
¿Por qué no simplemente: def test(a): return a + 10?
 – 
boardrider
31 dic. 2016 a las 01:23

El alcance de la variable es local al bloque a menos que se defina explícitamente usando la palabra clave global. Hay otra forma de acceder a la variable global local a una función usando la función globals

a = 15

def test():
    a = globals()['a']
    a += 10
    print ( a )

test()

El ejemplo anterior imprimirá 25 manteniendo intacto el valor global, es decir, 15.

0
MaNKuR 4 jun. 2018 a las 17:33
# All the mumbo jumbo aside, here is an experiment 
# to illustrate why this is something useful 
# in the language of Python:
a = 15    # This could be read-only, should check docs

def test_1():
    b = a + 10    # It is perfectly ok to use 'a'
    print(b)

def test_2():
    a = a + 10    # Refrain from attempting to change 'a'
    print(a)

test_1()    # No error
test_2()    # UnboundLocalError: ...
-2
Down the Stream 11 abr. 2017 a las 17:57
a = 15

def test():
    global a
    a = a +10
    print ( a )
    
test()

Si desea cambiar los valores de las variables locales dentro de una función, debe usar la palabra clave global.

1
NAIDU 23 ene. 2021 a las 06:24

El problema aquí es el alcance. Básicamente, el error significa que está utilizando la variable a, pero aún no existe. ¿Por qué? La variable a, no ha sido declarada en la función test(). Necesita "invitar" variables globales antes de usarlas.

Hay varias formas de incluir una variable en el alcance de una función. Una forma es usarlo como un parámetro como este:

def test(number):
    number = number + 10
    print ( number )

Luego impleméntalo como:

test(a)

Alternativamente, también puede usar la palabra clave global para mostrar la función de que a es una variable global. ¿Cómo? como esto:

def test():
    global a
    a = a + 10
    print ( a )

Usando la palabra clave global, simplemente le dice a test dónde buscar a. Con el primer método, no puede modificar la variable global a porque hizo una copia de ella y está usando la copia. Sin embargo, el segundo método, cualquier cambio que realice en la función test() a a, afectará a la variable global a.

0
TeeBeeGee 28 jun. 2021 a las 22:33

Su error no tiene nada que ver con que ya esté definido ... Una variable solo es válida dentro de su llamado alcance: si crea una variable en una función, solo se define en esta función.

def test():
   x=17
   print(x) # returns 17

test()
print(x) # results in an error.
-4
Nudin 28 dic. 2016 a las 23:46

Está modificando una variable a creada en el alcance de la función test(). Si desea que se modifique el a externo, puede hacer lo siguiente:

a = 15

def test():
    global a
    a = a + 1
    print(a)

test()
2
d4vsanchez 28 dic. 2016 a las 23:49
2
Si bien el uso de global resuelve el problema inmediato, es una mala práctica de programación y no debería enseñarlo como una solución a un principiante sin señalar también que es una mala práctica y que se evita fácilmente. global realmente no debería ser usado por alguien que no comprenda completamente por qué generalmente es lo incorrecto.
 – 
Zero Piraeus
29 dic. 2016 a las 00:33
Tienes razón y presento mis excusas para no expresar la mala elección de usar global. Actualizaré mi respuesta en un momento para expresar eso. Muchas gracias. @ZeroPiraeus
 – 
d4vsanchez
29 dic. 2016 a las 00:45