• Curso De Python3 Desde 0 (5ta Parte)

 #415360  por KHC
 06 Jun 2013, 11:32
Básicamente este 5to texto se enfoca en Funciones.
disfruten la lectura

Funciones:
------------

Básicamente una Función es una serie de sentencias que cumplen un objetivo.
Nos ayuda bastante en cuanto a reducir código, reutilizarlo, elegancia del lenguaje, entre otras.
A diferencia de otros lenguaje, las Funciones en Python --por ser un lenguaje interpretado--
no existen hasta el momento de ser invocada.
También contamos con una palabra reservada para poder declarar una Función "def".
Esto implica bastantes cosas, como por ejemplo que dentro de una Función podamos crear otra o
dependiendo el comportamiento del programa <<if, else, elif...>> podamos crear una Función.

Internamente, al definir una Función Python crea un nuevo objeto y se le asigna el nombre dado;
las Funciones en Python pueden ser asignadas a una variable o una lista.

Esta seria un ejemplo de como definir una Función en Python3:

Código: [ Debe registrarse para ver este enlace ]
def miPrimeraFuncion():
	pass

Donde:

- def = palabra reservada para declarar una Función
- miPrimeraFuncion = Nombre de la Función
- () = argumentos <<En este caso ninguno>>
- : = fin de la declaración de la Función y comienzo del contenido de la misma.
- pass : palabra reservada de Python --para crear el esqueleto de la Función--

Como vemos no es difícil declarar una Función, pero ahora le daremos un toque mas realista para
despejar algunas dudas que pidieran surgir:

>>> def saluda():
	print("My first function")

	
>>> saluda()
My first function
Perfecto, definimos nuestra Función saluda() --sin argumentos--, y procedemos a
escribir su funcionamiento que en este caso sera mostrar en pantalla el string "My first function".
Aposteriori, invocamos la función saluda() y recibimos el resultado esperado.

Daré otro ejemplo para fundamentar lo dicho anteriormente y procederemos a ver
los parámetro de una función:

>>> def myname(nombre):
	urname = nombre
	print(urname)


>>> myname("julianstafari")
julianstafari

Definimos la Función myname con un parámetro que es ("julian") y terminamos la declaración con ":".
Dentro de esta Función, tenemos una variable local que es "urname" y se le asigna el valor de
el argumento que fue recibido al invocar la Función y se utiliza print() para mostrar el contenido
de la variable urname.
Después de esto, se llama a la Función myname con el argumento que pide <<Si no se le
pasa el argumento que se pide tira un error y hasta no ver __init__ esto seguiría pasando.>>
Creo que los ejemplos son bastante claros y resuelven las dudas que me interesan despejar.


Paramentos:
---------------

En Python3, el uso de parámetros implica la asignación de un nombre a un objeto. Esto simplemente
significa que el pasar un parámetro solo ocurre por valor o por referencia dependiendo si los
argumentos son mutables o inmutables.

Para fundamentar lo anterior dare un ejemplo:
>>> def asigna(a, b):
	a = 1
	b = 2
	print(a,b)

	
>>> asigna(2,3)
1 2
>>> c = 3
>>> d = 4
>>> asigna(c,d)
1 2
>>> asigna(c,d)
3 4
Tomare un poco de tiempo para explicar lo anterior.
Primeramente se define una Función con la palabra reservada "def", se le asigna el nombre de
"asigna" y se indica que esta Función tomara dos argumentos "(a, b)" y terminamos
la declaración ":".
Dentro de la función indicamos que:
Primeramente al argumento "a", se le asigna el valor "1", al parámetro "b" se le asigna
el valor "2" y mostramos en pantalla con "print()" el valor de a y b.
>>> test(2,3)
1 2
En esta linea invocamos a nuestra nueva Función "asigna" con los parámetro "2 y 3".
Como resultado recibimos "1 2" y esto se explica puesto que nuestra Función internamente
reasigna esos valores es decir 2 y 3 por los que se indica "a = 1 y b = 2".

Procedemos a definir dos variables nuevas FUERA de la Función que serán "c=3 y d=4"
y en la siguiente linea procedemos a llamar a nuestra Función "asigna" con nuestras
nuevas variables como argumento:
>>> asigna(c,d)
1 2
Y observamos que el resultado es el mismo --puesto que "print(a, b)" imprime el valor de a y b
no el de "c y d". Si confirmamos lo anterior imprimimos el valor de "c y d":
>>> print(c,d)
5 6
Algunos pensaran <<Bueno, técnicamente la Función debería REASIGNAR el valor de "c y d" por "1 y 2"
puesto que dentro de la Función se indica.>> pero a decir verdad esto es falso, puesto que las
variables "c y d" son de tipo inmutable y la Función NO tiene autorización para modificar estos valores.
Mas adelante veremos que esto solo aplica en este nivel de conocimiento, puesto que tiene que ver con
temas mas avanzados.
>>> a = 1
>>> b = a
>>> print (b)
1
>>> b = 2
>>> print (b)
2
>>> print (a)
1
Si por el contrario aplicamos el mismo principio a un objeto mutable:
>>> a = [0,1]
>>> b = a
>>> print(b)
[0, 1]
>>> b[0]= 1
>>> print(b)
[1, 1]
>>> print(a)
[1, 1]
Vemos que el el objeto al que se hacia referencia en este caso "a" SI se ve afectado.

