Should I use a class? (Python) - python

I'm trying to write a small Python module which contain some mathematical functions. For example, it might contain a function like:
def quad(x, a, b, c):
return a*x**2 + b*x + c
As you may notice it contains several parameters (viz. a, b, c) apart from the variable x. Now if I were to put this in a file and simply import it, the end user would have to always call the function with the parameters in addition to the variable. Because of this I was thinking of creating a class such as this:
class quad:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def eq(x):
return self.a*x**2 + self.b*x + self.c
Thus allowing the end user to use it as:
q = quad(p, q, r)
eq = q.eq
Is this the right way of doing things? I am terribly sorry about the title of the question, as I couldn't think of a better one!

That seems like a perfectly reasonable use of a class. Essentially you should consider using a class when your program involves things that can be modelled as objects with state. Here, the "state" of your polynomial is just the coefficients a, b, and c.
You can also use Python's __call__ special method to allow you to treat the class as though it were a function itself:
class quad:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def __call__(x):
return self.a * x**2 + self.b * x + self.c
q = quad(p, q, r)
q(x)
Yet another way of doing it, which could be slightly cleaner, would be simply to return a function with those coefficients baked into it. This is essentially an example of currying, as Tichodrama mentions:
def quad(a, b, c):
def __quad(x):
return a * x**2 + b * x + c
return __quad
Or using lambda syntax:
def quad(a, b, c):
return lambda x: a * x**2 + b * x + c
These could then be used like so:
q = quad(p, q, r)
q(x)

It looks like you are searching for something like currying.
Perhaps this question can help you.

Related

Add a function using another function's parameter declaration

I am trying to add some customized logic outside of an existing function. Here are the example:
# existing function that I cannot change
def sum(a, b, c, d):
return a+b+c+d
# the function I want to build
def sumMultiply(a, b, c, d, multiplier):
return multiplier * sum(a, b, c, d)
This is a stupid example, but essentially I want to build a new function that takes all the parameter of the existing function and add a few new arguments.
The above solution is problematic when the existing function changes its definition. For example:
# in some updates the original function dropped one parameter
def sum(a, b, c):
return a+b+c
# the new function will give an error since there is no parameter "d"
def sumMultiply(a, b, c, d, multiplier):
return multiplier * sum(a, b, c, d) # error
How can I specify the new function so that I do not need to worry about changing the new function definition when the existing function definition changes?
One way would be to use arbitrary positional or keyword arguments:
def sumMultiply(multiplier, *numbers):
return multiplier * sum(*numbers)
def sumMultiply(multiplier, *args, **kwargs):
return multiplier * sum(*args, **kwargs)
However, if you see yourself passing around the same set of data around, consider making a parameter object. In your case, it can simply be a list:
def sum(numbers):
...
def sumMultiply(multiplier, numbers):
return multiplier * sum(numbers)
There are some additional downsides to using arbitrary arguments:
the arguments are implicit: you might need to dig through several layers to see what you actually need to provide
they don't play well with type annotations and other static analysers (e.g. PyCharm's refactorings)
I would create a decorator function
def create_fun_multiplier(fun, multiplier=1):
def multiplier_fun(*args):
return multiplier * fun(*args)
return multiplier_fun
def my_sum(a, b, c):
return a + b + c
sumMultiply = create_fun_multiplier(my_sum, multiplier=2)
print(sumMultiply(3, 4, 7))
I would look at using keyword args for this problem.
eg.
def sum(a, b, c):
return a + b + c
def sumMultiply(*args, multiplier=1):
return multiplier * sum(*args)

How would I run a function given its name?

I have a large number of blending functions:
mix(a, b)
add(a, b)
sub(a, b)
xor(a, b)
...
These functions all take the same inputs and provide different outputs, all of the same type.
However, I do not know which function must be run until runtime.
How would I go about implementing this behavior?
Example code:
def add(a, b):
return a + b
def mix(a, b):
return a * b
# Required blend -> decided by other code
blend_name = "add"
a = input("Some input")
b = input("Some other input")
result = run(add, a, b) # I need a run function
I have looked online, but most searches lead to either running functions from the console, or how to define a function.
I'm not really big fan of using dictionary in this case so here is my approach using getattr. although technically its almost the same thing and principle is also almost the same, code looks cleaner for me at least
class operators():
def add(self, a, b):
return (a + b)
def mix(self, a, b):
return(a * b)
# Required blend -> decided by other code
blend_name = "add"
a = input("Some input")
b = input("Some other input")
method = getattr(operators, blend_name)
result = method(operators, a, b)
print(result) #prints 12 for input 1 and 2 for obvious reasons
EDIT
this is edited code without getattr and it looks way cleaner. so you can make this class the module and import as needed, also adding new operators are easy peasy, without caring to add an operator in two places (in the case of using dictionary to store functions as a key/value)
class operators():
def add(self, a, b):
return (a + b)
def mix(self, a, b):
return(a * b)
def calculate(self, blend_name, a, b):
return(operators.__dict__[blend_name](self, a, b))
# Required blend -> decided by other code
oper = operators()
blend_name = "add"
a = input("Some input")
b = input("Some other input")
result = oper.calculate(blend_name, a, b)
print(result)
You can create a dictionary that maps the function names to their function objects and use that to call them. For example:
functions = {"add": add, "sub": sub} # and so on
func = functions[blend_name]
result = func(a, b)
Or, a little more compact, but perhaps less readable:
result = functions[blend_name](a, b)
You could use the globals() dictionary for the module.
result = globals()[blend_name](a, b)
It would be prudent to add some validation for the values of blend_name

