<jupyter_start><jupyter_text>A veces, al igual que en matemáticas, es necesario crear una *función* para calcular algún resultado de una entrada, o realizar alguna acción de manera *modularizada*, es decir, separada del resto del código.Esto puede ser útil en muchas situaciones, especialmente en el caso de que tengamos un fragmento de código que se ejecuta varias veces; Si creamos una función, esto puede evitar repeticiones del código y facilitar el mantenimiento.Primero, pensemos en funciones matemáticas simples, tales como$$f(x) = x^2.$$Para definir esta función en Python, usamos la palabra clave `def`:<jupyter_code>def f(x):
    return x**2<jupyter_output><empty_output><jupyter_text>En la celda anterior, hemos definido una función que, dada $x$, genera el valor de $x ^ 2$. Puede ver que la palabra clave `return` nos dice qué valor se devuelve cuando se ejecuta la función. Además, las acciones que se ejecutarán en el ámbito de la función (que puede contener varias líneas de código) deben estar dentro de un bloque de código, al igual que para los bloques de repetición y condicionales.Finalmente, puedes ver que la función `f` no ha sido *llamada* aún, simplemente definida. Para llamar a esta función y ejecutar las acciones enumeradas dentro de la función, usamos la siguiente sintaxis:<jupyter_code>f(3)
y = f(3)
print(y)<jupyter_output>9<jupyter_text>En una función de Python, varias cosas pueden suceder. Por ejemplo, podemos definir<jupyter_code>def myfunction(x):
    print('Hi, here I am!')<jupyter_output><empty_output><jupyter_text>Tenga en cuenta que<jupyter_code>y = myfunction(2)<jupyter_output>Hi, here I am!<jupyter_text>Para esta función, el argumento de entrada $x$ no se ha utilizado; de hecho, la función tampoco devuelve un valor:<jupyter_code>y<jupyter_output><empty_output><jupyter_text>Podemos definir funciones sin argumentos de entrada o salida:<jupyter_code>def f():
    print("Here.")
f()<jupyter_output>Here.<jupyter_text>Tenga en cuenta que los nombres de los argumentos de entrada y salida no son importantes y no tienen que coincidir; solo sus posiciones:<jupyter_code>y = 4

def compute_cube(number):
    return number**3

cube = compute_cube(y)
print(cube)<jupyter_output>64<jupyter_text>Los argumentos de entrada y salida en una función explicitan qué variables se conocen dentro de esta función. Sin embargo, debemos tener en cuenta el *alcance* de nuestras funciones. Esto significa, de una manera muy simplificada, que:- Las variables que están definidas dentro de una función pero no están listadas como argumentos de salida no serán accesibles fuera de la función.- Las variables definidas fuera de una función pueden ser visibles dentro de la función, *a menos que estén redefinidas en la función*.Veamos algunos ejemplos.<jupyter_code>def inaccessible_variable():
    myvar = 1
myvar<jupyter_output><empty_output><jupyter_text>Ahora, observa qué sucede con las variables definidas fuera de la función.<jupyter_code>a = 2
def add(x,y):
    print('x+y is {}'.format(x+y))
    print('a is {}'.format(a))
    return x+y<jupyter_output><empty_output><jupyter_text>Puede ver que `a` no se ha listado como una variable de entrada para la función `add`, pero se puede acceder desde dentro de la función:<jupyter_code>add(2,3)<jupyter_output>x+y is 5
a is 2<jupyter_text>Por otro lado, redefinamos la función así:<jupyter_code>def add(x,y):
    print('x+y is {}'.format(x+y))
    print('a is {}'.format(a))
    a = 3
    return x+y<jupyter_output><empty_output><jupyter_text>Podríamos pensar que cuando calculamos la función `add` para dos números cualquiera, el flujo de ejecución continuará normalmente, imprimiendo "a es 2" y, después de eso, cambiando el valor de `a` a 3. Esto no sucede, aunque:<jupyter_code>add(2,3)<jupyter_output>x+y is 5<jupyter_text>Como la variable `a` se redefine (se le asigna un nuevo valor) dentro de la función` add`, no se puede acceder a ella antes de esta redefinición. Esto evita problemas con la reutilización de nombres de variables y la sobrescritura accidental de valores.Veamos algunos ejemplos para probar el alcance de este concepto:<jupyter_code>name = 'MyName'
