Simplest way to multithread using class variables? - python-3.x

I'm trying to implement a function in my class that calculates information from array A and outputs the result in array B. Array A and array B are both variables of a class, as is the function. Something along these lines:
class MyClass:
def __init__(self, A):
self.A = A
self.B = np.zeros((A.shape[0], A.shape[1])
def my_function(self, i):
self.B += self.A[i]
def main(self):
for i in range(A.shape[2]):
my_function(i)
example = np.random.rand(256, 256, 1000)
my_class = MyClass(example)
my_result = my_class.B
Obviously this function is oversimplified but the question revolves about how to use multiprocess with variables self.A and self.B. I've tried something like this but it didn't work at all:
class MyClass:
def __init__(self, A):
self.A = A
self.B = np.zeros((A.shape[0], A.shape[1])
def my_function(self, i):
return self.A[i]
def main(self):
with multiprocessing.Pool() as p:
position = range(self.A.shape[2])
for i, result in enumerate(p.map(my_function, position))
self.B += result

You can get your example code to work doing something like...
class MyClass:
def __init__(self, A):
self.A = A
self.B = np.zeros((A.shape[0], A.shape[1]))
def my_function(self, i):
return self.A[:,:,i]
def run(self):
with Pool() as p:
position = range(self.A.shape[2])
for result in p.imap(self.my_function, position, chunksize=self.A.shape[2]):
self.B += result
example = np.random.rand(256, 256, 1000)
my_class = MyClass(example)
st = time.time()
my_class.run()
print(time.time() - st)
The problem with multiprocessing is that it has to fork new processes and then serialize (via pickle) the data going into and out of them. For simple code like this, the overhead is much more than the actual function you're completing.
Setting chunksize to the size of your iterable is just a way to assure that python doesn't fork process pools more than once and thus reduce the overhead. For this example the multiprocessed code is still slower than doing it single process, however if you have a more complex function, the MP version could be faster.
As a general rule, I try to never put the multiprocessed function/data inside of the class. This leads to a lot of extra overhead in the fork/pickle/unpickle process. You can move your function outside with something like...
# Simple gobal data / function
data = None
def my_function(i):
global data
return data[:,:,i]
class MyClass:
def __init__(self, A):
global data
data = A # move class data to global
self.A = A
self.B = np.zeros((A.shape[0], A.shape[1]))
def run(self):
with Pool() as p:
position = range(self.A.shape[2])
for result in p.imap(my_function, position, chunksize=self.A.shape[2]):
self.B += result
example = np.random.rand(256, 256, 1000)
my_class = MyClass(example)
st = time.time()
my_class.run()
print(time.time() - st)
For a simple function like this multiprocessing will still be slower, but if your actual function has a lot of complexity this can speed things up.

Related

how to remove objects from set irrespective of order

I'm having trouble removing objects from a set. What i did was, create a test class and store two variables in it. Its a string variable. I need to store the objects i create into a set and also, any object where (t.a, t.b) is same as (t.b, t.a). Hence, whenever i add tuples into my set, i'm having trouble removing the reverse condition. Is there a way to do this in python?
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
self.variables = [a, b]
def __hash__(self):
return hash((self.a, self.b))
def __eq__(self, other: Test):
return type(self) is type(other) and self.endpoint() == other.endpoint() or
self.endpoint() == other.endpoint()[::-1]
def endpoint(self):
return (self.a, self.b)
T = Test('A','B')
T2 = Test("B",'A")
result = set()
result.add(T)
result.add(T2)
However, result is showing me both the objects in it as opposed to one. Is there a way to fix this? Thanks

Creating a child class from a parent method in python

I am trying to make a class that has a bunch of children that all have their own respective methods but share common methods through the parent. The problem is I need to create an instance of the child class in the parent method but am not sure how to go about it
my code so far looks like this
def filterAttribute(self, attribute, value):
newlist = []
for thing in self._things:
if thing._attributes[attribute] == value:
newlist.append(thing)
return self.__init__(newlist)
the class constructor takes in a list as its sole argument. Does anyone know if there is a standard way of doing this because my code is returning a NoneType object
Here are a few examples of classes I have made
This is the parent class:
class _DataGroup(object):
def __init__(self, things=None):
self._things=things
def __iter__(self):
for x in self._things:
yield x
def __getitem__(self, key):
return self._things[key]
def __len__(self):
return len(self._things)
def extend(self, datagroup):
if(isinstance(datagroup, self.__class__)):
self._things.extend(datagroup._things)
self._things = list(set(self._things))
def filterAttribute(self, attribute, value):
newlist = []
for thing in self._things:
if thing._attributes[attribute] == value:
newlist.append(thing)
#return self.__init__(newlist)
return self.__init__(newlist)
this is one of the child classes
class _AuthorGroup(_DataGroup):
def __init__(self, things=None):
self._things = things
def getIDs(self):
return [x.id for x in self._things]
def getNames(self):
return [x.name for x in self._things]
def getWDs(self):
return [x.wd for x in self._things]
def getUrns(self):
return [x.urn for x in self._things]
def filterNames(self, names, incl_none=False):
newlist = []
for thing in self._things:
if((thing is not None or (thing is None and incl_none)) and thing.name in names):
newlist.append(thing)
return _AuthorGroup(newlist)
The functionality I am looking for is that I can use the parent class's with the child classes and create instances of the child classes instead of the overall DataGroup parent class
So if I correctly understand what you are trying to accomplish:
You want a Base Class 'DataGroup' which has a set of defined attributes and methods;
You want one or mpore child classes with the ability to inherit both methods and attributes from the base class as well as have the ability to over-ride base class methjods if necessary: and
You want to invoke the child class without also having to manually invoke the base class.
If this in fact is your problem, this is how I would proceed:
Note: I have modified several functions, since I think you have several other issues with your code, for example in the base class self._things is set up as a list, but in the functions get_item and filterAttribute you are assuming self._things is a dictionary structure. I have modified the functions so all assume a dict structure for self._things
class _DataGroup:
def __init__(self, things=None):
if things == None:
self._things = dict() #Sets up default empty dict
else:
self._things=things
def __iter__(self):
for x in self._things.keys():
yield x
def __len__(self):
return len(self._things)
def extend(self, datagroup):
for k, v in datagroup.items():
nv = self._things.pop(k, [])
nv.append(v)
self._things[k] = nv
# This class utilizes the methods and attributes of DataGroup
# and adds new methods, unique to the child class
class AttributeGroup(_DataGroup):
def __init__(self, things=None):
super.__init__(things)
def getIDs(self):
return [x for x in self._things]
def getNames(self):
return [x.name for x in self._things]
def getWDs(self):
return [x.wd for x in self._things]
def getUrns(self):
return [x.urn for x in self._things]
# This class over-rides a DataGroup method and adds new attribute
class NewChild(_DataGroup):
def __init__(self, newAttrib, things = None):
self._newattrib = newAttrib
super.__init__(self, things)
def __len__(self):
return max(len(self._newattrib), len(self._things))
These examples are simplified, since I am not absolutely sure of what you really want.

Is it posible to use super() with all the parents in Python3 multiple inheritance?

If a class has 2 or more parents, how can I use super(), or any equivalent, to make reference to each of them? For example here:
class A:
def __init__(self, x): self.a = x
class B:
def __init__(self, y): self.b = y
class C(A,B):
def __init__(self, x, y):
super().__init__(x)
B.__init__(self,y) # I would to like to use super() here too
ObjetoC = C(4,3);
print (ObjetoC.a, ObjetoC.b) # It works fine
PD: I understand the MROrder. I just wonder if there is a way to reach a non-priority parent with super() or equivalent. Or if there is another elegant way to do that I have already done by using B.__init__(self,y)

Why generator object is not return generator?

I have simple iteration object that use generator. But __iter__() method of Obj() can not get iterator from Generator().__init__()
Live demo is here.
#!/usr/bin/env python3
class Obj():
def __init__(self, word):
self.word = word
def __iter__(self):
return Generator(self.word)
class Generator():
def __init__(self, word):
for l in word:
yield l
obj = Obj('qwe')
it = iter(obj)
print(it.__next__())
print(it.__next__())
print(it.__next__())
I expect console output to be 'qwe'.
__init__ cannot return or yield a non-None value. With what you have, Generator is not a generator object, but Obj is. Obj.__iter__ is the method that should be yielding values, and you can probably get rid of the Generator class altogether, unless it is used for other things that you haven't shown in your posted snippet.
class Obj():
def __init__(self, word):
self.word = word
def __iter__(self):
for l in self.word:yield l
obj = Obj('qwe')
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())

How do I define a variable for all class methods use?

I need to define a variables that is to be used in all the methods of one class.
class A:
def method1(self):
for i in range(N):
*do something with M*
def method2(self):
for i in range(N):
*do other thing with M*
and so on.
All I need to do is define N and M variables somewhere in the begining of class describing. I tried to define it in constructor like this:
class A:
def __init__(N, M):
self.N=N
self.M=M
def method1(self):
...
in tend to call A with this parameters just once. But there was another error in case I don't really now how to use constructor for now. Then I add this variables as parameters of each method:
...
def method1(self, N, M):
...
And that time it worked perfectly. But I don't want to input values each time I call A.methodX() and I'm 100% sure there is one or two ways that accords my requests.
You were on the right track so I wrote a short version of what I suspect you wanted to do with a little 'debug' code embedded. Note the instantiation of the class aa = A(1,2)
class A:
def __init__(self, N, M):
self.N = N
self.M = M
def method1(self):
print ("iterate N")
for i in range(self.N):
print (str(i) + "\n")
def method2(self):
print ("iterate M")
for i in range(self.M):
print (str(i) + "\n")
aa = A(1, 2)
aa.method1()
aa.method2()
Note that what I've done here is create instance variables - you can also create class variables by instantiating them outside of the methods. So iVar becomes the class variable.
class A:
iVar = 0
def __init__(self, N, M):
self.N = N
self.M = M
......

Resources