EDIT: typo, changed link
EDIT: typo, changed link
I could write these clients as lambdas or nested functions which close over the stuff which init creates. But why? An object makes it much clearer that there is state.
But the parent comment applies
def __init__(self):
pass
Not necessarily. Or the constructor is just setting some values that are later used by the calculation but are not modified and used later var quadrupler = new ConstantMultiplier(4);
var output = quadrupler.applyTo(42);
The 'quadrupler' instance is stateless in that it's immutable, but it presumably has a (constant) member to store the value 4.For example, I recently wrote this:
const labelizeElement = ((element) => element.type.replace(/\s+/g, ''));
const labelizeRelation = ((relation) => relation.type.toUpperCase().replace(/\s+/g, '_'));
const groupByLabels = (labelizer) => (set, element) => (
set[labelizer(element)]
? { ...set, [labelizer(element)]: [element, ...set[labelizer(element)]] }
: { ...set, [labelizer(element)]: [element] }
);
const groupByElement = groupByLabels(labelizeElement);
const groupByRelation = groupByLabels(labelizeRelation);
I needed two functions that did almost the same thing, but not quite. I don't want to write a ton of nearly-duplicate code, so instead I extract the bits that are different, and write a function that I actually need.So yes, I'm inclined to say that only mutable state needs a class. And the more you get used to working with constants and immutables, the less mutable state you're going to find.
I used to be a big fan of OO, but somehow experience seems to have landed me in the functional programming camp.
const labelizeElement = ...
const labelizeRelation = ...
const groupByLabels = (labelizer, set, element) => ....
const groupByElement = (set, element) => groupByLabels(labelizeElement, set, element);
const groupByRelation = (set, element) => groupByLabels(labelizeRelation, set, element);
This makes it slightly simpler to create a binding function around groupByLabels which fixes, say, the middle argument. I also think that avoiding the chained use of the => operator makes the code more readable to the average JavaScript dev.C++ has (strange looking) support for the bind pattern in its standard-library now [0], and Python3 has functools.partial but it only supports left-to-right binding [1] (not that there's any particular reason to avoid lambdas).
[0] https://en.cppreference.com/w/cpp/utility/functional/bind
In languages that let you operator overload function call syntax, you end up with an object that, once constructed, supports being called like with same syntax as a function call. This works easily in python (define __init__ and __call__ ), and you don't have to fight the typesystem to structure code that will accept both a callable object or a function.
Another perspective of the whole thing is that you have a function with many arguments, then you curry to bind some arguments, then pass the resulting function with remaining free arguments to be called.
I prefer structuring code as functor objects as it lets you access the bound parameters as attributes (if you want to expose them) which can also sometimes be useful in code or in test
> Another perspective of the whole thing is that you have a function with many arguments, then you curry to bind some arguments, then pass the resulting function with remaining free arguments to be called.
It's not the same, though, because a socket etc is being constructed in the constructor. Here's an abridged (and possibly wrong!) version of a monitoring client:
class Monitor:
def __init__(self, monitoring_url, app_name):
self.monitoring_url = monitoring_url
self.session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=1))
self.details = ujson.dumps({'app_name': app_name, 'pid': os.getpid()})
async def send(self):
async with self.session.post(monitoring_url, data=self.details, raise_for_status=True) as resp:
pass
If you treat that as currying, you will create a new ClientSession every time you call ping(). A ClientSession contains a connection pool, so that means you will create a new socket instead of reusing one.> In languages that let you operator overload function call syntax, you end up with an object that, once constructed, supports being called like with same syntax as a function call. This works easily in python (define __init__ and __call__ ), and you don't have to fight the typesystem to structure code that will accept both a callable object or a function.
In Python and Java, you can easily refer to bound methods to produce callables from objects, so this seems like unnecessary work.
After that, it's a question of taste. Agreed, callable objects can be useful but if you don't modify your "self" during function calls, partial funciton aplication might be clearer.
This is my key question, and I ask with minimal assumption that either one is better.
What are the criteria we are even using to judge? Clarity that there is state is one. Elsewhere there are arguments about ABI that I fail to have practical knowledge of. There was some discussion of the data structure describing your program that completely lost me. Explicitness is lost when discussing closures.
What makes one better than the other in ways that arent entirely subjective?
And then the resulting classes probably meet some other rule that says "reduce trivial classes to pure functions or closures".
Generally speaking, before a person writes a private function, they should remember that it means "if you misuse it you'll break my otherwise unprotected invariants." Whenever that's not true, you shouldn't write "private". In particular, it doesn't mean "I was too lazy to structure my code correctly so I tried to hide it from the public implementation".
I have some functions that live in closures that are fairly generic. I’m always contemplating extracting them into a higher level. But they’re currently only used in one place, and they currently reside close to that one place, and this seems sensible.
Classes, of course, offer inheritance. Yet most prefer composition over inheritance. Classes, too, offer polymorphism through typing. But dynamic languages use duck typing. In most dynamic languages where duck typing and composition are used, I find little need for traditional object orientated programming.
I frankly prefer closures, composition and duck-typing over the classes, inheritance and polymorphism through typing. The only thing missing is type checking and Golang's interfaces offer a way through that without the overhead of traditional object orientated design.
Leading single underscore is private by convention. Still should generally be avoided, but it’s the proper use.
>>> def a(u, v):
... def b():
... return u + v
... return b
...
>>> b = a(1,2)
>>> b()
3
Like this, there is no way to access u & v from b. >>> def f():
... x = 5
... class Blub:
... def incx(self):
... x += 1
... def getx(self):
... return x
... return Blub()
...
>>> j = f()
>>> j.incx()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in incx
UnboundLocalError: local variable 'x' referenced before assignment def f():
x = 5
class Blub:
def incx(self):
nonlocal x
x += 1
def getx(self):
return x
return Blub()
j = f()
j.incx()
print(j.getx())
This will print 6, as expected.Sure, you could handle this the functional way by passing a getData() function to run() so it can do so itself but I'm not sure that's any more readable.
def x():
expensive precompute
def y():
cheap stuff
return y
It's a matter of taste I guess. class Thing:
def __init__(self, arg):
# expensive precompute
self.result = ...
def __call__(self, arg):
# cheap stuff that uses self.result for something
...
And then usage is: f = Foo("some val")
bar = f("another val")
So yeah, both definitely do the same job. And your pattern is probably faster. But I generally like using Python's dunder methods as I think they allow for more consistent/recognizable code patterns.# pylint: disable=too-few-public-methods