Loop through functions in a class - python

I have several functions inside a class that I applied augmentation to a numpy image array. I would like to know how to loop through all of them and apply those. For example:
Class Augmentation():
def rotation(data):
return rotated_image
def shear(data):
return sheared_image
def elasticity(data):
return enlarged_image
A=Augmentation()
My end result should be stacking all my functions. So for example: my data is (64,64) in shape. So after all my augmentations I should have a final numpy of (12,64,64). I currently tried creating different functions and then used
stack_of_images = np.stack(f1,f2,f3,....,f12)
stack_of_images.shape = (12,64,64)
I am using 12 different functions to augmentate numpy image arrays. I insert 1 image (64,64) and I get 12 images stacked (12,64,64).

You can do this by accessing the attribute dictionary of the type.
You can either get it with vars(Augmentation) or Augmentation.__dict__. Then, just iterate through the dict, and check for functions with callable.
NOTE: querying vars(A) or A.__dict__ (note it's the instance, not the class), will NOT include anything defined in the class, and in this case would be just {}. You don't even have to create an instance in the first place.
NOTE2: It seems like you should tag all methods with the decorator #staticmethod instead. Otherwise calling any method on an instance, like A.shear(), would pass A as data instead, which is most likely not desired.
class foo:
#staticmethod
def bar(data):
...
Example:
methods = []
for attrname,attrvalue in vars(Augmentation).items():
if callable(attrvalue):
methods.append(attrvalue)
print([i.__name__ for i in methods])

Related

Specify helper function that's used by another helper function inside a class

Update to question:
I want to include a helper function in my class that uses another helper function that's only used within one of the methods of the class. Using #staticmethod and self.func_name is what I'd do if I had one staticmethod.. However, if I want to call another staticmethod from a staticmethod and specify that using self.helper_func, I get an 'name 'self' is not defined' error.
To give you some context, the reason I'm doing this is because in my actual use case, I'm working with a list of grouped dataframes. Then within that outer apply statement, I then iterate through sets of specific columns in each grouped dataframe and apply the actual function. So the outer helper function is just an apply over the groups in the grouped dataframes, and it then calls the inner helper that performs manipulations on groups of columns.
import pandas as pd
import numpy as np
class DataManipulation():
def __init__(self, data):
self.data = data
#staticmethod
def helper_func(const):
return const
#staticmethod
def add_constant(var):
res = var+self.helper_func(5)
return res
def manipulate_data(self):
res = self.data.apply(add_constant)
return res
test_df = pd.DataFrame({'a': np.arange(4), 'b': np.arange(4)})
data_manip = DataManipulation(test_df)
data_manip.manipulate_data()
how can static #staticmethod access self
Static method can be called without creating an object or instance.
So what will be self when staticmethod is called before creating any object?
PS. Well that's my opinion, I may be wrong (I am new to python, that's how it was in C / C++ / Java).
Maybe you need to call DataManipulation.helper_func(5) instead of self.helper_func(5).

Why is Python reference equality not working?

I have a class with an array called self.sheets. I have a function, find_sheet which will do a comparison on the titles if a string is passed, or do a reference comparison if a Worksheet is passed. Here's a minimal reproducible example:
class Worksheet:
pass
class Spread:
#property
def sheets(self):
return [Worksheet() for i in range(5)]
def find_sheet(self, sheet):
for ix, obj in enumerate(self.sheets):
print("comparing {} is {}".format(id(obj), id(sheet)))
if obj is sheet:
print("found you")
s = Spread()
s.find_sheet(s.sheets[0])
This outputs comparing 140134415396760 is 140134393512344
I figured out my problem. When I refer to s.sheets, it's calling the property function and generating a new list each time. So when I pass s.sheets[0], it's actually a different object than the first object when iterating through self.sheets.
Lesson here is... be careful when using properties, they behave differently than variables.

apply python class methods on list of instances

