I'm trying to transition from OOP to functional programming. I have the following situation: (the variables make no sense - they're just examples).
Funcs = namedtuple('Funcs', ('func1', 'func2'))
def thing_1(alpha, beta):
gamma = alpha+beta
def func_1(x):
return x+gamma
def func_2(x):
return x*gamma
return Funcs(func_1, func_2)
def thing_2(alpha, beta):
gamma = alpha+beta
delta = alpha*beta
def func_1(x):
return x+gamma
def func_2(x):
return x*gamma+delta
return Funcs(func_1, func_2)
Now, we have some code duplication: func_1
is the same in both things. Both things also initialize gamma the same way.
If I use OOP, it's obvious what to do - make a BaseThing
, make func_2
abstract, and have Thing1 override method func_2
, and Thing2
override both the func_2
method and __init__
(which will call BaseThing.__init__
then initialize delta).
Using closures, it's not obvious to me - what's the best way to do the same thing?
I didn't understand your specific example, but in a more abstract way, the difference between OOP and FP can be summarized as follows:
In other words, the behavior of an object depends on (or "is parametrized by") virtual methods it calls. To fix (or "specialize") a certain set of "parameters" (=methods), you extend the object.
To parametrize a function you pass other functions to it. To fix a set of parameters you create a new function which is the base func with partially applied params.
Illustration:
# OOP style
class Animal:
def voice(self):
pass
def greet(self, person):
return '%s, %s!' % (self.voice(), person)
class Dog(Animal):
def voice(self):
return 'woof'
class Cat(Animal):
def voice(self):
return 'meow'
print Dog().greet('master')
print Cat().greet('master')
# FP style
def greet(voice, person):
return '%s, %s!' % (voice(), person)
from functools import partial
dogGreet = partial(greet, lambda: 'woof')
catGreet = partial(greet, lambda: 'meow')
print dogGreet('master')
print catGreet('master')
This works, but it's not particularly neat.
#!/usr/bin/env python
from collections import namedtuple
Funcs = namedtuple('Funcs', ('func1', 'func2'))
def thing_1(alpha, beta):
gamma = alpha+beta
def func_1(x):
return x+gamma
def func_2(x):
return x*gamma
return Funcs(func_1, func_2)
t1 = thing_1(3, 7)
print t1.func1(10), t1.func2(10)
def thing_2(alpha, beta):
delta = alpha*beta
t = thing_1(alpha, beta)
def func_2(x):
return t.func2(x) + delta
return Funcs(t.func1, func_2)
t2 = thing_2(4, 6)
print t2.func1(10), t2.func2(10)
output
20 100
20 124
The most basic way is to create a separate closure for func_1
:
def gammafied_func_1(gamma):
def func_1(x):
return x + gamma
return func_1
def thing_1(alpha, beta):
gamma = alpha+beta
def func_2(x):
return x*gamma
return Funcs(gammafied_func_1(gamma), func_2)
This sort of thing comes up often enough that there is a higher-order function for it, called partial , referring to the general concept of partial application. That lets you use one function to create a smaller function with some of its parameters "frozen":
from functools import partial
def func_1(gamma, x):
return x + gamma
def thing_1(alpha, beta):
gamma = alpha+beta
def func_2(x):
return x*gamma
return Funcs(partial(func_1, gamma), func_2)
Here, partial(func_1, gamma)
returns a new function that has the same body as func_1
, except it only takes an x
parameter and gamma
has been "frozen" to the local gamma
inside thing_1
.
Scope works:
In [22]: def f1(x):
....: return x + gamma
....:
In [23]: def t1():
....: return f1("foo")
....:
In [24]: gamma = "bar"
In [25]: t1()
Out[25]: 'foobar'
f1
closes over gamma
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.