Introducción a la programación en Python

clase 09a
Funciones (II)

Veremos a continuación algunos temas adicionales referidos a funciones en Python: funciones con número variable de argumentos, funciones anidadas y funciones recursivas.

Número variable de argumentos

Hay ocasiones en las que necesitamos crear una función que pueda recibir un número arbitrario de argumentos. La manera de implementar eso en Python es utilizar argumentos con asterisco: *arg.

Todos los valores que se incluyan en un argumento con asterisco al llamar la función, son guardados en una tupla de argumentos, como se ve en el siguiente programa:

In [1]:
def funcion_1(*args):
    '''número variable de argumentos, que genera una tupla'''
    print(args)
    return None

funcion_1("a")
funcion_1("X", 500, [1, 2, 3])
('a',)
('X', 500, [1, 2, 3])

La tupla de argumentos se puede recorrer como cualquier iterable, para operar sobre cada valor pasado en la llamada a la función:

In [2]:
def funcion_2(*args):
    '''número variable de argumentos, se imprime el total y cada uno de ellos'''
    print('Se llamó a la función con %d argumento(s):' % len(args))
    for arg in args:
        print(arg)
    print('-' * 77)
    return None

funcion_2("a")
funcion_2("X", 500, [1, 2, 3])
Se llamó a la función con 1 argumento(s):
a
-----------------------------------------------------------------------------
Se llamó a la función con 3 argumento(s):
X
500
[1, 2, 3]
-----------------------------------------------------------------------------

En el siguiente programa, la función aplica la operación *=3 a cada uno de los argumentos de la tupla, y lo imprime:

In [3]:
def funcion_3(*args):
    '''número variable de argumentos, a cada uno aplica la operación *=3'''
    for arg in args:
        arg *= 3
        print(arg)
    return None

n = 5   # variable numérica
s = "ABC"    # cadena
t = (1, 2, 3)   # tupla
l = ['x', 'y', 'z']   # lista

print("valor inicial de las variables:")
print('n:', n)
print('s:', s)
print('t:', t)
print('l:', l)

print("\nsalida de la función:")
funcion_3(n, s, t, l)

print("\nvalor de las variables después de llamar la función:")
print('n:', n)
print('s:', s)
print('t:', t)
print('l:', l)
valor inicial de las variables:
n: 5
s: ABC
t: (1, 2, 3)
l: ['x', 'y', 'z']

salida de la función:
15
ABCABCABC
(1, 2, 3, 1, 2, 3, 1, 2, 3)
['x', 'y', 'z', 'x', 'y', 'z', 'x', 'y', 'z']

valor de las variables después de llamar la función:
n: 5
s: ABC
t: (1, 2, 3)
l: ['x', 'y', 'z', 'x', 'y', 'z', 'x', 'y', 'z']

Se pueden observar dos aspectos importantes:

  • el polimorfismo de algunos operadores en Python, que adquieren distinto significado según el tipo de variable sobre la que operan. En este caso, el operador asterisco (=*) realiza la operación de multiplicación en las variables numéricas, y la de repetición en las variables de tipo iterable (cadena, tupla, lista).
  • si dentro de la función se opera sobre el valor de una variable, ésta modificará o no su valor fuera de la función dependiendo de que sea de un tipo mutable (lista) o inmutable (número, cadena, tupla).

argumentos de número variable con argumentos fijos

En una función, un argumento de número variable se puede combinar con argumentos fijos. En una función se puede incluir un único argumento de número variable, que debe ir al final, después de todos los argumentos fijos.

In [4]:
def funcion_4(fijo1, fijo2, *variable):
    print("Argumentos fijos:")
    print(fijo1)
    print(fijo2)
    print("Argumento(s) de número variable:")
    for i in variable:
        print(i)
    print('-' * 77)

funcion_4(1, 2, 3)
funcion_4(1, 2, 3, 4, 5, 6, 7)
Argumentos fijos:
1
2
Argumento(s) de número variable:
3
-----------------------------------------------------------------------------
Argumentos fijos:
1
2
Argumento(s) de número variable:
3
4
5
6
7
-----------------------------------------------------------------------------

Funciones anidadas

Toda función devuelve un valor de algún tipo, y podemos hacer que devuelva una función. Para eso, se define una función dentro de la definición de otra función, a lo que se le llama funciones anidadas.

En el siguiente programa, la función acorde recibe como argumento una lista de intervalos, y devuelve una función que recibe como argumento una nota fundamental. El nombre de esta nueva función se asigna al llamar la función acorde. Esta función devuelve una lista de notas con los intervalos pasados a la función acorde, a partir de la fundamental que recibe como argumento.

