una - python argumentos por referencia
¿Cómo paso una variable por referencia? (23)
No hay variables en Python
La clave para entender el paso de parámetros es dejar de pensar en "variables". Hay nombres y objetos en Python y juntos aparecen como variables, pero es útil distinguir siempre los tres.
- Python tiene nombres y objetos.
- La asignación enlaza un nombre a un objeto.
- Pasar un argumento a una función también vincula un nombre (el nombre del parámetro de la función) a un objeto.
Eso es todo lo que hay que hacer. La mutabilidad es irrelevante para esta pregunta.
Ejemplo:
a = 1
Esto une el nombre a
a un objeto de tipo entero que contiene el valor 1.
b = x
Esto une el nombre b
al mismo objeto al que está vinculado actualmente el nombre x
. Después, el nombre b
ya no tiene nada que ver con el nombre x
.
Vea las secciones 3.1 y 4.2 en la referencia del lenguaje Python 3.
Entonces, en el código que se muestra en la pregunta, la declaración self.Change(self.variable)
vincula el nombre var
(en el alcance de la función Change
) al objeto que contiene el valor ''Original''
y la asignación var = ''Changed''
( en el cuerpo de la función Change
) asigna ese mismo nombre otra vez: a algún otro objeto (que también contiene una cadena, pero podría haber sido algo completamente distinto).
La documentación de Python parece poco clara sobre si los parámetros se pasan por referencia o por valor, y el siguiente código produce el valor sin cambios ''Original''
class PassByReference:
def __init__(self):
self.variable = ''Original''
self.change(self.variable)
print(self.variable)
def change(self, var):
var = ''Changed''
¿Hay algo que pueda hacer para pasar la variable por referencia real?
(edición - Blair ha actualizado su respuesta enormemente popular para que ahora sea precisa)
Creo que es importante tener en cuenta que la publicación actual con la mayoría de los votos (por Blair Conrad), aunque es correcta con respecto a su resultado, es engañosa y está en el límite incorrecto según sus definiciones. Si bien hay muchos idiomas (como C) que permiten al usuario pasar por referencia o pasar por valor, Python no es uno de ellos.
La respuesta de David Cournapeau apunta a la respuesta real y explica por qué el comportamiento en la publicación de Blair Conrad parece ser correcto mientras que las definiciones no lo son.
En la medida en que Python se pasa por valor, todos los idiomas se pasan por valor ya que algunos datos (ya sea un "valor" o una "referencia") deben enviarse. Sin embargo, eso no significa que Python se pase por valor en el sentido de que un programador de C lo pensaría.
Si quieres el comportamiento, la respuesta de Blair Conrad está bien. Pero si desea conocer los aspectos básicos de por qué Python no es pasar por valor ni pasar por referencia, lea la respuesta de David Cournapeau.
Como puede indicar, necesita tener un objeto mutable, pero permítame sugerirle que compruebe las variables globales, ya que pueden ayudarle o incluso resolver este tipo de problema.
ejemplo:
>>> def x(y):
... global z
... z = y
...
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''y'' is not defined
>>> z
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''z'' is not defined
>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''y'' is not defined
>>> z
2
Effbot (también conocido como Fredrik Lundh) ha descrito el estilo de paso variable de Python como llamada por objeto: http://effbot.org/zone/call-by-object.htm
Los objetos se asignan en el montón y los punteros a ellos se pueden pasar a cualquier lugar.
Cuando realiza una asignación como
x = 1000
, se crea una entrada de diccionario que asigna la cadena "x" en el espacio de nombres actual a un puntero al objeto entero que contiene mil.Cuando actualiza "x" con
x = 2000
, se crea un nuevo objeto entero y el diccionario se actualiza para que apunte al nuevo objeto. El antiguo mil objeto permanece sin cambios (y puede o no estar vivo dependiendo de si alguna otra cosa se refiere al objeto).Cuando realiza una nueva asignación como
y = x
, se crea una nueva entrada de diccionario "y" que apunta al mismo objeto que la entrada de "x".Objetos como cadenas y enteros son inmutables . Esto simplemente significa que no hay métodos que puedan cambiar el objeto después de que se haya creado. Por ejemplo, una vez que se crea el objeto entero mil, nunca cambiará. Matemáticas se realiza mediante la creación de nuevos objetos enteros.
Objetos como listas son mutables . Esto significa que el contenido del objeto puede ser cambiado por cualquier cosa que apunte al objeto. Por ejemplo,
x = []; y = x; x.append(10); print y
x = []; y = x; x.append(10); print y
x = []; y = x; x.append(10); print y
se imprimirá[10]
. Se creó la lista vacía. Tanto "x" como "y" apuntan a la misma lista. El método de anexión muta (actualiza) el objeto de la lista (como agregar un registro a una base de datos) y el resultado es visible tanto para "x" como para "y" (al igual que una actualización de la base de datos sería visible para cada conexión a esa base de datos).
Espero que te aclare el tema.
El esquema de paso por asignación de Python no es exactamente lo mismo que la opción de parámetros de referencia de C ++, pero resulta ser muy similar al modelo de paso de argumentos del lenguaje C (y otros) en la práctica:
- Los argumentos inmutables se pasan efectivamente " por valor ". Los objetos como enteros y cadenas se pasan por referencia de objeto en lugar de copiarlos, pero como no se pueden cambiar los objetos inmutables en su lugar, el efecto es muy parecido a hacer una copia.
- Los argumentos mutables se pasan efectivamente " por puntero ". Los objetos, como listas y diccionarios, también se pasan por referencia a objetos, que es similar a la forma en que C pasa a las matrices como punteros: los objetos mutables pueden cambiarse en el lugar de la función, al igual que las matrices C .
El problema proviene de un malentendido de qué variables están en Python. Si estás acostumbrado a la mayoría de los lenguajes tradicionales, tienes un modelo mental de lo que sucede en la siguiente secuencia:
a = 1
a = 2
Usted cree que a
es una ubicación de memoria que almacena el valor 1
, luego se actualiza para almacenar el valor 2
. Así no es como funcionan las cosas en Python. Más bien, a
comienza como una referencia a un objeto con el valor 1
, luego se reasigna como una referencia a un objeto con el valor 2
. Esos dos objetos pueden continuar coexistiendo aunque a
ya no se refiera al primero; de hecho, pueden ser compartidos por cualquier número de otras referencias dentro del programa.
Cuando llama a una función con un parámetro, se crea una nueva referencia que se refiere al objeto pasado. Esto es independiente de la referencia que se usó en la llamada a la función, por lo que no hay manera de actualizar esa referencia y hacer que se refiera a un nuevo objeto En tu ejemplo:
def __init__(self):
self.variable = ''Original''
self.Change(self.variable)
def Change(self, var):
var = ''Changed''
self.variable
es una referencia al objeto de cadena ''Original''
. Cuando llama a Change
, crea una segunda referencia var
al objeto. Dentro de la función, reasigna la referencia var
a un objeto de cadena diferente ''Changed''
, pero la referencia self.variable
está separada y no cambia.
La única forma de evitar esto es pasar un objeto mutable. Debido a que ambas referencias se refieren al mismo objeto, cualquier cambio en el objeto se refleja en ambos lugares.
def __init__(self):
self.variable = [''Original'']
self.Change(self.variable)
def Change(self, var):
var[0] = ''Changed''
En este caso, a la variable titulada var
en el método Change
se le asigna una referencia a self.variable
, e inmediatamente asigna una cadena a var
. Ya no está apuntando a self.variable
. self.variable
. El siguiente fragmento de código muestra lo que sucedería si modificara la estructura de datos apuntada por var
y self.variable
, en este caso una lista:
>>> class PassByReference:
... def __init__(self):
... self.variable = [''Original'']
... self.change(self.variable)
... print self.variable
...
... def change(self, var):
... var.append(''Changed'')
...
>>> q = PassByReference()
[''Original'', ''Changed'']
>>>
Estoy seguro de que alguien más podría aclarar esto aún más.
Los argumentos se pasan por la asignación . La razón detrás de esto es doble:
- el parámetro pasado es en realidad una referencia a un objeto (pero la referencia se pasa por valor)
- algunos tipos de datos son mutables, pero otros no lo son
Asi que:
Si pasa un objeto mutable a un método, el método obtiene una referencia a ese mismo objeto y puede mutarlo para deleite de su corazón, pero si vuelve a enlazar la referencia en el método, el ámbito externo no sabrá nada al respecto, y luego Ya ha terminado, la referencia externa todavía apuntará al objeto original.
Si pasa un objeto inmutable a un método, aún no puede volver a enlazar la referencia externa y ni siquiera puede mutar el objeto.
Para que sea aún más claro, vamos a tener algunos ejemplos.
Lista - un tipo mutable
Intentemos modificar la lista que se pasó a un método:
def try_to_change_list_contents(the_list):
print(''got'', the_list)
the_list.append(''four'')
print(''changed to'', the_list)
outer_list = [''one'', ''two'', ''three'']
print(''before, outer_list ='', outer_list)
try_to_change_list_contents(outer_list)
print(''after, outer_list ='', outer_list)
Salida:
before, outer_list = [''one'', ''two'', ''three'']
got [''one'', ''two'', ''three'']
changed to [''one'', ''two'', ''three'', ''four'']
after, outer_list = [''one'', ''two'', ''three'', ''four'']
Dado que el parámetro pasado es una referencia a la outer_list
, no una copia, podemos usar los métodos de la lista de mutación para cambiarlo y que los cambios se reflejen en el ámbito externo.
Ahora veamos qué sucede cuando intentamos cambiar la referencia que se pasó como parámetro:
def try_to_change_list_reference(the_list):
print(''got'', the_list)
the_list = [''and'', ''we'', ''can'', ''not'', ''lie'']
print(''set to'', the_list)
outer_list = [''we'', ''like'', ''proper'', ''English'']
print(''before, outer_list ='', outer_list)
try_to_change_list_reference(outer_list)
print(''after, outer_list ='', outer_list)
Salida:
before, outer_list = [''we'', ''like'', ''proper'', ''English'']
got [''we'', ''like'', ''proper'', ''English'']
set to [''and'', ''we'', ''can'', ''not'', ''lie'']
after, outer_list = [''we'', ''like'', ''proper'', ''English'']
Dado que el parámetro the_list
se pasó por valor, asignarle una nueva lista no tuvo ningún efecto que el código fuera del método pudiera ver. the_list
era una copia de la referencia de outer_list
, y teníamos el punto the_list
apuntando a una nueva lista, pero no había forma de cambiar a dónde señalaba la lista outer_list
.
Cadena - un tipo inmutable
Es inmutable, así que no hay nada que podamos hacer para cambiar el contenido de la cadena.
Ahora, intentemos cambiar la referencia
def try_to_change_string_reference(the_string):
print(''got'', the_string)
the_string = ''In a kingdom by the sea''
print(''set to'', the_string)
outer_string = ''It was many and many a year ago''
print(''before, outer_string ='', outer_string)
try_to_change_string_reference(outer_string)
print(''after, outer_string ='', outer_string)
Salida:
before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago
Nuevamente, dado que el parámetro the_string
se pasó por valor, la asignación de una nueva cadena no tuvo ningún efecto que el código fuera del método pudiera ver. the_string
era una copia de la referencia de outer_string
, y teníamos el punto de cadena a una nueva cadena, pero no había forma de cambiar a dónde señalaba la outer_string
.
Espero que esto aclare un poco las cosas.
EDITAR: Se ha observado que esto no responde a la pregunta que @David originalmente formuló: "¿Hay algo que pueda hacer para pasar la variable por referencia real?". Vamos a trabajar en eso.
¿Cómo podemos solucionar esto?
Como muestra la respuesta de @ Andrea, podría devolver el nuevo valor. Esto no cambia la forma en que se transmiten las cosas, pero le permite obtener la información que desea recuperar:
def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string
# then you could call it like
my_string = return_a_whole_new_string(my_string)
Si realmente desea evitar el uso de un valor de retorno, puede crear una clase para mantener su valor y pasarlo a la función o usar una clase existente, como una lista:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string
# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)
do_something_with(wrapper[0])
Aunque esto parece un poco engorroso.
No es ni paso por valor ni paso por referencia, es llamada por objeto. Mira esto, por Fredrik Lundh:
http://effbot.org/zone/call-by-object.htm
Aquí hay una cita significativa:
"... las variables [nombres] no son objetos; no pueden ser denotadas por otras variables ni referidas por objetos".
En su ejemplo, cuando se llama al método Change
, se crea un namespace para él; y var
convierte en un nombre, dentro de ese espacio de nombres, para el objeto de cadena ''Original''
. Ese objeto entonces tiene un nombre en dos espacios de nombres. A continuación, var = ''Changed''
vincula var
a un nuevo objeto de cadena y, por lo tanto, el espacio de nombres del método se olvida de ''Original''
. Finalmente, ese espacio de nombres se olvida, y la cadena ''Changed''
junto con él.
Piense en cosas que se pasan por asignación en lugar de por referencia / por valor. De esa manera, siempre está claro, lo que está sucediendo, siempre y cuando entiendas lo que sucede durante la asignación normal.
Entonces, al pasar una lista a una función / método, la lista se asigna al nombre del parámetro. Anexar a la lista resultará en la modificación de la lista. La reasignación de la lista dentro de la función no cambiará la lista original, ya que:
a = [1, 2, 3]
b = a
b.append(4)
b = [''a'', ''b'']
print a, b # prints [1, 2, 3, 4] [''a'', ''b'']
Dado que los tipos inmutables no se pueden modificar, parece que se pasan por un valor, pasar un int a una función significa asignar el int al parámetro de funciones. Solo puedes reasignar eso, pero no cambiará el valor de las variables originales.
Técnicamente, Python siempre usa valores de referencia por paso . Voy a repetir mi otra respuesta para apoyar mi declaración.
Python siempre usa valores de paso por referencia. No hay ninguna excepción. Cualquier asignación de variable significa copiar el valor de referencia. Sin excepción. Cualquier variable es el nombre vinculado al valor de referencia. Siempre.
Puede pensar en un valor de referencia como la dirección del objeto de destino. La dirección se elimina automáticamente cuando se utiliza. De esta manera, trabajando con el valor de referencia, parece que trabaja directamente con el objeto de destino. Pero siempre hay una referencia en el medio, un paso más para saltar al objetivo.
Aquí está el ejemplo que prueba que Python usa pasar por referencia:
Si el argumento fue pasado por valor, el lst
externo no pudo ser modificado. Los verdes son los objetos de destino (el negro es el valor almacenado en el interior, el rojo es el tipo de objeto), el amarillo es la memoria con el valor de referencia en el interior, dibujado como la flecha. La flecha azul sólida es el valor de referencia que se pasó a la función (a través de la ruta de la flecha azul discontinua). El feo amarillo oscuro es el diccionario interno. (De hecho, podría dibujarse también como una elipse verde. El color y la forma solo dicen que es interno).
Puede usar la función incorporada id()
para saber cuál es el valor de referencia (es decir, la dirección del objeto de destino).
En los lenguajes compilados, una variable es un espacio de memoria que puede capturar el valor del tipo. En Python, una variable es un nombre (capturado internamente como una cadena) vinculado a la variable de referencia que contiene el valor de referencia para el objeto de destino. El nombre de la variable es la clave en el diccionario interno, la parte de valor de ese elemento del diccionario almacena el valor de referencia al objetivo.
Los valores de referencia están ocultos en Python. No hay ningún tipo de usuario explícito para almacenar el valor de referencia. Sin embargo, puede utilizar un elemento de lista (o elemento en cualquier otro tipo de contenedor adecuado) como variable de referencia, ya que todos los contenedores almacenan los elementos también como referencias a los objetos de destino. En otras palabras, los elementos en realidad no están contenidos dentro del contenedor, solo lo están las referencias a los elementos.
Tienes algunas respuestas realmente buenas aquí.
x = [ 2, 4, 4, 5, 5 ]
print x # 2, 4, 4, 5, 5
def go( li ) :
li = [ 5, 6, 7, 8 ] # re-assigning what li POINTS TO, does not
# change the value of the ORIGINAL variable x
go( x )
print x # 2, 4, 4, 5, 5 [ STILL! ]
raw_input( ''press any key to continue'' )
Un truco simple que normalmente utilizo es simplemente envolverlo en una lista:
def Change(self, var):
var[0] = ''Changed''
variable = [''Original'']
self.Change(variable)
print variable[0]
(Sí, sé que esto puede ser un inconveniente, pero a veces es lo suficientemente simple para hacer esto).
Dado que su ejemplo está orientado a objetos, puede realizar el siguiente cambio para lograr un resultado similar:
class PassByReference:
def __init__(self):
self.variable = ''Original''
self.change(''variable'')
print(self.variable)
def change(self, var):
setattr(self, var, ''Changed'')
# o.variable will equal ''Changed''
o = PassByReference()
assert o.variable == ''Changed''
Si bien pasar por referencia no es nada que se ajuste bien a python y se debe usar raramente, hay algunas soluciones que realmente pueden funcionar para obtener el objeto asignado actualmente a una variable local o incluso reasignar una variable local desde dentro de una función llamada.
La idea básica es tener una función que pueda hacer ese acceso y que pueda pasarse como objeto a otras funciones o almacenarse en una clase.
Una forma es usar global
(para variables globales) o nonlocal
(para variables locales en una función) en una función de envoltorio.
def change(wrapper):
wrapper(7)
x = 5
def setter(val):
global x
x = val
print(x)
La misma idea funciona para leer y del
escribir una variable.
Para solo leer hay incluso una forma más corta de usar lambda: x
que devuelve un llamable que cuando se llama devuelve el valor actual de x. Esto es algo así como "llamada por nombre" utilizado en idiomas en el pasado lejano.
Pasar 3 envoltorios para acceder a una variable es un poco difícil de manejar, por lo que se pueden envolver en una clase que tiene un atributo de proxy:
class ByRef:
def __init__(self, r, w, d):
self._read = r
self._write = w
self._delete = d
def set(self, val):
self._write(val)
def get(self):
return self._read()
def remove(self):
self._delete()
wrapped = property(get, set, remove)
# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15
El soporte de "reflexión" de Pythons permite obtener un objeto que es capaz de reasignar un nombre / variable en un ámbito determinado sin definir funciones explícitamente en ese ámbito:
class ByRef:
def __init__(self, locs, name):
self._locs = locs
self._name = name
def set(self, val):
self._locs[self._name] = val
def get(self):
return self._locs[self._name]
def remove(self):
del self._locs[self._name]
wrapped = property(get, set, remove)
def change(x):
x.wrapped = 7
def test_me():
x = 6
print(x)
change(ByRef(locals(), "x"))
print(x)
Aquí la ByRef
clase envuelve un acceso al diccionario. Por lo tanto, el acceso de atributo a wrapped
se traduce a un acceso de elemento en el diccionario pasado. Al pasar el resultado de la estructura interna locals
y el nombre de una variable local, esto termina por acceder a una variable local. La documentación de Python a partir de 3.5 informa que cambiar el diccionario podría no funcionar pero parece funcionar para mí.
dada la forma en que Python maneja los valores y las referencias a ellos, la única forma de hacer referencia a un atributo de instancia arbitraria es por nombre
class PassByReferenceIsh:
def __init__(self):
self.variable = ''Original''
self.change(''variable'')
print self.variable
def change(self, var):
self.__dict__[var] = ''Changed''
por supuesto, en el código real usted agregaría la verificación de errores en la búsqueda de dict.
Aparte de todas las grandes explicaciones sobre cómo funciona esto en Python, no veo una sugerencia simple para el problema. Como parece que creas objetos e instancias, la forma pirónica de manejar las variables de instancia y cambiarlas es la siguiente:
class PassByReference:
def __init__(self):
self.variable = ''Original''
self.Change()
print self.variable
def Change(self):
self.variable = ''Changed''
En los métodos de instancia, normalmente hace referencia a los self
atributos de instancia de acceso. Es normal establecer atributos de instancia __init__
y leerlos o cambiarlos en los métodos de instancia. Por eso también le pasas self
el primer argumento a def Change
.
Otra solución sería crear un método estático como este:
class PassByReference:
def __init__(self):
self.variable = ''Original''
self.variable = PassByReference.Change(self.variable)
print self.variable
@staticmethod
def Change(var):
var = ''Changed''
return var
Aquí está la explicación simple (espero) del concepto pass by object
utilizado en Python.
Siempre que pase un objeto a la función, el objeto en sí se pasa (el objeto en Python es en realidad lo que llamaríamos un valor en otros lenguajes de programación) no la referencia a este objeto. En otras palabras, cuando llamas:
def change_me(list):
list = [1, 2, 3]
my_list = [0, 1]
change_me(my_list)
Se está pasando el objeto real - [0, 1] (que se llamaría un valor en otros lenguajes de programación). Así que de hecho la función change_me
intentará hacer algo como:
[0, 1] = [1, 2, 3]
Lo que obviamente no cambiará el objeto pasado a la función. Si la función se veía así:
def change_me(list):
list.append(2)
Entonces la llamada resultaría en:
[0, 1].append(2)
que obviamente va a cambiar el objeto. Esta respuesta lo explica bien.
Hay muchas ideas en las respuestas aquí, pero creo que un punto adicional no se menciona aquí explícitamente. Cita de la documentación de Python https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python
"En Python, las variables a las que solo se hace referencia dentro de una función son implícitamente globales. Si a una variable se le asigna un nuevo valor en cualquier parte dentro del cuerpo de la función, se asume que es local. Si a una variable se le asigna un nuevo valor dentro de la función, la variable es implícitamente local, y usted necesita declararla explícitamente como "global". Aunque al principio es un poco sorprendente, un momento de consideración lo explica. Por un lado, la necesidad de una variable global para las asignadas proporciona una barra contra los efectos secundarios no deseados. Por otro lado, si se requiriera global para todas las referencias globales, estaría usando global todo el tiempo. Tendría que declarar como global cada referencia a una función incorporada o a un componente de un módulo importado.Este desorden anularía la utilidad de la declaración global para identificar efectos secundarios ".
Incluso cuando se pasa un objeto mutable a una función, esto sigue siendo válido. Y para mí, explica claramente la razón de la diferencia de comportamiento entre asignar el objeto y operar el objeto en la función.
def test(l):
print "Received", l , id(l)
l = [0, 0, 0]
print "Changed to", l, id(l) # New local object created, breaking link to global l
l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)
da:
Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632
La asignación a una variable global que no se declara global, por lo tanto, crea un nuevo objeto local y rompe el enlace al objeto original.
Hay un pequeño truco para pasar un objeto por referencia, aunque el lenguaje no lo haga posible. También funciona en Java, es la lista con un elemento. ;-)
class PassByReference:
def __init__(self, name):
self.name = name
def changeRef(ref):
ref[0] = PassByReference(''Michael'')
obj = PassByReference(''Peter'')
print obj.name
p = [obj] # A pointer to obj! ;-)
changeRef(p)
print p[0].name # p->name
Es un truco feo, pero funciona. ;-PAG
Simplemente puede usar una clase vacía como una instancia para almacenar objetos de referencia porque los atributos de objetos internos se almacenan en un diccionario de instancia. Vea el ejemplo.
class RefsObj(object):
"A class which helps to create references to variables."
pass
...
# an example of usage
def change_ref_var(ref_obj):
ref_obj.val = 24
ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)
Utilicé el siguiente método para convertir rápidamente un par de códigos Fortran a Python. Es cierto que no se pasa por referencia ya que se planteó la pregunta original, pero en algunos casos es un trabajo simple.
a=0
b=0
c=0
def myfunc(a,b,c):
a=1
b=2
c=3
return a,b,c
a,b,c = myfunc(a,b,c)
print a,b,c