So I have a class with a couple of (normal) methods. Depending on a value I want to call different methods. This behavior of choosing methods is static (same for all instantiation of the classes. How would you recommend doing this?
Will the answer change on best way to achieve this if the state of an instantiation is constant and never changes after initialization?
Example:
PLUS = 0
MINUS = 1
OPERATIONS = [PLUS, MINUS]
class Generator(object):
operations = {
PLUS: self.plus, # Not possible
MINUS: self.minus,
}
def __init__(self, state):
self._state = state
def plus(self, a, b):
# Depends on state
return a + b
def minus(self, a, b):
return a - b if self._state else b - a
def generate(self):
a, b = give_me_numbers()
for op in OPERATIONS:
print self.operations[op](a, b)
PLUS = 0
MINUS = 1
OPERATIONS = [PLUS, MINUS]
class Generator:
operations = {}
def __init__(self, state):
self._state = state
#classmethod
def init_operations(cls):
cls.operations = {
PLUS: cls.plus,
MINUS: cls.minus
}
def plus(self, a, b):
# Depends on state
return a + b
def minus(self, a, b):
return a - b if self._state else b - a
def generate(self):
a, b = 5, 10
for op in self.operations:
print( self.operations[op](self, a, b) )
gen = Generator(1)
gen.init_operations()
gen.generate()
In order for operations to store functions of a class definition it can't be done at the top of a class like you have done. This is because the parser won't find the functions you're referring to because it hasn't parsed them yet. So instead I've added a 'static' init_operations().
Note these operations are stored as unbound methods (since it's called from within a static); therefore when calling these functions it is necessary to include the self variable as the 1st argument.
One option is to turn operations into a method:
def operations(self, op):
dictMethods = {
"PLUS" : self.plus
"MINUS" : self.minus
}
return dictMethods[op]
Then call it like:
self.operations(op)(a, b)
Here's a slight modification to Richard's code that automatically calls the init_operations method the first time you instantiate a Generator.
class Generator:
def __init__(self, state):
self._state = state
if not hasattr(self, 'operations'):
self.init_operations()
#classmethod
def init_operations(cls):
cls.operations = {
PLUS: cls.plus,
MINUS: cls.minus,
}
def plus(self, a, b):
# Depends on state
return a + b
def minus(self, a, b):
return a - b if self._state else b - a
def generate(self):
a, b = give_me_numbers()
for op in self.operations:
print self.operations[op](self, a, b)
Here's an alternative that makes operations a plain instance attribute. This wastes a little bit of space, but it means you don't need to explicitly pass self when you call an operation.
class Generator(object):
def __init__(self, state=False):
self._state = state
self.operations = {
PLUS: self.plus,
MINUS: self.minus
}
def plus(self, a, b):
# Depends on state
return a + b
def minus(self, a, b):
return a - b if self._state else b - a
def generate(self):
a, b = give_me_numbers()
for op in OPERATIONS:
print self.operations[op](a, b)
And finally, this variation uses the method names to identify the operations instead of integers.
#!/usr/bin/env python
from random import seed, randint
def give_me_numbers():
a, b = randint(1, 99), randint(1, 99)
print 'a=%d, b=%d' % (a, b)
return a, b
OPERATIONS = ('plus', 'minus')
class Generator(object):
def __init__(self, state=False):
self._state = state
def plus(self, a, b):
# Depends on state
return a + b
def minus(self, a, b):
return a - b if self._state else b - a
def operations(self, op):
return getattr(self, op)
def generate(self):
a, b = give_me_numbers()
for op in OPERATIONS:
#print getattr(self, op)(a, b)
print self.operations(op)(a, b)
seed(42)
g1 = Generator(False)
g1.generate()
g2 = Generator(True)
g2.generate()
output
a=64, b=3
67
-61
a=28, b=23
51
5
You don't really need the operations method here - I just left it in to stay (relatively) consistent with the OP code. Instead, you can just call getattr(self, op)(a, b) directly. OTOH, it is cleaner to supply the operations method if you want to call it from outside the class.
What you want to do actually works (there is no need for a more complicated solution), but you have to (1) write the dictionary definition of operations properly (with : instead of =) and (2) put its definition when it can be understood (after the methods that it refers to are defined):
PLUS = 0
MINUS = 1
OPERATIONS = [PLUS, MINUS]
class Generator(object):
def __init__(self, state):
self._state = state
def plus(self, a, b):
# Depends on state
return a + b
def minus(self, a, b):
return a - b if self._state else b - a
def generate(self):
a, b = give_me_numbers()
for op in OPERATIONS:
print operations[op](a, b)
operations = { # plus and minus are defined, at this point
PLUS: plus,
MINUS: minus
}
Side notes:
Note the Generator(object) syntax (not Generator())—or simply Generator, in Python 3.
You might want check out the enum module, which handles the constants PLUS, MINUS and OPERATIONS that you define in a clean and convenient way.
PS: As PM 2Ring noted, using the values from operations can be done through self.operations[op](self, a, b). I would personally do Generator.operations[op](self, a, b), since operations is not specific to any instance and is instead a dictionary associated with the Generator class.
Related
How can I pass in arguments (a, b, c) to a function for a quadratic formula without having to redefine them in the function? I know I could use self.a instead of just a inside the formula (same thing for b and c) but how do I pass in the argument self.a as a, self.b as b, and self.c into the function?
class Calc:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def quadraticformula(self):
c = self.c
b = self.b
a = self.a
neg = ((b*-1)-(sqrt((b**2)-4*a*c)))/(2*a)
pos = ((b*-1)+(sqrt((b**2)-(4*a*c))))/(2*a)
return (pos,neg)
Instead of using a class with a constructor function just use a normal function in general
def calc(a, b, c):
neg = ((b*-1)-(sqrt(b**2 - 4*a*c)))/(2*a)
pos = ((b*-1)+(sqrt(b**2 - 4*a*c)))/(2*a)
return pos, neg
Then call the function:
>>> calc(1, 2, -3)
(1.0, -3.0)
You don't have to redefine anything. The __init__ method allows for all other methods of the class to be able to access that variable. So once you actually define a variable you passed to the class (you referenced it as a function, which its not) in the __init__ method all you have to do it just reference it with whatever operation you need.
# within you quadraticformula method
...
neg = ((self.b*-1)-(sqrt(self.b**2 - 4*self.a*self.c)))/(2*self.a)
pos = ((self.b*-1)+(sqrt(self.b**2 - 4*self.a*self.c)))/(2*self.a)
return pos, neg
When passing attributes to the class you have create an instance of it like so:
a = # something
b = # something
c = # something
cl = Calc(a, b, c)
cl.quadraticformula() # call the method (a function with a method) of the function here
# You can call this method in the __init__ method if you want to
# execute as soon as you call the class instead of using the instance
# to reference it
class Calc:
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
self.quadraticformula
class classname():
def func(self,a,b):
self.c = a+b
self.d = a-b
self.e = a*b
return self
cn = classname()
This way i can access cn.c, cn.d and cn.e can i use something else other then self to return it and it will be a structure. I know its possible in matlab where you can define structure in a function. Something what i expect should look like this:
class classname():
def func(self,newself,a,b):
self.c = a+b
self.d = a-b
newself.e = a*b
return self, newself
cn = classname()
I know this is not a valid code but just an idea what i want from code.
I think what you want is this:
class classname:
def __init__(self, a, b):
self.c = a+b
self.d = a-b
self.e = a*b
cn = classname(12, 34) # Just random values for 'a' and 'b'. Use whatever you like!
print(cn.c)
>>> 46
print(cn.d)
>>> -22
print(cn.e)
>>> 408
The __init__ function is automatically called when the object is created. Self will always refer to the object, so adding attributes to it will add it to the object, so you don't need to return anything.
I'm quite new using Python and can't find the answer to this.
Let's souppose I have a code like:
class numbers():
def __init__(self,a,b):
self._a = a
self._b = b
def add(self):
self._suma = self._a + self._b
After that, I create a lot of instances of numbers:
obj1 = numbers(1,2)
obj2 = numbers(7,16)
...
Then, I want to call the add method in all the objects of the class numbers in a simple and clean way.
Notice that if I instantiated numbers class 1000 times I don't want to write 1000 times this
objX.add()
I looked for an answer in the web and I found that, in other lenguages, they put all the names of the objects in a string, and then iterates on it calling the method.
The problem is I don´t know how to do that in python, nor if it's the best way to solve this problem.
Thank you
create a list of objects.
objs = []
add elements to this list
obj1 = numbers(1,2) # say these are your objects
obj2 = numbers(7,16)
...
objs.append(obj1) # add them to the list
call the add method for each element in the list
for obj in objs:
obj.add() # call the add method.
Well you'll need to have some way to find all the numbers objects. For clarity, I'll adjust your code naming conventions slightly so they're more standard (per PEP9).
class Number(object):
def __init__(self, a, b):
self._a = a
self._b = b
def add(self):
self.sum = self._a + self._b
return self.sum # not really sure where we're using it, so here?
a = Number(1, 2)
b = Number(2, 3)
c = Number(3, 4)
Now we have three objects, a, b, and c, that are all Number objects. There's two ways to get a list of them, and one is really bad. We'll go over that one first.
number_objs = [obj for obj in globals() if isinstance(obj, Number)]
for number in number_objs:
number.add()
This queries the all the objects currently in the namespace to see if they're Numbers. The problem with doing it this way is that you lose encapsulation. You probably don't want to rely on your functions finding a number object by calling globals(). Instead, let's give Number an encompassing object!
class NumberList(list):
# this is literally just a list, but we want to add one method:
def make_number(a, b):
number = Number(a, b)
self.append(number)
all_numbers = NumberList()
a = all_numbers.make_number(1, 2)
b = all_numbers.make_number(2, 3)
c = all_numbers.make_number(3, 4)
for number in all_numbers:
number.add()
Alternatively you can give Number a classmethod that works as an alternate constructor, but also adds it to a list. This is probably the cleanest way to handle it.
class Number(object):
def __init__(self, a, b):
self._a = a
self._b = b
def add(self):
self.sum = self._a + self._b
return self.sum
#classmethod
def track(cls, a, b, container):
n = cls(a, b)
container.append(n)
return n
all_numbers = []
a = Number.track(1, 2, all_numbers)
b = Number.track(2, 3, all_numbers)
c = Number.track(3, 4, all_numbers)
for number in all_numbers:
number.add()
You need to append each obj to a list. To automate that, simply create an empty list and write the code inside the init. This will run automatically every time a new object is created
class numbers():
def __init__(self,a,b):
self.a = a
self.b = b
listObjs.append(self)
def Add(self):
return self.a + self.b
listObjs = []
ob1 = numbers(4,5)
ob2 = numbers(4324,5)
ob3 = numbers(1,25)
ob4 = numbers(2,5324)
ob5 = numbers(21,5)
ob6 = numbers(4213,54)
Then simply make a loop and print the obj.Add(). This will run for each obj in the list.
for obj in listObjs:
print(obj.Add())
Output:
9
4329
26
5326
26
4267
A class:
class Spam:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
An instantiation:
from Spam import *
c = Spam(1,5,4)
In this case, to perform a check on the input values of 'a' or 'b' or 'c' I've the decorators #property, #a.setter, #b.setter, #c.setter but...what if I need to check this variables but they are not directly copied into 'private' class variables?
I mean
class Egg()
def __init__(self, a, b, c):
self.var = (a + b)*c
Say I need to check a < c and c > b, what is the best way to perform checks on variables 'a', 'b', 'c' inside the class Egg and bound their value to some standards if checks are not consistent? Is there any particular decorator? (I need to keep code "clean and easy to uderstand" outside the class...this is why I am not performing checks before instantiation).
Have you considered immutability?
from collections import namedtuple
class Egg(namedtuple('Egg', 'a b c'))
def __new__(cls, a, b, c):
assert a < c and c > b
return super(Egg, cls).__new__(a, b, c)
#property
def var(self):
return (a + b)*c
I'm writting a Fraction class and I am trying to use gcd(a,b) in the initialization of a Fraction object. However, when I was trying to do this it would not work WITHOUT the Fraction part of Fraction.gcd(a,b). I used #staticmethod here, but it does absolutely nothing, i.e. my code works the same without it.
Is there anyway I can call gcd without putting Fraction. in front of it? In Java I would normally create a static method and then just call it. I could very easily put the GCD code inside of the init, but I am trying to learn here!
I am missing a lot here. Can anyone explain: static methods, helper methods in a class and pretty much how I can use various methods inside of a class?
class Fraction(object):
def __init__(self, a, b):
if Fraction.gcd(a, b) > 1:
d = Fraction.gcd(a, b)
self.num = a/d
self.denom = b/d
else:
self.num = a
self.denom = b
#staticmethod
def gcd(a,b):
if a > b: a,b = b,a
while True:
if b % a == 0: return a
a, b = b%a, a
def __repr__(self):
return str(self.num) + "/" + str(self.denom)
Don't forget, in Python not everything needs to be in a class. There's nothing about gcd that makes it better-suited to being a class method than a standalone function: so take it out of the class. Now you can just call gcd(a, b).
Think of methods in a class just like any other class attribute -- reference them on self:
def __init__(self, a, b):
if( self.gcd(a,b) > 1):
d = self.gcd(a,b)
It doesn't matter whether it's an instance method, class method, or static method.
While you certainly can use a staticmethod if you want to keep the code associated with the class, it's usual in Python to use a module-level function, in which case you can call it as gcd:
def gcd(a,b):
if a > b: a,b = b,a
while True:
if b % a == 0: return a
a, b = b%a, a
class Fraction(object):
def __init__(self, a, b):
if( gcd(a,b) > 1):
d = gcd(a,b)
If you have a big method within your class that requires many calls to a static method you can define a local function object and assign the method to it so you can call this function instead.
For Static Method gdc:
class Fraction(object):
def __init__(self, a, b):
gcd = Fraction.gcd
if( gcd(a,b) > 1):
d = gcd(a,b)
self.num = a/d
self.denom = b/d
else:
self.num = a
self.denom = b
#staticmethod
def gcd(a,b):
if a > b: a,b = b,a
while True:
if b % a == 0: return a
a, b = b%a, a
def __repr__(self):
return str(self.num) + "/" + str(self.denom)
For Instance Method gdc:
class Fraction(object):
def __init__(self, a, b):
gcd = self.gcd
if( gcd(a,b) > 1):
d = gcd(a,b)
self.num = a/d
self.denom = b/d
else:
self.num = a
self.denom = b
def gcd(self,a,b):
if a > b: a,b = b,a
while True:
if b % a == 0: return a
a, b = b%a, a
def __repr__(self):
return str(self.num) + "/" + str(self.denom)
So
gcd = Fraction.gcd
and
gcd = self.gcd
will allow you to call (without Fraction at the beginning as per your request :))
gcd(a,b)
Also, if you want some basic examples of python classes and instance/static methods
have a look at some of my blog posts, specially the one called "Factorial and Fibonacci in Jython":
http://carlosqt.blogspot.com/search/label/Jython
I think you are referring to Java's "import static" feature.
Just to clarify: as Java enforces object orientation, it cannot have "modules" like other languages. So using import static Math.*; for example will make all static methods on Math available to be called without the class name.
In Python you can just add this function outside a class and call it.
That's how static methods work. You call them via Classname.methodname() (or via instance.methodname(), but self won't be available inside the method).
What you want is a regular function on the module level. Define it outside the class and do not decorate it.