Functional Programming with
Python
Functionalprogrammingisaparadigmwhere
functions,notobjectsorprocedures,areusedasthe
fundamentalbuildingblocksofaprogram.
Whiletalkingaboutfunctionalcharacteristics,we
comeacrosshostofterms,somefamiliarandfewnot
[Link]
[Link]
immutabledata,firstclassfunctionsandtailcall
[Link]
[Link],wehearabout
mapping,reducing,pipelining,recursing,
[Link]
areprogrammingtechniquesusedtowritefunctional
[Link],lazy
[Link]
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
the changed copy. This eliminates bugs that arise
from a programmers incomplete model of the
possiblestatestheirprogrammayenter.
Languages that support first class functions allow
functions to be treated like any other value. This
means they can be created, passed to functions,
returned from functions and stored inside data
structures.
Tail call optimization is a programming language
[Link],anewstack
[Link]
argumentsandlocalvaluesforthecurrentfunction
[Link]
times,itispossiblefortheinterpreterorcompilerto
run out of memory. Languages with tail call
optimisation reuse the same stack frame for their
entire sequence of recursive calls. Languages like
Python that do not have tail call optimization
generallylimitthenumberoftimesafunctionmay
recursetosomenumberinthethousands.
Currying means decomposing a function that takes
multipleargumentsintoafunctionthattakesthefirst
argumentandreturnsafunctionthattakesthenext
argument,andsoforthforallthearguments.
Functionsarefirstclass(objects).Thatis,everything
youcandowith"data"canbedonewithfunctions
themselves(suchaspassingafunctiontoanother
function).
However,Functionalcodecanbecharacterizedjust
byonething:[Link]
relyondataoutsidethecurrentfunction,andit
doesntchangedatathatexistsoutsidethecurrent
[Link]
derivedfromthisproperty.
FollowingisapieceofcodewritteninPython,which
isunfunctional:
a=0
defincrement1():
globala[forcestheinterpretertousetheglobal
variable,a]
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
[Link]
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
[Link]
[Link]
whenpassingasimplefunctionasanargumentto
[Link]
(includingoptionalarguments)andreturnsthevalue
[Link]
containcommands,andtheycannotcontainmore
thanoneexpression.
Thesyntaxconsistsofthelambdakeywordfollowed
byalistofarguments,acolon,andtheexpressionto
[Link]
[Link]
[Link]
runningthefunctionbodyis(implicitly)returned.
deff(x):...
returnx*2...
print(f(3))
6
g=lambdax:x*2
g(3)
6
(lambdax:x*2)(3)
6
lambdafunctioncanbeusedwithoutevenassigning
[Link]
intheworld,butitjustgoestoshowthatalambdais
justaninlinefunction.
Anotherexample:
processFunc=collapseand(lambdas:"
".join([Link]()))or(lambdas:s)
alambdafunctionisalwaystrueinabooleancontext.
(Thatdoesn'tmeanthatalambdafunctioncan'treturn
[Link];itsreturn
valuecouldbeanything.)
Intheaboveexample,processFuncisnowa
function,butwhichfunctionitisdependsonthe
[Link],
processFunc(string)willcollapsewhitespace;
otherwise,processFunc(string)willreturnits
argumentunchanged.
Nested Functions
Functionscanbedefinedwithinthescopeofanother
[Link],
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
[Link]
[Link],itis
possibletoreturnaninnerfunctionthatremembers
thestateoftheouterfunction,evenaftertheouter
[Link]
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
[Link]
generatealistcontainingnumbersfrom1to25:
alist=[]
foriinrange(1,26):
...[Link](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']
[[Link]()forlineinalist]
['Thisisline1','Thisisline2','','Thisisline3']
Heresamodificationoftheabove,whichtestsif
astringisanullstring,anddoesntprintifthatisthe
case.
[[Link]()forlineinalist
...ifline!='']
['Thisisline1','Thisisline2','Thisisline3']
Theactualsyntaxoflistcomprehensionisas
follows:
[expressionforiteminsequenceifcondition]
Thiscanalsoberememberedinthefollowingway:
Resultant_list=[transformiterationfilter]
TheequivalentPythoncodefortheaboveis
shownbelow:
foriteminsequence:
if(condition):
expression
LoopofLoops:
Itsworthmentioningthatyoucanuselist
[Link]
example:
list_a=['A','B']
list_b=[1,2]
[(x,y)forxinlist_aforyinlist_b]
[('A',1),('A',2),('B',1),('B',2)]
Here,thefirstlististheouterloopandthelastloopis
[Link]
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.
[Link]
names,whichonlydifferinthecaseusedtorepresent
them,duplicatesandnamesconsistingofonlyone
[Link]
thenonecharacterandwishtorepresentallnamesin
thesameformat:Thefirstlettershouldbecapitalised,
allothercharactersshouldbelowercase.
Giventhelist:
names=['Bob','JOHN','alice','bob','ALICE','J',
'Bob']
Werequiretheset:
{'Bob','John','Alice'}
[Link]
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([Link])seq.
map()appliesthefunctionfunctoalltheelementsof
[Link]
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:
name_lengths = map(len, ["Mary", "Isla",
"Sam"])
print name_lengths # => [4, 4, 3]
This is a map that squares every number in the passed
collection:
squares = map(lambda x: x * x, [0, 1, 2, 3,
4])
print squares # => [0, 1, 4, 9, 16]
Reduce
[Link]
returns a value that is created by combining the
[Link],Reduceisusedtoaggregate(orreduce)a
particularcollectionintoavaluebyprocessingeach
item of the given collection with a particular
function.
[Link]
itemsinthecollection.
sum = reduce(lambda a, x: a + x, [0, 1, 2,
3, 4])
print sum # => 10
xis the current item being iterated [Link] the
[Link]
of the lambda on the previous [Link]()walks
[Link],itrunsthelambda
on the currentaandxand returns the result as
theaofthenextiteration.
Whatisainthefirstiteration?Thereisnoprevious
[Link]()usesthe
firstiteminthecollectionforainthefirstiteration
[Link],the
firstxistheseconditem.
Filter
Asthenamesuggests,filterextractseachelementin
thesequenceforwhichthefunctionreturnsTrue.
ThesignatureoftheFilterfunction:
modified_list=filter(func,list)
Ittakesafunction,[Link]
returnsaBooleanvalue,[Link]
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]