In [5]:
def acorde(lista_intervalos):
    '''define tipo de acorde con una estructura de intervalos'''
    def transporte(nota_fundamental):
        '''transporta el tipo de acorde a una fundamental'''
        notas = [nota_fundamental + i for i in lista_intervalos]
        return notas
    return transporte

mayor = acorde([0, 4, 7])
menor = acorde([0, 3, 7])
dominante = acorde([0, 4, 7, 10])

print(mayor(60))
print(menor(60))
print(dominante(67))
[60, 64, 67]
[60, 63, 67]
[67, 71, 74, 77]

Funciones recursivas

La recursión es el proceso de definir algo en función de sí mismo. En programación, una función es recursiva, cuando se invoca a sí misma.

El cuerpo de una función recursiva debe incluir dos cosas:

  • una invocación a la propia función, para que sea efectivamente recursiva
  • una condición de terminación, para que la función no se llame a sí misma infinitamente

Ventajas e inconvenientes

Para cierto tipo de problemas, la solución por recursión puede ofrecer varias ventajas:

  • permite descomponer un problema complejo en problemas más sencillos
  • el código es más compacto y simple que una solución por iteración

Por otra parte, la recursión puede tener algunos inconvenientes:

  • la lógica detrás del algoritmo recursivo puede ser difícil de seguir, lo cual puede hacer más difícil tanto programar como depurar (debug) una función recursiva
  • cada llamada a la función ocupa memoria que no se libera hasta no terminar el ciclo, por lo que el proceso puede llegar a ocupar mucha memoria. Para un número moderado de recursiones, esto no es un problema, pero sí es algo a tener en cuenta si el número de recursiones es muy alto. Como medida preventiva, Python tiene definido un número máximo de recursiones, que se puede ajustar como veremos más adelante

En la definición de una función recursiva, se deben seguir estos pasos:

  1. definir el caso base; es el caso más simple, y funciona como condición de terminación, porque al llegar a ese caso se termina la recursión
  2. determinar cómo se puede expresar un caso dado en términos de un caso más simple, para aproximarse sucesivamente al caso base
  3. llamar a la propia función, para resolver el caso más simple

Un caso típico para resolver por recursión, es el cálculo del factorial de un número (n!), definido como:

n! = 1 x 2 x 3 x .... x n

(Por convención se define que 0! = 1.)

Lo anterior se puede escribir en reversa como:

n! = n x n-1 x n-2 x ... x 1

Por lo tanto:

n! = n x (n-1)!

Es decir que el factorial de un número n es igual a ese número multiplicado por el factorial de n-1. Aprovechando esta propiedad, se puede calcular el factorial mediante una función recursiva.

La siguiente función primero establece como casos base cuando n vale 0 o 1 (n < 2),

In [6]:
def factorial(n):
    '''calcula el factorial de un número entero'''
    if n < 2:
        return 1
    else:
        return n * factorial(n-1)

for i in range(10):
    print('%d! = %d' % (i, factorial(i)))
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

En realidad, para que la función se más robusta, debería en primer término verificar que el número que recibe como argumento se un entero y no sea negativo, ya que la operación factorial solo está definida para números enteros positivos.

El siguiente ejemplo muestra una función con una solución diferente, no por recursión sino por iteración mediante un bucle while. Esta función sí verifica en primer término que el argumento sea un entero positivo, y en caso contrario imprime un mensaje de error y devuelve el valor -1.

In [7]:
def factorial_i(n):
    '''calcula el factorial de un número entero'''

    if type(n) != int or n < 0:
        print('Argumento inválido, la operación factorial debe ser un entero positivo.')
        return -1
    
    fact = 1
    while n > 0:
        fact *= n
        n -=1
    return fact

for i in range(10):
    print('%d! = %d' % (i, factorial_i(i)))
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

Como se puede ver, esta función imprime un mensaje y devuelve el valor -1 cuando el argumento es negativo, o no es un entero.

In [8]:
print(factorial_i(-1))
Argumento inválido, la operación factorial debe ser un entero positivo.
-1
In [9]:
print(factorial_i(1.1))
Argumento inválido, la operación factorial debe ser un entero positivo.
-1

Otro problema típico para resolver por recursión, es el cálculo de los términos de la serie de Fibonacci. Los dos primeros términos de esta serie son 0 y 1, y cada término sucesivo se calcula como la suma de los dos términos anteriores.