En el caso de necesitar modificar un valor inmutable como lo son las variables, podremos
utilizar "return()" y asignar directamente el valor de las variables con nuestra
Función que contenga "return()":

>>> def asigna(a, b):
	a = 1
	b = 2
	return(a, b)

>>> c = 3
>>> d = 4
>>> print(c, d)
3 4
>>> c, d = asigna(c, d)
>>> print(c, d)
1 2

Definimos nuestra Función con "def" con el nombre "asigna", con dos parámetros "(a, b)" y
terminamos la declaración de propiedades la Función ":".
Procedemos a escribir su contenido y es tan simple como asignar a los argumentos
"a = 1 y b = 2" y con "return(a, b)" regresamos los valores.
>>> def asigna(a, b):
	a = 1
	b = 2
	return(a, b)
FUERA de la Función asignamos valores a las variables "c=3 y d=4" e imprimimos sus valores
con "print(c, d)" para comprobar la asignación que hicimos antes.
>>> c = 3
>>> d = 4
>>> print(c, d)
3 4
Después reasignamos a las variables "c y d" directamente con nuestra Función "asigna(a, c)"
y después con "´print(c, d)" comprobamos que nuestra Función cumplió su cometido correctamente.
>>> c, d = asigna(c, d)
>>> print(c, d)
1 2
De echo, podría ser cualquier valor al momento de invocar a "asigna()", podría ser "asiga(52,23)"
y como internamente esta reasignado esos valores y "return()" nos da "derecho" de poder reasignar
esos valores a objetos inmutables

RECORDAR que Python3 toma los parámetros por ASIGNACIÓN.


Return():
-----------

Para explicar mejor el funcionamiento de la Función "return()" daré un ejemplo:

>>> def fun1(a):
	a = "hola"
	b="aaa"
	c="aqw"
	return(a,b,c)

>>> fun1(1)
('hola', 'aaa', 'aqw')

Y es simple, "return()" nos regresa un tupla con los valores que se le indican.

De ser el caso que solo se le pase un parámetro nos los regresa como string directamente:

>>> def fun1(a):
	return(a)

>>> fun1(1)
1

Valores y Parámetros Por Defecto:
-----------------------------------------

Tenemos la posibilidad de asignar un valor por defecto al momento de pedir argumentos y esto
nos amplia bastante las cosas, puesto que nos facilita y da mayor potencial para explotar
al momento de escribir una Función:

>>> def saludo(nombre="programador"):
	print(nombre)

	
>>> saludo()
programador
>>> saludo("julianstafari")
julianstafari

En Python 3, esta permitido poner un valor por defecto a todos los parámetro o solo al que uno indique:
>>> def saludo(edad, nombre="programador"):
	print(edad, nombre)

	
>>> saludo(22)
(22, 'programador')
>>> saludo(20,"julianstafari")
(20, 'julianstafari')
Tener en cuenta que los argumentos con valor por defecto, SE DEBEN DEJAR AL ULTIMO o si no obtendremos:

SyntaxError: non-default argument follows default argument

Y como lo anterior es un poco incomodo --viéndolo de cierta perspectiva--, Python 3 nos permite
indicar los argumentos con el nombre asignado para dicho argumento SIN importar el orden:

>>> def saludo(edad, nombre, nacionalidad="N/A"):
	print(nombre, edad, nacionalidad)

	
>>> saludo(nombre="julianstafari",nacionalidad="Mexicana",edad=20)
('julianstafari', 20, 'Mexicana')

Como vemos, indique en orden edad, nombre y nacionalidad al momento de definir los argumentos
de nuestra Función "saludo" y con "print(nombre, edad, nacionalidad)" indico que los muestre
en pantalla en el orden que yo deseo.

En la segunda linea invoco a mi nueva Función "saludo" y EN EL ORDEN QUE YO INDIQUE" --no importa
cual sea-- asigno el valor a mis argumentos --que no son mas que variables locales para la Función--
y comprobamos que no importa el orden, mientras se indique claramente el nombre del argumento y
su valor.


Argumentos Indefinidos:
-----------------------------

Este es un tema bastante útil también al momento de tratar con Funciones en Python, puesto
que nos permite tener un numero Indefinido de argumentos sin tener que declararlos en la
cabecera de nuestra Función, es decir:

En lugar de hacer esto:
Código: [ Debe registrarse para ver este enlace ]
def suma(a, b, c, d, e, f):
hasta llegar al numero de argumentos que queramos -- y ya que es una Función que sumara, no
sabemos cuantos argumentos se le pasaran--

HACER ESTO:
Código: [ Debe registrarse para ver este enlace ]
def suma(*numeros):