I recently moved from Matlab to Python and want to transfer some Matlab code to Python. However an obstacle popped up.
In Matlab you can define a class with its methods and create nd-arrays of instances. The nice thing is that you can apply the class methods to the array of instances as long as the method is written so it can deal with the arrays. Now in Python I found that this is not possible: when applying a class method to a list of instances it will not find the class method. Below an example of how I would write the code:
class testclass():
def __init__(self, data):
self.data = data
def times5(self):
return testclass(self.data * 5)
classlist = [testclass(1), testclass(10), testclass(100)]
times5(classlist)
This will give an error on the times5(classlist) line. Now this is a simple example explaining what I want to do (the final class will have multiple numpy arrays as variables).
What is the best way to get this kind of functionality in Python? The reason I want to do this is because it allows batch operations and they make the class a lot more powerful. The only solution I can think of is to define a second class that has a list of instances of the first class as variables. The batch processing would need to be implemented in the second class then.
thanks!
UPDATE:
In your comment , I notice this sentence,
For example a function that takes the data of the first class in the list and substracts the data of all following classe.
This can be solved by reduce function.
class testclass():
def __init__(self, data):
self.data = data
def times5(self):
return testclass(self.data * 5)
from functools import reduce
classlist = [x.data for x in [testclass(1), testclass(10), testclass(100)]]
result = reduce(lambda x,y:x-y,classlist[1:],classlist[0])
print(result)
ORIGIN ANSWER:
In fact, what you need is List Comprehensions.
Please let me show you the code
class testclass():
def __init__(self, data):
self.data = data
def times5(self):
return testclass(self.data * 5)
classlist = [testclass(1), testclass(10), testclass(100)]
results = [x.times5() for x in classlist]
print(results)

Method changes both instances even if applied to only one of them

I'm struggling to understand why my simple code behaves like this. I create 2 instances a and b that takes in an array as argument. Then I define a method to change one of the instances array, but then both get changed. Any idea why this happen and how can I avoid the method changing the other instance?
import numpy as np
class Test:
def __init__(self, arg):
self.arg=arg
def change(self,i,j,new):
self.arg[i][j]=new
array=np.array([[11,12,13]])
a=Test(array)
b=Test(array)
#prints the same as expected
print(a.arg)
print(b.arg)
print()
a.change(0,0,3)
#still prints the same, even though I did
#not change b.arg
print(a.arg)
print(b.arg)
Because you assigned the same object as the instance members. You can use np.array(x, copy=True) or x.copy() to generate a new array object:
array = np.array([[11,12,13]])
a = Test(array.copy())
b = Test(np.array(array, copy=True))
Alternatively, if your arg is always a np.array, you could do it in the __init__ method (as noted by roganjosh in the comments):
class Test:
def __init__(self, arg):
self.arg = np.array(arg, copy=True)
...

How do I avoid recomputing variables in this python class?

In the snippet below, how do I avoid computing the following numpy variables mask, zbar, te , ro and rvol in the procedures Get_mask, Get_K_Shell_Te etc? These variables are large arrays and I have to define at least six more procedures that reuse them. It looks like what I am doing is not a good idea and is slow.
import numpy as np
# this computes various quantities related to the shell in a object oriented way
class Shell_Data:
def __init__(self, data):
self.data = data
def Get_mask(self):
zbar=self.data['z2a1']
y=self.data['y']*1000
mask= np.logical_and(zbar >= 16 ,zbar<= 19 )
return self.mask
def Get_Shell_Te(self):
self.mask =self.Get_mask()
te =self.data['te'][self.mask]
ro =self.data['ro'][self.mask]
rvol =self.data['rvol'][self.mask]
self.Shell_Te=np.sum(te*ro/rvol)/(np.sum(ro/rvol))
print "Shell Temperature= %0.3f eV" % (self.Shell_Te)
return self.Shell_Te
def Get_Shell_ro(self):
mask =self.Get_mask()
te =self.data['te'][mask]
ro =self.data['ro'][mask]
rvol =self.data['rvol'][mask]
radk =self.data['radk'][mask]
self.Shell_ro=np.sum(radk*ro/rvol)/np.sum(radk/rvol)
return self.Shell_ro
zbar depends on self.data. If you update self.data, you likely have to re-compute it.
If you can make your data immutable, you can compute these values once, e.g. in the constructor.
If you want to avoid calculating the mask data until it's actually required, you can cache the value, like so:
class Shell_Data(...):
def __init__(self,...):
self.cached_mask = None
...
# #property makes an access to self.mask
# to actually return the result of a call to self.mask()
#property
def mask(self):
if self.cached_mask is None: # Not yet calculated.
self.cached_mask = self.computeMask()
return self.cached_mask
def computeMask(self):
zbar = ...
...
return np.logical_and(...)
def someComputation(self):
# The first access to self.mask will compute it.
te = self.data['te'][self.mask]
# The second access will just reuse the same result.
ro = self.data['ro'][self.mask]
...
If you have to mutate self.data, you can cache the computed data, and re-calculate it only when self.data changes. E.g. if you had a setData() method for that, you could recalculate the mask in it, or just set self.cached_mask to None.
(Also, read about instance variables again.
Every method receives the parameter named self, the instance of the object for which it is called. You can access all its instance variables as self.something, exactly the way you access instance variables (and methods) when an object is not called self. If you set an instance variable in one method, you can just access it an another (e.g. self.mask), no need to return it. If you return something from a method, likely it's not worth storing as an instance variable, like self.mask.)

Categories

Resources