La siguiente función calcula de manera recursiva el término n de la serie de Fibonacci. Primero define como casos base cuando n vale 0 o 1 (n < 1), y para todo otro n devuelve la suma de llamar a la propia función con los argumentos n-1 y n-2. Previamente se verifica que el argumento sea un entero positivo.

In [10]:
def fibonacci(n):
    '''calcula el término enésimo de la serie de Fibonacci'''

    if type(n) != int or n < 0:
        print('Argumento inválido, debe ser un entero positivo.')
        return 0

    if n < 2:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

for i in range(10):
    print(fibonacci(i))
0
1
1
2
3
5
8
13
21
34

Límite de recursión

Como se mencionó anteriormente, Python tiene un límite interno de llamadas recursivas en una función, como manera de proteger la memoria. Se puede conocer este límite mediante la función getrecursionlimit() que provee el módulo sys:

In [11]:
import sys
print(sys.getrecursionlimit())
3000

Por lo tanto, si la función recursiva factorial() definida anteriormente se llama con el argumento 3000, va a devolver un error, por exceder el límite de profundidad de la recursión.

In [12]:
print(factorial(3000))
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-12-9eb6552901e5> in <module>
----> 1 print(factorial(3000))

<ipython-input-6-38c247a04344> in factorial(n)
      4         return 1
      5     else:
----> 6         return n * factorial(n-1)
      7 
      8 for i in range(10):

... last 1 frames repeated, from the frame below ...

<ipython-input-6-38c247a04344> in factorial(n)
      4         return 1
      5     else:
----> 6         return n * factorial(n-1)
      7 
      8 for i in range(10):

RecursionError: maximum recursion depth exceeded in comparison

El módulo sys también provee la función setrecursionlimit(), con la cual se puede modificar ese límite de recursión.

El siguiente bloque primeramente redefine el límite de recursión como 4000, luego de lo cual se puede a la función factorial() con un argumento de 3000.

In [13]:
sys.setrecursionlimit(4000)
print("El nuevo límite de recursión es %d" % sys.getrecursionlimit())