Lo único que tenemos que hacer en este caso es indicar con el operador "*" antepuesto a el
nombre de nuestro argumento <<variable local>> y el nombre del argumento, puede ser el que
uno desee; nuestra Función quedaría de la siguiente forma:

def suma(*numeros):
	for x in numeros:
		print(x)
Cabe mencionar, que al usar el operador "*" Python 3 interpreta que sera un numero indefinido
de argumentos y por ende, tratara a estos como una Tupla y no como variables locales....

También contamos con la posibilidad de poder tratar los argumentos de otra forma y esta es
anteponiendo el dos veces el operador "*" al nombre de nuestro argumento.
Y esto nos permite tratar al numero indefinido de argumentos como un diccionario y por ende,
asignar un nombre y valor para cada uno de ellos:
>>> def agenda(**elementos):
	print(elementos)

	
>>> agenda(nombre="julianstafari", edad=20, site="greydotsec.org",schol=1337)
{'edad': 20, 'nombre': 'julianstafari', 'site': 'greydotsec.org', 'schol': 1337}

También es posible posicionar argumentos por defecto o argumentos en cierta posición y los
demás indefinidos:

>>> def agenda(nombre,edad=0,**elementos):
	print(nombre, edad, elementos)

>>> agenda("julianstafari",1,localidad="Mexico",school=1337)
julianstafari 1 {'localidad': 'Mexico', 'school': 1337}

O también como Tupla:
>>> def agenda(nombre, edad=0, *elementos):
	print(nombre, edad, elementos)

	
>>> agenda("julianstafari", 20, 1337, "greydosec.org")
julianstafari 20 (1337, 'greydosec.org')
Si a esto le sumamos un format string y muchas cosas que hemos visto en el curso,
podremos obtener resultados bastante agradables para el nivel de conocimiento que
estamos tratando hasta el momento sobre el lenguaje y miles de posibilidades. :D


Desempaquetado de argumentos:
---------------------------------------

Esta técnica, no es mas que el inverso de lo que acabamos de ver.
Es decir, que si anterior mente obteníamos un tupla o un diccionario de nuestros argumentos,
ahora pasaremos esta tupla o diccionario para pasarlo como argumento:

>>> t1 = ["julianstafari",20,"greydotsec"]
>>> t1
['julianstafari', 20, 'greydotsec']
>>> def agenda(nombre, edad, web):
	print(nombre,edad,web)

	
>>> agenda(*t1)
julianstafari 20 greydotsec
Lo mismo seria en el caso de un diccionario...


Funciones Con El Mismo Nombre:
-------------------------------------

En Python 3 a diferencia de otro lenguajes como Java, al definir una Función con el mismo nombre
Python 3 siempre emplea la ULTIMA que fue definida y esto es básicamente por que Python 3 trata
a las Funciones como un tipo especifico de objeto.

>>> def a(x):
	print(x)

	
>>> def a(x,y):
	print(x,y)

	
>>> a(1,2)
1 2
>>> a(1)
Traceback (most recent call last):
  File "<pyshell#51>", line 1, in <module>
    a(1)
TypeError: a() takes exactly 2 arguments (1 given)

Como podemos observar, Python toma la ultima Función definida para
utilizarla y si tratamos de invocar la primera Función, obtenemos un error
que no nos dice que nuestra Función "a" toma dos argumentos.

>>> type(a)
<class 'function'>
Con la Función "type()", podemos observar como Python 3 trata a las Funciones como
un tipo de objeto especifico.


Lambda:
----------

Básicamente la Función Lambda es una Función especial y a diferencia de otros lenguajes,
en Python 3 tiene que seguir ciertas reglas y se comporta de distinta forma.
Lambda ejecuta determinada expresión, con o sin argumentos y regresa el resultado.
Digamos que hasta cierto punto es como una mini Función --por sintaxis y comportamiento--
Lambda en Python 3 puede ser utilizada como parámetro para otras Funciones y NO puede
contener bucles y tampoco puede utilizar la Función "return()" <<que es una palabra
reservada de Python que se emplea dentro de Funciones para regresar datos y trabajar
con ellos>>.

La sintaxis:
Código: [ Debe registrarse para ver este enlace ]
lambda parámetros : expresión
Y a decir verdad las Funciones lambda no son sentencias, sino expresiones.
A diferencia de las funciones definidas con "def" <<que Python 3 las asocia a un nombre
determinado>>, lambda simplemente regresa un resultado; por ende consume menos memoria
y bla bla bla...

>>> b = lambda x: x*8
>>> b
<function <lambda> at 0x00000000032B90C8>
>>> b(1)
8

Vemos que asignamos a la variable "b" la Función "lambda x: x*5".
Si tratamos de invocar a lambda desde nuestra variable "b" directamente, tan solo recibimos
una dirección de memoria donde esta nuestra Función lambda y No olvidar que es una Función
y por ende requiere argumentos y en este caso uno que es "x"
Se procede a llamar correctamente la Función y cumple correctamente su cometido.

[ Debe registrarse para ver este enlace ]
@Greydotsec
@julianstafari