Is it OK to replace a method by a plain function? - python

This works as expected, but I am somehow unsure about this approach. Is it safe? Is it pythonic?
class Example:
def __init__(self, parameter):
if parameter == 0:
# trivial case, the result is always zero
self.calc = lambda x: 0.0 # <== replacing a method
self._parameter = parameter
def calc(self, x):
# ... long calculation of result ...
return result
(If there is any difference between Python2 and Python3, I'm using Python3 only.)

This is very confusing. If someone else reads it, they won't understand what is going on. Just put a if statement at the beginning of your method.
def calc(self, x):
if self.parameter == 0:
return 0
# ... long calculation of result ...
return result
Also if you change self.parameter after it was initialized with 0, your function wouldn't work anymore.

You'll have a problem should parameter ever changes, so I don't consider it good practice.
Instead, I think you should do this:
class Example:
def __init__(self, parameter):
self._parameter = parameter
def calc(self, x):
if not self._parameter:
return 0.0
# ... long calculation of result ...
return result

I decided to post a summary of several comments and answers. Please do not vote for this summary, but give +1 to the original authors instead.
the approach is safe except for special __methods__
the approach is deemed unpythonic, undesirable, or unnecessary etc.
the parameter determining the function to use must be constant. If it is not the case, this approach makes no sense at all.
from several suggestions I prefer the code below for general cases and the obvious if cond: return 0.0 for simple cases:
class Example:
def __init__(self, parameter):
if parameter == 0:
self.calc = self._calc_trivial
else:
# ... pre-compute data if necessary ...
self.calc = self._calc_regular
self._parameter = parameter
def _calc_regular(self, x):
# ... long calculation of result ...
return result
#staticmethod
def _calc_trivial(x):
return 0.0

Related

python decorator doesn't take the arguments of the function

I'm learning python and I'm stuck at decorators
the way I understand it is that decorators add functionality to a function
I made a simple function that checks if a number is even
and then a decorator that adds taking the absolute value to it
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
func(num)
return is_even_new()
#decorate
def is_even(x):
if x%2 == 0:
return True
else:
return False
is_even(8)
but it I keep getting a TypeError: abs() takes exactly one argument (0 given)
is there is something wrong in the code or is my understanding of decorators is false?
When you decorate the function, you should return the decorated function itself:
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
func(num)
return is_even_new
rather than calling the decorated function and returning that:
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
func(num)
return is_even_new()
(notice the extra parentheses). In this last example, when you decorate a function with #decorate, the decorator defines this inner function is_even_new and then, instead of returning the latter, it tries to call it with no arguments: is_even_new(). This is also why you're getting the TypeError: abs expects (exactly) one argument, but you've given it none.
Remember that functions in Python are objects like anything else ("first-class citizens") and so you can reference them directly by their name.
Also, as a recommendation, if you know that the functions decorated with #decorate will only ever take exactly one argument (like is_even), don't use variable arguments and keyword arguments, just define the decorate function to take exactly one parameter as well:
import functools
def decorate(func):
#functools.wraps(func)
def decorated(x):
func(abs(x))
return decorated
This will make the error messages more helpful (instead of raising at the abs call,
Or, if you only want to apply abs to, say, the first argument:
def decorate(func):
#functools.wraps(func)
def decorated(x, *args, **kwargs):
func(abs(x), *args, **kwargs)
return decorated
You'll also have noticed the #functools.wraps. This is a very useful standard library utility for defining wrapper functions, such as those you return from a decorator. It sets special attributes like __name__, __module__, etc. on the wrapper function so that it essentially looks like the wrapped function.
in your code you need to return the value from the decorator in correct way and also change the input data to the decorator by keeping the limitation of other inside function in mind ie (abs take 1 positonal argument not many) and you cant directly call the return function from outside as full without provideing value as return is_new_even(), you need to pass the argument there also if want to do that way
def decorate(func):
def is_even_new(val):
num = abs(val)
return func(num)
return is_even_new
#decorate
def is_even(x):
if x%2 == 0:
return True
else:
return False
is_even(8)
I am not sure what your decorated function is trying to do as modulo is working on negative integers as well, but you basically have two problems:
like #Anakhand said you should return a function and not the function return value.
your is_even_new does not return nothing
so I guess this is the correct implemntation:
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
return func(num)
return is_even_new
#decorate
def is_even(x):
if x%2 == 0:
return True
else:
return False
print(is_even(-8)) # --> True

How to create a function that applies a function to the inputs of another function in Python?

I'm looking for a nice functional way to do the following:
def add(x, y):
return x + y
def neg(x):
return -x
def c(x, y):
# Apply neg to inputs for add
_x = neg(x)
_y = neg(y)
return add(_x, _y)
neg_sum = c(2, 2) # -4
It seems related to currying, but all of the examples I can find use functions that only have one input variable. I would like something that looks like this:
def add(x, y):
return x + y
def neg(x):
return -x
c = apply(neg, add)
neg_sum = c(2, 2) # -4
This is a fairly direct way to do it:
def add(x, y):
return x + y
def neg(x):
return -x
def apply(g, f):
# h is a function that returns
# f(g(arg1), g(arg2), ...)
def h(*args):
return f(*map(g, args))
return h
# or this:
# def apply(g, f):
# return lambda *args: f(*map(g, args))
c = apply(neg, add)
neg_sum = c(2, 2) # -4
Note that when you use *myvar as an argument in a function definition, myvar becomes a list of all non-keyword arguments that are received. And if you call a function with *expression as an argument, then all the items in expression are unpacked and sent as separate arguments to the function. I use these two behaviors to make h accept an unknown list of arguments, then apply function g to each one (with map), then pass all of them as arguments to f.
A different approach, depending on how extensible you need this to be, is to create an object which implements your operator methods, which each return the same object, allowing you to chain operators together in arbitrary orders.
If you can cope with it always returning a list, you might be able to make it work.
class mathifier:
def __init__(self,values):
self.values = values
def neg(self):
self.values = [-value for value in self.values]
return self
def add(self):
self.values = [sum(self.values)]
return self
print (mathifier([2,3]).neg().add().values)
And you can still get your named function for any set of chained functions:
neg_add = lambda x : mathifier(x).neg().add()
print(neg_add([2,3]).values)
From Matthias Fripp's answer, I asked myself : I'd like to compose add and neg both ways : add_neg(*args) and neg_add(*args). This requires hacking Matthias suggestion a bit. The idea is to get some hint on the arity (number of args) of the functions to compose. This information is obtained with a bit of introspection, thanks to inspect module. With this in mind, we adapt the way args are passed through the chain of funcs. The main assumption here is that we deal with real functions, in the mathematical sense, i.e. functions returning ONE float, and taking at least one argument.
from functools import reduce
from inspect import getfullargspec
def arity_one(func):
spec = getfullargspec(func)
return len(spec[0])==1 and spec[1] is None
def add(*args):
return reduce(lambda x,y:x+y, args, 0)
def neg(x):
return -x
def compose(fun1,fun2):
def comp(*args):
if arity_one(fun2): return fun1(*(map( fun2, args)))
else: return fun1(fun2(*args))
return comp
neg_add = compose(neg, add)
add_neg = compose(add, neg)
print(f"-2+(-3) = {add_neg(2, 3)}")
print(f"-(2+3) = {neg_add(2, 3)}")
The solution is still very adhoc...

Conditional performance in python closures

In the code example below, I have two higher level functions, factory1 and factory2, that produce a function with identical behavior. The first factory, factory1, avoids having to explicitly define two different functions by letting the returned function change behavior based on a boolean from the factory. The usefulness of this is not as obvious in this example, but if the function to be produced were more complex, it would be detrimental to both readability and and maintainability to explicitly write out two almost identical copies of the function, like is done in factory2.
However, the factory2 implementation is faster, as can be seen by the timing results.
Is there a way to achieve the performance of factory2 without explicitly defining two alternative functions?
def factory1(condition):
def fn():
if condition:
return "foo"
else:
return "bar"
return fn
def factory2(condition):
def foo_fn():
return "foo"
def bar_fn():
return "bar"
if condition:
return foo_fn
else:
return bar_fn
def test1():
fn = factory1(True)
for _ in range(1000):
fn()
def test2():
fn = factory2(True)
for _ in range(1000):
fn()
if __name__ == '__main__':
import timeit
print(timeit.timeit("test1()", setup="from __main__ import test1"))
# >>> 62.458039999
print(timeit.timeit("test2()", setup="from __main__ import test2"))
# >>> 49.203676939
EDIT: Some more context
The reason I am asking is that I am trying to produce a function that looks something like this:
def function(data):
data = some_transform(data)
if condition:
# condition should be considered invariant at time of definition
data = transform1(data)
else:
data = transform2(data)
data = yet_another_transform(data)
return data
Depending on what you mean by "explicitly defining two functions", note that you don't have to execute a def statement until after you check the condition:
def factory3(condition):
if condition:
def fn():
return "foo"
else:
def fn():
return "bar"
return fn
One might object that this still has to compile two code objects before determining which one gets used to define the function at run-time. In the case, you might fallback on using exec on a dynamically constructed string. NOTE This needs to be done carefully for anything other than the static example I will show here. See the old definition for namedtuple for a good(?) example.
def factory4(condition):
code = """def fn():\n return "{}"\n""".format("foo" if condition else "bar")
exec(code)
return fn
A safer alternative might be to use a closure:
def factory5(condition):
def make_fun(val):
def _():
return val
return _
if condition:
return make_fun("foo")
else:
return make_fun("bar")
make_fun can be define outside of factory5 as well, as it doesn't rely on condition at all.
Based on your edit, I think you are just looking to implement dependency injection. Don't put an if statement inside your function; pass transform1 or transform2 as an argument:
def function(transform):
def _(data):
data = some_transform(data)
data = transform(data)
data = yet_another_transform(data)
return data
return _
if condition:
thing = function(transform1)
else:
thing = function(transform2)

Turning my function into a class

I have a function that works exactly how I want it to, but for my course work, I have to turn this function into a class that:
Must have a function called solveIt,
returns the following two values:
a boolean that is True if you've solved this knapsack problem, and
the knapsack object with the correct values in it.
The class must have a __str__() function that returns a string like this. The first line is the size, and the second is a comma-separated list of the elements:
10
4,1,9,2,0,4,4,4,3,7
I dont understand classes that well, so any help will be appreciated. Here is the function I have right now:
from itertools import combinations
def com_subset_sum(seq, target):
if target == 0 or target in seq:
print(target)
return True
for r in range(len(seq),1,-1):
for subset in combinations(seq, r):
if sum(subset) == target:
print(subset)
return True
return False
print(com_subset_sum([4,1,9,2,0,4,4,4,3,7],10))
One obvious way to transform a function to a class is to turn the function parameters (or some of them) into object attributes. For example:
class Knapsack(object):
def __init__(self, seq, target):
self.seq = seq
self.target = target
self.solution = None
def solveIt(self):
if self.target == 0 or self.target in self.seq:
self.solution = (target,)
return True, self.solution
for r in range(len(self.seq),1,-1):
for subset in combinations(self.seq, r):
if sum(subset) == self.target:
self.solution = subset
return True, self.solution
return False, ()
Now you can do this:
>>> knapsack = Knapsack([4,1,9,2,0,4,4,4,3,7],10)
>>> print(knapsack.solveIt())
(True, (4, 1, 2, 0, 3))
And then, adding a __str__ method is simple:
def __str__(self):
if self.solution is None:
self.solveIt()
return '{}\n{}'.format(len(self.seq),
','.join(map(str, self.solution)))
The reason I added that self.solution is so that calling __str__ over and over won't keep calculating the results over and over. You could just as easily drop that member and write this:
def __str__(self):
solved, solution = self.solveIt()
return '{}\n{}'.format(len(self.seq),
','.join(map(str, solution)))
Either way, I'm not sure how this is better than the function. (In fact, it's strictly worse: with the function, you can always use functools.partial to bind in just the sequence, or both the sequence and the target, or of course bind in neither, whereas with the class, you always have to bind in both.)
Maybe your professor has given you some kind of hints on how you'd want to use this object that might help? Or maybe your professor is just an idiot who doesn't know how to come up with a good motivating assignment for teaching you about classes…

Using super to make a pipeline?

I was thinking about how to use super to make a pipeline in python. I have a series of transformations I must do to a stream, and I thought that a good way to do it was something in the lines of:
class MyBase(object):
def transformData(self, x):
return x
class FirstStage(MyBase):
def transformData(self, x):
y = super(FirstStage, self).transformData(x)
return self.__transformation(y)
def __transformation(self, x):
return x * x
class SecondStage(FirstStage):
def transformData(self, x):
y = super(SecondStage, self).transformData(x)
return self.__transformation(y)
def __transformation(self, x):
return x + 1
It works as I intended, but there's a potential repetition. If I have N stages, I'll have N identical transformData methods where the only thing I change is the name of the current class.
Is there a way to remove this boilerplate? I tried a few things but the results only proved to me that I hadn't understood perfectly how super worked.
What I wanted was to define only the method __transformation and naturally inherit a transformData method that would go up in MRO, call that class' transformData method and then call the current class' __transformation on the result. Is it possible or do I have to define a new identical transformData for each child class?
I agree that this is a poor way of implementing a pipeline. That can be done with much simpler (and clearer) schemes. I thought of this as the least modification I could do on a existing model to get a pipeline out of the existing classes without modifying the code too much. I agree this is not the best way to do it. It would be a trick, and tricks should be avoided. Also I thought of it as a way of better understanding how super works.
Buuuut. Out of curiosity... is it possible to do it in the above scheme without the transformData repetition? This is a genuine doubt. Is there a trick to inherit transformData in a way that the super call in it is changed to be called on the current class?
It would be a tremendously unclear, unreadable, smart-ass trickery. I know. But is it possible?
I don't think using inheritance for a pipeline is the right way to go.
Instead, consider something like this -- here with "simple" examples and a parametrized one (a class using the __call__ magic method, but returning a closured function would do too, or even "JITing" one by way of eval).
def two_power(x):
return x * x
def add_one(x):
return x + 1
class CustomTransform(object):
def __init__(self, multiplier):
self.multiplier = multiplier
def __call__(self, value):
return value * self.multiplier
def transform(data, pipeline):
for datum in data:
for transform in pipeline:
datum = transform(datum)
yield datum
pipe = (two_power, two_power, add_one, CustomTransform(1.25))
print list(transform([1, 2, 4, 8], pipe))
would output
[2.5, 21.25, 321.25, 5121.25]
The problem is that using inheritance here is rather weird in terms of OOP. And do you really need to define the whole chain of transformations when defining classes?
But it's better to forget OOP here, the task is not for OOP. Just define functions for transformations:
def get_pipeline(*functions):
def pipeline(x):
for f in functions:
x = f(x)
return x
return pipeline
p = get_pipeline(lambda x: x * 2, lambda x: x + 1)
print p(5)
An even shorter version is here:
def get_pipeline(*fs):
return lambda v: reduce(lambda x, f: f(x), fs, v)
p = get_pipeline(lambda x: x * 2, lambda x: x + 1)
print p(5)
And here is an OOP solution. It is rather clumsy if compared to the previous one:
class Transform(object):
def __init__(self, prev=None):
self.prev_transform = prev
def transformation(self, x):
raise Exception("Not implemented")
def transformData(self, x):
if self.prev_transform:
x = self.prev_transform.transformData(x)
return self.transformation(x)
class TransformAdd1(Transform):
def transformation(self, x):
return x + 1
class TransformMul2(Transform):
def transformation(self, x):
return x * 2
t = TransformAdd1(TransformMul2())
print t.transformData(1) # 1 * 2 + 1

Categories