print(factorial(3000))
El nuevo límite de recursión es 4000
41493596034378540855568670930866121709511191949318099176894676576975585651235319500860007652178003420075184635383617118495750871114045907794553402161068339611621037904199177522062663390179682805164719697495968842457728766097103003726111095340241127118833157738815328438929737613021106312930374401485378725446079610290429491049793888120762511625132917004641668962117590203575175488980653577868915285093782469994674699190832093511068363824287063522268544339213775150488588104036818809099292912497141900508938994404715351473154531587441509960174267875087460367974117072368747277143988920683691618503608198459718093784453523958505377611086511162363145920886108557450874513945305436213711898150847192094426374203275029996333784944014775671414680824207499914714878359669720638954670589960178569480263388767112871068004950827400717124819476386401369193544354120312786601434792549959143530120653103406625503231020738351502195103148673612338739395096551462159349015789949944072311004426924838140141455487872738045856023561583204317945953055830693351246890721246151468485308724031267967089113548982733475375756899365176396424781733462510879015743437398920492267098317033932107176343983352444576040476565400414414699479984354554597799386702839428513413188913165695310848513525094006147774047007331406541794428004436691903685469270857271701648011512057452448607968773784803660653009109815639091294110633715621540903800135058671624262333902434166628716521228590274568833504897926869369792878376894841436573866436955075473964882256222183380014600761196859217603234808467455216330411738004331144225926243690558782914907973885758784585739828695390302383837265882427654306437517757897215045071361801730051628424476294227485755627828763498767195281368913583918824499284741591683130334032199946752082914885764345863832313545205075955912062067273296951386122994658607527317884452449865348164169238844889061495850934373442889814884427321817131272533891534506581143823381205875379808605080889761753882896252933633750454549168600267229591225528854584482686655324313011353754812409561237686078007700707939541848907149467377854407528307872988103912945121929864793703451257436445581459757140822705986325165352906584571123585270211933452981105568398809884094980346185078025273038736784042169427237980464304250045030806637032760016341921442805708802430850567892108646977455139539119838636167190300278146380136932482332771595180596193069504237836082620570887209297929797429404576877338319877444685544294800321741056689423710545028870419611915072739000031642014474213323293871618029555614004602867400422885389854650328028428515122296028795741801621823236098320971441047012533067314896153236788734984553949604397050352347766211395914519270422122231426998692087463520980686224354813376194395131942868113486531562228173214976481705381846155326596187530296478601160872263640443922257601926494610916885151013143945574398303192557154162151442469122370519149097861849436150963109933639594561796593396851958605338631176324147066842257192394742531726479559749993283247279807896470753054014194090200609712674753186365525403212757757853930697530056595208207457499471898144453772248207888443335118545601568853708182892895218300139654376947286418776665762815389737340159410543681435437346134244692067070082782423645557450882556670157242752810317141640631410681384330924027281318960884813040665226169552825637183862464944295688859393846726723694199475571320546018263425731029115353532728808182773021596787088437293412117084511580629967697266601663635276959969021502122104954259567278593185516268447100374434620422003535391203738393095420695021486207390653190910821344334251497896284236198571674773848126097443055036250866354720730971298084697196537722779893160200560725058007512407494448163392214398118492748281978655178478547749198714138485042290383954090570842038137277135667703565041081780520695032136233521692740531015340921761834078817735674646749071616600653230438902639786065509005309872435445689315601329942407112295015453771521051942445512795364971214872222193729289159833001742397977592530501318837883494884232222507318816399438935627817102875432588794558857742780390717166381257903798149148445526885871629931014510733215554773264576035916184298708323237568837917135073006026738292294687081030751946020376438138677107333779312582257356435534577162804030480925785909747233413932904072239860005448269296110393640127539539899397420021925268928622564959279136369546983247314494094297494213208716963662812963846191378114609210701033012119934264941666449130310898493535366401831282683112506578386425906537197010907276429330534751297336716929415047870949241778121534979499449732358445130210029720359993576507730563696950539990891252004810120090569633144368179194247963563389102486250773367249399801723451627048850149438343735826440053481474957421328873648479589553843836378275601433377798816126854462406494134416119108952653326761627660221130879211665924379496534838030236064294981985541014311566601739518539426008673198564586684635442730180022292607589767192198367529528365158715521887698317999005853121518691037776676883654291247419826099434535671529412823837612115555686210454583810355154404953718470726363218532775486501811002621331228429860926112159573066023932077476742800909462674322138805290643067711276964013735906251051050623568241317651533030775358975134565147424167401517470720839101869989993279364910892687924739705814152855543965954222603919059265825637344676406359525838966981511983959886603683753042017990328185945569412550519066302854869533377682984600031808093822130038102214387057461181304251961916405970456035183121708151658647356556540532928411748628957082856792300053525846377061280591452035546389932127875906349627837975871352588618213252263577038396202737385324908353680497990085701522483303439525197344653342994652565236096742834550523739733902374261808871799283722285366293439240895762913154442106573609205481842139365893867715542842477275100166734357743093638948444564764377184073874379471007867151070449554657626281566137550730763768080600031844296233977808233311359787577136983012817571625671683287281511937336685789437109097748581222868126824122317272681184975207863453107495331708260153159440253645365524453587952034745213429248916644504804355352281977721981971869054884176896398782704782066126921472548618247859626434279190274503452994769367997217285165465591799471789067885687278574470084289723778234763080740919512966238346427839653865017324665850192144091694630371265581197700774682562035198318782913591013997817303635173764706714383992810291224460848320518983248348855131025539721583184931653670732273172995431750775475634748127320956655431851879586978172491721700865768098908327830838240437737974455342525688712898855513180967012497859454290609627370590659970784172738420721605576789060565167694565490120388165775861939230924362983389549857279874523398090499858467484850399509109398834210424693113617875978611803096108774362764990414655167545507613665725914993376114340243762910290384135888531312591132544849225896007184851169390193985434649415483782338302531368775990005443722332901462568184095998830522521585328599833990336595418932696680163265899358234663247080324020429791357425755498549372896192091650794671997121439832581553945835125648010889886887056882711222628734035772418424803231173027338442220604015609242079569493204943809402465562530303328824165302038006041288444384884189129393985971765670211501611340121169355535864984802941563238279447576315042685734269863116562800932164578165410411899078396210758605145091526528422433647230880469088426412525126584729134059195171754291152622002229756986927959124620964363057052133099216422258437651889193630329851223282950806126200573565554213183555838289318138795940962303792777230344423432341561603558590502324475274502630869831414125396371754413611897269158650716722308083435295578401087236027347001118786146233185439431057058483770474806035004556885020602730222256397630738939985024978155182679916994164145540329909813190506654358156657691529068908186204138444091456355291242064901717436430473455191375922914953282988151808740076733486997695322871450791584448703980405737673555777873593937891577147956023340708456392314170118392555234618119775915673385955919265270624063734277760215846511035368057963320714896942663358570375305829676608224208465464558556667889222627619990263961792637457851652540918756608543859661221944248720424960000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000