Functional Programming in Python
Functional Programming in Python
Python
Functionalprogrammingisaparadigmwhere
functions,notobjectsorprocedures,areusedasthe
fundamentalbuildingblocksofaprogram.
Whiletalkingaboutfunctionalcharacteristics,we
comeacrosshostofterms,somefamiliarandfewnot
sofamiliar.Letsdigdeeperintothemanddissect
eachoneofthem.Thethingsthatwehearare
immutabledata,firstclassfunctionsandtailcall
optimisation.Thesearelanguagefeaturesthataid
functionalprogramming.Next,wehearabout
mapping,reducing,pipelining,recursing,
currying4andtheuseofhigherorderfunctions.These
areprogrammingtechniquesusedtowritefunctional
code.Thelistgoesonparallelization,lazy
evaluation6anddeterminism.Theseareadvantageous
propertiesoffunctionalprograms.
An immutable piece of data is one that cannot be
changed. Some languages, like Clojure, make all
values immutable by default. Any mutating
operationscopythevalue,changeitandpassback
a+=1
Thisisanexampleoffunctionalfunction:
defincrement2(a):
returna+1
Functions as Objects
FunctionsarefirstclassobjectsinPython,
meaningtheyhaveattributesandcanbereferenced
andassignedtovariables.
defi_am_an_object(myarg):...
'''Iamanicefunction....
Pleasebemyfriend.'''...
returnmyarg...
print(i_am_an_object(1))
1
an_object_by_any_other_name=
i_am_an_object
print(an_object_by_any_other_name(2))
2
print(i_am_an_object)
<functioni_am_an_objectat0x100432aa0>
print(an_object_by_any_other_name)
<functioni_am_an_objectat0x100432aa0>
print(i_am_an_object.__doc__)
'Iamanicefunction.\nPleasebemyfriend.'
Higher-Order Functions
Pythonsupportshigherorderfunctions,meaning
thatfunctionscanacceptotherfunctionsas
argumentsandreturnfunctionstothecaller.
Map,reduceandfilter,thefunctions,whichhelp
ustoworkwithdatastructuresrelyonhigherorder
functionsfortheiroperations.Allthethreefunctions
acceptafunctionreferenceasanargumenttoact
uponeachoftheelementsofthelistpassedasthe
secondargument.
resultant_list=map(func,seq)
Here,funcisafunctionwhosereference/pointer
ispassedontothefunctionmap.
Belowisanexampleofhigherorderfunction
deftransformer(element):
returnelement*5
defmy_map(funct,element_list)
modified_list=[]
forelementinelement_list:
modified_list.append(funct(element))
returnmodified_list
seq=[1,2,3,4,5]
transformed_list=my_map(transformer,seq)
print(transformed_list)
[5,10,15,20,25]
Anonymous Functions
AnonymousfunctionsinPythonarecreatedby
usingthelambdastatement.Alambdaisaninline
function.Thisapproachismostcommonlyused
whenpassingasimplefunctionasanargumentto
anotherfunction.Ittakesanynumberofarguments
(includingoptionalarguments)andreturnsthevalue
ofasingleexpression.lambdafunctionscannot
containcommands,andtheycannotcontainmore
thanoneexpression.
Thesyntaxconsistsofthelambdakeywordfollowed
byalistofarguments,acolon,andtheexpressionto
evaluateandreturn.Theparametersofthelambdaare
definedtotheleftofthecolon.Thefunctionbodyis
definedtotherightofthecolon.Theresultof
runningthefunctionbodyis(implicitly)returned.
deff(x):...
returnx*2...
print(f(3))
6
g=lambdax:x*2
g(3)
6
(lambdax:x*2)(3)
6
lambdafunctioncanbeusedwithoutevenassigning
ittoavariable.Thismaynotbethemostusefulthing
intheworld,butitjustgoestoshowthatalambdais
justaninlinefunction.
Anotherexample:
processFunc=collapseand(lambdas:"
".join(s.split()))or(lambdas:s)
alambdafunctionisalwaystrueinabooleancontext.
(Thatdoesn'tmeanthatalambdafunctioncan'treturn
afalsevalue.Thefunctionisalwaystrue;itsreturn
valuecouldbeanything.)
Intheaboveexample,processFuncisnowa
function,butwhichfunctionitisdependsonthe
valueofthecollapsevariable.Ifcollapseistrue,
processFunc(string)willcollapsewhitespace;
otherwise,processFunc(string)willreturnits
argumentunchanged.
Nested Functions
Functionscanbedefinedwithinthescopeofanother
function.Ifthistypeoffunctiondefinitionisused,
theinnerfunctionisonlyinscopeinsidetheouter
function,soitismostoftenusefulwhentheinner
functionisbeingreturned(movingittotheouter
scope)orwhenitisbeingpassedintoanother
function.
Inthegivenexample,anewinstanceofthe
functioninner()iscreatedoneachcalltoouter().That
isbecauseitisdefinedduringtheexecution
ofouter().Thecreationofthesecondinstancehasno
impactonthefirst.
defouter():...
definner(a):...
returna...
returninner...
f=outer()
print(f)
<functioninnerat0x1004340c8>
print(f(10))
10
f2=outer()
print(f2)
<functioninnerat0x1004341b8>
print(f2(11))
11
Closures
Essentially,aCLOSUREisafunctionobjectthat
remembersvaluesinenclosingscopesregardlessof
whetherthosescopesarestillpresentinmemory.
Anestedfunctionhasaccesstotheenvironmentin
whichitwasdefined.Thedefinitionoccursduring
theexecutionoftheouterfunction.Therefore,itis
possibletoreturnaninnerfunctionthatremembers
thestateoftheouterfunction,evenaftertheouter
functionhascompletedexecution.Thismodelis
referredtoasaclosure.
defouter2(a):...
definner2(b):...
returna+b...
returninner2...
add1=outer2(1)
print(add1)
<functioninner2at0x100519c80>
print(add1(4))
5
print(add1(5))
6
add2=outer2(2)
print(add2)
<functioninner2at0x100519cf8>
print(add2(4))
6
print(add2(5))
7
Closurescanbeusefulinthefollowing
scenarios:
Replacinghardcodedconstants
Eleminatingglobals
Providingconsistentfunctionsignatures
ImplementingObjectOrientation
List comprehensions
Listcomprehensionsareeasywaysofgenerating
listsfromagivenlist.Considerthefollowingcodeto
generatealistcontainingnumbersfrom1to25:
alist=[]
foriinrange(1,26):
...alist.append(i)
print(alist)
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
18,19,20,21,22,23,24,25]
Analternative,functionalapproachtothesame
wouldbeasfollows:
alist=[iforiinrange(1,26)]
print(alist)
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
18,19,20,21,22,23,24,25]
Noticethatthiscodeismoreintuitiveandeasy
tounderstandandbecomesshorteraswell!
However,whileimplementingalistcomprehension
makesuretotakecareofanyexceptionscenario,as
thecodeislikelytothrowexceptionsiftheexception
scenariosarenotaccountedfor.
Anotherexamplehowtostripwhitespace
fromalistofstrings:
alist=['Thisisline1\n','Thisisline2\n','',
'Thisisline3\n']
[line.strip()forlineinalist]
['Thisisline1','Thisisline2','','Thisisline3']
Heresamodificationoftheabove,whichtestsif
astringisanullstring,anddoesntprintifthatisthe
case.
[line.strip()forlineinalist
...ifline!='']
['Thisisline1','Thisisline2','Thisisline3']
Theactualsyntaxoflistcomprehensionisas
follows:
[expressionforiteminsequenceifcondition]
Thiscanalsoberememberedinthefollowingway:
Resultant_list=[transformiterationfilter]
TheequivalentPythoncodefortheaboveis
shownbelow:
foriteminsequence:
if(condition):
expression
LoopofLoops:
Itsworthmentioningthatyoucanuselist
comprehensionstoiterateonmorethanonelist.For
example:
list_a=['A','B']
list_b=[1,2]
[(x,y)forxinlist_aforyinlist_b]
[('A',1),('A',2),('B',1),('B',2)]
Here,thefirstlististheouterloopandthelastloopis
theinnerloop.Alsonotethatthismethodreturnsa
listoftuples.
Ifyoudlikenestedlists,youcanalsonestonelist
comprehensionwithinanother.
list_a=['A','B']
list_b=['C','D']
[[x+yforxinlist_a]foryinlist_b]
[['AC','BC'],['AD','BD']]
Here,thelatterlistisexecutedfirst,whichmeansthatitis
theouterloopandthenthefirstloopisexecuted,makingit
theinnerloop.
Dictionary comprehension
Dictionarycomprehensionsareaneasywaytogeneratea
dictionarybyprocessingagivendictionary.
My_dict = {a:5, b:10, c:15, d:20, e:25, f:30}
another_dict = {k:v*2 for k:v in my_dict}
print(another_dict = %s %( another_dict))
another_dict = {a:10, b:20, c:30, d:40, e:50,
f:60}
Set Comprehensions
Setcomprehensionsallowsetstobeconstructed
usingthesameprinciplesaslistcomprehensions,the
onlydifferenceisthatresultingsequenceisaset.
Saywehavealistofnames.Thelistcancontain
names,whichonlydifferinthecaseusedtorepresent
them,duplicatesandnamesconsistingofonlyone
character.Weareonlyinterestedinnameslonger
thenonecharacterandwishtorepresentallnamesin
thesameformat:Thefirstlettershouldbecapitalised,
allothercharactersshouldbelowercase.
Giventhelist:
names=['Bob','JOHN','alice','bob','ALICE','J',
'Bob']
Werequiretheset:
{'Bob','John','Alice'}
Notethenewsyntaxfordenotingaset.Membersare
enclosedincurlybraces.
Thefollowingsetcomprehensionaccomplishesthis:
{name[0].upper()+name[1:].lower()fornamein
namesiflen(name) > 1 }
Map
Map takes a function and a collection of items. It
makesanew,emptycollection,runsthefunctionon
eachitemintheoriginalcollectionandinsertseach
return value into the new collection. It returns the
newcollection.
Mapisthususedtocreateanewcollectionoutofa
collectionafterprocessingeachitemofthecollection
usingagivenfunction.
r=map(func,seq)
Thefirstargumentfuncisthenameofafunctionand
thesecondasequence(e.g.alist)seq.
map()appliesthefunctionfunctoalltheelementsof
thesequenceseq.Itreturnsanewlistwiththe
elementschangedbyfunc
Example :
deffahrenheit(T):
return((float(9)/5)*T+32)
defcelsius(T):
return(float(5)/9)*(T32)
temp=(36.5,37,37.5,39)
F=map(fahrenheit,temp)
C=map(celsius,F)
Thisisasimplemapthattakesalistofnamesand
returnsalistofthelengthsofthosenames:
Reduce
Reducetakesafunctionandacollectionofitems.It
returns a value that is created by combining the
items.Thus,Reduceisusedtoaggregate(orreduce)a
particularcollectionintoavaluebyprocessingeach
item of the given collection with a particular
function.
Thisisasimplereduce.Itreturnsthesumofallthe
itemsinthecollection.
sum = reduce(lambda a, x: a + x, [0, 1, 2,
3, 4])
print sum # => 10
Filter
Asthenamesuggests,filterextractseachelementin
thesequenceforwhichthefunctionreturnsTrue.
ThesignatureoftheFilterfunction:
modified_list=filter(func,list)
Ittakesafunction,funcasitsfirstargument.func
returnsaBooleanvalue,i.e.eitherTrueorFalse.This
functionwillbeappliedtoeveryelementofthe
listlist.
OnlyiffuncreturnsTruewilltheelementofthelist
beincludedintheresultantmodified_list.
fib=[0,1,1,2,3,5,8,13,21,34,55]
result=filter(lambdax:x%2,fib)
printresult
[1,1,3,5,13,21,55]
result=filter(lambdax:x%2==0,fib)
printresult
[0,2,8,34]