How to write a function with a variable to be defined on later code [duplicate]

This question already has answers here:
How do I write a function that returns another function?
(5 answers)
Closed 2 years ago.
I am making a function on python3 that solves ax^2+bx+c so a quadratic equation
My code looks like this:
def quadratic(a, b, c):
return a*x**2 + b*x + c
But it wont let me do this because x is undefined. I want to take the argument x on a test code
that looks like this:
def testQuadratic(a, b, c, x):
try:
return quadratic(a, b, c)(x)
except TypeError:
return None
Can anyone tell me how I can fix this?
Thank you!!
You can make use of the fact that Python supports first-class functions, that can be passed into and returned from other functions.
def make_quadratic(a, b, c):
def f(x):
return a*(x**2) + b*x + c
return f
# You would call the returned function
my_quadratic = make_quadratic(a, b, c)
# You can then call my_quadratic(x) as you would elsewhere
Your quadratic function should... return a function!
def quadratic(a, b, c):
def calculate_quadratic(x):
return a*x**2 + b*x + c
return calculate_quadratic
It's unclear whether you intend 'solve' to mean
find a root of the quadratic equation, or
produce an output for a given value of x
Since you're taking x as an input argument, I'll assume the second option (snatchysquid gave an answer for the first option):
def quadratic(a, b, c):
return a*x**2 + b*x + c
def testQuadratic(a, b, c, x):
try:
return quadratic(a, b, c, x)
except TypeError:
return None
of course unless you need def quadratic for some other reason, then it would be simpler to use:
def testQuadratic(a, b, c, x):
try:
return a*x**2 + b*x + c
except TypeError:
return None
You still might want to handle other errors besides TypeError.
You can read about quadratic equations here and in wikipedia.
There is a formula for solving quadratic equations so you can use this code:
import math
def quadratic_equation(a, b, c):
"""
:params a,b,c: the coefficients of the equation ax^2+bx+c=0
:return: a tuple (of len 2) with the solutions to the equation
:rtype: tuple
"""
discriminant = b ** 2 - (4 * a * c)
if discriminant < 0:
return None, None
if discriminant == 0:
return -b / (2 * a), None
return (-b + math.sqrt(discriminant)) / (2 * a), (-b - math.sqrt(discriminant)) / (2 * a)
The reason your code doesn't work is that python does not understand "missing variables", and thinks that x is some defined variable, for example x = 2, but you use the math notation which python does not understand.

From Haskell to functional Python