def f(): 
    print("Inside f(), name = {}".format(name))<jupyter_output><empty_output><jupyter_text>Dado que la función `f` definida en la celda anterior no define una variable llamada` nombre`, accedemos al valor *global* de esta variable:<jupyter_code>f()<jupyter_output>Inside f(), name = MyName<jupyter_text>Por otra parte,<jupyter_code>def g():
    name = 'Leia'
    print("Inside g(), name = {}".format(name)) 
g()<jupyter_output>Inside g(), name = Leia<jupyter_text>Ahora si lo hacemos<jupyter_code>def h():
    print("Inside h(), name = {}".format(name)) 
    name = 'Leia'<jupyter_output><empty_output><jupyter_text>veremos un error:<jupyter_code>h()<jupyter_output><empty_output><jupyter_text>Esto es consistente con la idea de que se le prohíba acceder al valor de una función definida dentro de la función antes de que se le haya asignado un valor localmente. Para obtener más información sobre el alcance y la idea de las variables locales y globales, consulte [la documentación oficial](https://docs.python.org/3/faq/programming.htmlwhat-are-the-rules-for-local-and-global-variables-in-python). ---Podemos definir una función con múltiples argumentos de entrada y salida:<jupyter_code>def NewLanguage(word1, word2, word3, word4):
    print('Words:')
    print(word1)
    print(word2)
    print(word3)
    print(word4)
    return ['Pe'+word1, 'Pe'+word2, 'Pe'+word3, 'Pe'+word4, 'end']
NewLanguage('my', 'name', 'is', 'bond')
output = NewLanguage('my', 'name', 'is', 'bond')
type(output)<jupyter_output><empty_output><jupyter_text>Argumentos opcionalesA veces, puede ser útil tener argumentos con valores predeterminados para ser utilizados en caso de que no estén explícitamente en la llamada a la función.Por ejemplo, imagine que tiene un sistema de aire acondicionado donde el usuario puede elegir una temperatura en grados Celsius. Si el usuario no decide explícitamente por un valor, el dispositivo funcionará con un valor predeterminado de 25 grados Celsius.<jupyter_code>def air_conditioning(temperature=25):
    print("The temperature is {}".format(temperature))<jupyter_output><empty_output><jupyter_text>Ahora, si llamamos a la función `air_conditioning` sin argumentos, el valor de la variable de temperatura es 25; de lo contrario, el usuario sobrescribirá el valor.<jupyter_code>air_conditioning()
air_conditioning(10)<jupyter_output>The temperature is 10<jupyter_text>Argumentos de palabras claveAlgunos argumentos especiales se pueden definir utilizando palabras clave. Por ejemplo,<jupyter_code>def quadratic(a, b, c, x):
    return a*x**2+b*x+c<jupyter_output><empty_output><jupyter_text>Podemos llamar a esta función como de costumbre:<jupyter_code>quadratic(1,1,1,0)<jupyter_output><empty_output><jupyter_text>También podemos llamar a esta función así:<jupyter_code>quadratic(a=1, b=1, c=1, x=0)<jupyter_output><empty_output><jupyter_text>Esta última formulación puede ser interesante si decidimos, por ejemplo, cambiar el orden de la llamada de función:<jupyter_code>quadratic(x=0, a=1, b=2, c=1)<jupyter_output><empty_output><jupyter_text>Por otro lado, podemos hacer esto solo con un subconjunto de los argumentos de entrada:<jupyter_code>quadratic(1,1,1, x=0)<jupyter_output><empty_output><jupyter_text>Sin embargo, hay una reestructuración: los argumentos de las palabras clave deben aparecer en último lugar en la llamada a la función:<jupyter_code>quadratic(x=1, 1,1,1)<jupyter_output><empty_output><jupyter_text>Podemos mezclar palabras clave y argumentos opcionales en una llamada de función:<jupyter_code>def quadratic(x, a=1, b=1, c=1):
    return a*x**2+b*x+c
quadratic(0)
quadratic(0, b=-1)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09192022500639223, 'fraction_numerical': 0.009460496036819227, 'mean_word_length': 6.338649155722327, 'pattern_counts': {'":': 0, '<': 100, '<?xml version=': 0, '>': 100, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '27', 'dir': 'jupyter-structured-clean-dedup', 'id': '660697', 'license': 'MIT', 'n_tokens_mistral': 2806, 'n_tokens_neox': 2741, 'n_words': 881, 'path': 'content/es/Notebook 05 - Funciones.ipynb', 'repo_name': 'mauriciolanda4/callicore', 'size': '7822'}