I want to translate some Haskell code into Python.
The Haskell classes/instances look like:
{-# LANGUAGE MultiParamTypeClasses #-}
module MyModule where
class Example a b where
doSomething :: a -> b -> Bool
doSomethingElse :: a -> b -> Int
instance Example Int Int where
doSomething a b = (a + b * 2) > 5
doSomethingElse a b = a - b * 4
Is there a way in Python to approximate the Haskell class/instance construct?
What is the least offensive way to translate this into Python?
This doesn't really have an analogue in Python, but you can fake it:
def int_int_doSomething(a, b):
return (a + b * 2) > 5
def int_int_doSomethingElse(a, b):
return a - b * 4
Example = {}
Example[(int, int)] = (int_int_doSomething, int_int_doSomethingElse)
def doSomething(a, b):
types = type(a), type(b)
return Example[types][0](a, b)
def doSomethingElse(a, b):
types = type(a), type(b)
return Example[types][1](a, b)
All you have to do is add new values to Example for each type combination you want to have. You could even throw in some extra error handling in doSomething and doSomethingElse, or some other methods to make it easier. Another way would be to make an object that keeps track of all of these and lets you add new types to the map in a more managed way, but it's just more bookkeeping on top of what I've already shown.
Keep in mind that this is essentially how Haskell does it, too, except the checks are performed at compile time. Typeclasses are really nothing more than a dictionary lookup on the type to pick the appropriate functions to insert into the computation. Haskell just does this automatically for you at compile time instead of you having to manage it yourself like you do in Python.
To add that bookkeeping, you could do something like the following, keeping it in its own module and then it'll only (by default) export the symbols in __all__. This keeps things looking more like the Haskell version:
class _Example(object):
def __init__(self, doSomething, doSomethingElse):
self.doSomething = doSomething
self.doSomethingElse = doSomethingElse
ExampleStore = {}
def register(type1, type2, instance):
ExampleStore[(type1, type2)] = instance
def doSomething(a, b):
types = type(a), type(b)
return ExampleStore[types].doSomething(a, b)
def doSomethingElse(a, b):
types = type(a), type(b)
return ExampleStore[types].doSomethingElse(a, b)
def Example(type1, type2, doSomething, doSomethingElse):
register(type1, type2, _Example(doSomething, doSomethingElse))
__all__ = [
'doSomethingElse',
'doSomethingElse',
'Example'
]
Then you can make instances like
Example(int, int,
doSomething=lambda a, b: (a + b * 2) > 5,
doSomethingElse=lambda a, b: a - b * 4
)
Which looks almost like Haskell.
You don't have parametric types in Python, as it's dynamically typed. Also the distinction between classes and instances is clear in Python, but as classes are themselves "live objects", the distinction of usage might be a little bit blurred sometimes...
For your case, a classical implementation might go as:
#you don't really need this base class, it's just for documenting purposes
class Example:
def doSomething(self, a, b):
raise "Not Implemented"
def doSomethingElse(self, a, b):
raise "Not Implemented"
class ConcreteClass(Example):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def doSomething(self, a, b):
return (a + b * self.x) > self.y
def doSomethingElse(self, a, b):
return a - b * self.z
instance = ConcreteClass((2, 5, 4)
but I personally dislike that convoluted style, so you might just go with something more lightweight, like:
from collections import namedtuple
Example = namedtuple('Example', 'doSomething doSomethingElse')
instance = Example((lambda a, b: (a + b * 2) > 5),
(lambda a, b: a - b *4 ))
And of course, rely on duck typing and usually "let it crash". The lack of type safety should be made up with extensive unit testing.

Python importing class attributes into method local namespace

I have been wondering for a while if there is easier way to assign class attributes to method local namespace. For example, in dosomething method, I explicitly make references to self.a and self.b:
class test:
def __init__(self):
self.a = 10
self.b = 20
def dosomething(self):
a = self.a
b = self.b
return(a + b)
But sometimes I have a lot of variables (more than 10) and it gets messy to type and look at - I would have bunch of var = self.var statements at the beginning of a method.
Is there any way to do this more compact way? (I know updating local() is not a good idea)
Edit: Ideally, what I want is:
def dosomething(self):
populate_local_namespace('a', 'b')
return(a + b)
Q. Is there any way to do this more compact way?
1. If the variables are read-only, it would be reasonably Pythonic to factor-out a multi-variable accessor method:
class Test:
def __init__(self):
self.a = 10
self.b = 20
self.c = 30
def _read_vars(self):
return self.a, self.b, self.c
def dosomething(self):
a, b, c = self._read_vars()
return a + b * c
def dosomethingelse(self):
a, b, c = self._read_vars()
return a - b * c
If the variables aren't read-only, it is best to stick with self.inst_var = value. That is the normal way to write Python code and is usually what most people expect.
2. Once in a while you will see people abbreviate self with a shorter variable name. It is used when the readability benefits of decluttering outweigh the readability cost of using a non-standard variable name:
def updatesomethings(s):
s.a, s.b, s.c = s.a + s.c, s.b - s.a, s.c * s.b
3. Another way to handle a very large number instance variable is to store them in a mutable container for ease of packing and unpacking:
class Test:
def __init__(self, a, b, c, d, e, f, g, h, i):
self._vars = [a, b, c, d, e, f, g, h, i]
def fancy_stuff(self):
a, b, c, d, e, f, g, h, i = self._vars
a += d * h - g
b -= e * f - c
g = a + b - i
self._vars[:] = a, b, c, d, e, f, g, h, i
4. There is also a dictionary manipulation approach that would work, but it has a code smell that most Pythonistas would avoid:
def updatesomethings(self):
a = 100
b = 200
c = 300
vars(self).update(locals())
del self.self
You can easily solve this problem with a tradeoff, by storing the variables in a dictionary.
data = {}
copy_to_local_variables = ["a", "b", "c", "d"]
for var_name in copy_to_local_variables:
data[var_name] = getattr(self, var_name)
(Though I am unable to understand why you need to copy class attributes to method local namespace)
I found another way:
def dosomething(self):
for key in ['a', 'b']:
exec('{} = self.{}'.format(key, key))
return(a + b)
I don't know if this is dangerous or not. Would be great if someone can comment on this.

Categories