Python: dynamically create methods based on other classes' - python

I've looked for quite a while but couldn't find a proper answer to my question:
I have a class containing methods which operate on arrays and I want dynamically create methods with a similar name in another class with a modified output.
I've got something like this so far, can anyone guide me ?
Thanks
Class A():
def__init__(self,array):
self.data = array
def method1(self,*args):
newarray = whatever(self.data,*args)
return newarray
def method2(self,*args):
newarray = whatever2(self.data,*args)
return newarray
I want to be able to use those methods to generate new ones in a more complex class, say:
class B(C): #inherits from C
def __init__(self,[arg1,array]):
#initialize from parent class
C.__init__(self,[arg1,array])
#create new methods for this class using same name
methodnames = [element for element in dir(A) if element[0] != '_']
for methodname in methodnames:
##following works but this is not the output I want here
#self.__dict__[methodname] = getattr(A(array),methodname)
#following doesn't work... at least not as I expect it to
#case1
#self.__dict__[methodname] = [arg1,getattr(A(array),methodname)]
#case2
self.__dict__[methodname] = list([arg1,getattr(A(array),methodname)])
a = array
#following returns a list of [arg1, method] but what I really want is [arg1,newarray]
C([arg1,array]).method1(*args)
OK, so let's try to be clearer:
Class A contains filters, takes an array and applies filter as method, returns filtered data.
Class filters()
def__init__(self,array):
self.data = array
def filter1(self,*args):
newarray = median(self.data,*args)
return newarray
def filter2(self,*args):
newarray = gaussian(self.data,*args)
return newarray
...
In another module, I have class SpecialData, which operates on a list of x,y data (where x and y are iterables, i.e. lists, arrays...). So something like
Class SpecialData():
def __init__(self,[x,y]):
self.data = [x,y]
def power(self,power):
ypow = self.data[1]**power
return [x,pow]
def porod(self):
return [x**4,x**4*y]
....
Now, what I want is to add the filter methods contained in class filters to class SpecialData.
I could, of course do this by re-coding all filters with proper format for SpecialClass. but what I really want, is that each time a new filter is added to class filters, to make it available at runtime in class SpecialData without having to re-hard code the new filter.
So, not being very clever, I tried to read the list of available filters in class filters by:
import filters
filternames = [element for element in dir(filters) if element[0] != '_']
for fitlername in filternames:
generate_filters_in_class_SpecialClass
How do I do this properly ?
I found a number of posts related to this, some using super(), others using SpecialData.dict or even setattr. Since the 2nd seemed more understandable to me, I focused on this one and came up with:
import filters
Class SpecialData():
def __init__(self,[x,y]):
self.data = [x,y]
filternames = [element for element in dir(filters) if element[0] != '_']
for fitlername in filternames:
self.__dict__[fitlername ] = [self.data[0],getattr(filters(self.data[1]),fitlername)]
Of course, this doesn't work, because the list is not callable. If I change the last line to :
self.dict[fitlername ] = list([self.data[0],getattr(filters(self.data[1]),fitlername)])
it returns the method as the 2nd element, rather than the result.
Note that the following works, but this is not what I want...
self.dict[fitlername ] = getattr(filters(self.data[1]),fitlername)
Hope this is clearer now...

I think you are trying to make an advanced use of Python without using/knowing its advanced features, like you are borrowing techniques from another language.
This is not a criticism, but you should have a look on Python tutorial, Python introspection or metaclasses.
I think that if you just complete your knowledge on Python functions you will be easily able to solve your problem in a much simpler way.

Rather than generating a proposed solution, you should make it clearer what you are trying to achieve. Class A is a clear example of the starting point; please post an example of your desired ending point, e.g.
Class B():
def__init__(self,array):
self.data = array
def method1(self,*args):
newarray = ComplexWhatever(self.data,*args)
return newarray
def method2(self,*args):
newarray = EvenBiggerWhatever2(self.data,*args)
return newarray
a = A(input_array)
b = B(input_array)
print(a.method1(args))
print(b.method1(args))
What isn't clear is how you want to "dynamically generate" the new function "ComplexWhatever()" instead of writing the function by hand.

Related

List in a class changes when given as a function attribute

I am relatively new to Python and love it. If I have a problem, usually I manage to find my own solutions, or relative topics that can help me understand where I messed up. But this one I can't seem to wrap my head around it.
So the issue I'm facing right now is that I give a class1.list to another class2.function, where I do some stuff there with that list. My issue is that my class1.list changes in that function and I don't understand why:
class ClassA():
def function_a(self, test):
test[0][0] = '?'
test[0][1] = '?'
return test
class ClassB():
class_b_list = [[1,2],[3,4]]
def function_b(self,value):
value[1][0] = 350
value[1][1] = 250
return value
my_class_a = ClassA()
my_class_b = ClassB()
print(my_class_b.class_b_list)
my_class_a.function_a(my_class_b.class_b_list)
my_class_b.function_b(my_class_b.class_b_list)
print(my_class_b.class_b_list)
So my first print will give me the actual value of the list: [[1,2][3,4]]
But after I give it as an attribute, my list changes to: [['?','?'][350,250]]
What am I missing? I don't want my_class_b.class_b_list to change.
Don't try to mutate the argument. Copy it first, then change and return.
from copy import deepcopy
class ClassA():
def function_a(self, test):
# use deep copy if you're using nested lists, or mutating object/dict in list
test = deepcopy(test)
# if not, slice copy is usually enough
# test = test[:]
test[0][0] = '?'
test[0][1] = '?'
return test
Also see: How to clone or copy a list?

Why is the returned address and not value being printed?

So I wrote a code for returning a list of objects with dimensions (5, 3, 3, 3) in python 3.5. Now my problem is that whenever I try to print the returned value, it prints the addresses of 5 separate 3D lists instead of the list as a whole. Even the type of returned value shows up as list. What exactly is the problem here?
Here is my initializing and returning function.
class layer(object):
def __init__(self, inputDimensions, channels, padding, stride, layerInput):
self.inputDimensions = inputDimensions
self.channels = channels
self.padding = padding
self.stride = stride
self.layerInput = layerInput
def getLayerInfo(self):
return self.inputDimensions, self.channels, self.padding, self.stride
def getLayerInput(self):
return self.layerInput
def getLayerFilterInfo(self):
return self.filterDimensions, self.numberOfFilters
def getLayerFilters(self):
return self.filters
def initializeFilter(self, filterDimensions, numberOfFilters):
self.filterDimensions = filterDimensions
self.numberOfFilters = numberOfFilters
self.filters = []
for i in range(0, self.numberOfFilters):
fil = filters(self.filterDimensions)
self.filters.append(fil)
Here is my filter class.
class filters(object):
def __init__(self, dimensions):
self.dimensions = dimensions
self.fil = np.random.random_sample(self.dimensions)
Here is a sample of input and output.
In [11]: l.getLayerFilters()
Out[11]:
[<__main__.filters at 0xb195a90>,
<__main__.filters at 0xb1cb588>,
<__main__.filters at 0xb1cb320>,
<__main__.filters at 0xb1cb5c0>,
<__main__.filters at 0xb1cbba8>]
In [12]: type(l.getLayerFilters())
Out[12]: list
In short: Instead of doing this:
fil = filters(self.filterDimensions)
self.filters.append(fil)
you can probably achieve what you want if you do this:
fil = filters(self.filterDimensions)
self.filters += fil.fil
Not sure, it depends on what those filters are supposed to be and how you want to put them together and in your result. Then again, it seems possible to get rid of the filters class altogether.
As it is, you're creating instances of your filters class, then append them to a list. What you get when you print that list is, as expected and seen in OP, a list of objects. Those objects ofc don't represent themselves as lists, they are all general objects with a default string representation that only shows the class that object "comes from" and the address in memory to remind you that it's indeed an object and not a class. To make your class aware of that it's supposed to be some sort of list and to make it behave that way, you could implement the __repr__ method like so:
def __repr__(self):
return "<Filters: {}>".format(self.fil)
Put that in your filters class and any print of it should now show you the list inside of it (or its representation). Improvable, still.
BTW: Please consider renaming your class to Filters or so. Upper camel case is what PEP8 suggests for classes.
I wanted to know how to return data members
You need to access them
filters = l.getLayerFilters()
for f in filters:
print(f.dimensions, f.fil)
whenever I try to print the returned value, it prints the addresses
You never told Python how else it should print your object. Just because those fields are there, it will not automatically show you them.
As attempted to discuss with you in the comments, you will need to override that output behavior yourself with a new function that returns a single human-readable representation of your class
As an example
class filters(object):
def __init__(self, dimensions):
self.dimensions = dimensions
self.fil = np.random.random_sample(self.dimensions)
def __repr__(self):
return "dimensions: {}\nfil: {}".format(self.dimensions, self.fil)
Now, try it again
Some more reading
https://stackoverflow.com/a/2626364/2308683
Understanding repr( ) function in Python

Python class that works as list of lists

I'm trying to create a python class that can work as a list of lists. However, all I've managed to develop so far is,
class MyNestedList(list):
...
I'm aware that the above code will work as,
my = MyNestedList()
my[0] = 1
...
But I want my class to work as,
my[0][0] = 1
...
Will anyone please guide me further?
EDIT: I want the class to pass as a type for the deap framework, as my individual. I can't pass list of lists as my type as it would break my structure.
Here is an example. You have to initialize the nested list with enough elements, or you'll get index errors.
class NestedLst(object):
def __init__(self, x, y):
self.data = [[None]*y]*x
def __getitem__(self, i):
return self.data[i]
nlst = NestedLst(2, 2)
nlst[0][0] = 10
print nlst[0][0]

Python - Recommended way to dynamically add methods within a class

I have a class where I want to initialize an attribute self.listN and an add_to_listN method for each element of a list, e.g. from attrs = ['list1', 'list2'] I want list1 and list2 to be initialized as empty lists and the methods add_to_list1 and add_to_list2 to be created. Each add_to_listN method should take two parameters, say value and unit, and append a tuple (value, unit) to the corresponding listN.
The class should therefore look like this in the end:
class Foo():
def __init__(self):
self.list1 = []
self.list1 = []
def add_to_list1(value, unit):
self.list1.append((value, unit))
def add_to_list2(value, unit):
self.list2.append((value, unit))
Leaving aside all the checks and the rest of the class, I came up with this:
class Foo():
def __init__(self):
for attr in ['list1', 'list2']:
setattr(self, attr, [])
setattr(self, 'add_to_%s' % attr, self._simple_add(attr))
def _simple_add(self, attr):
def method(value, unit=None):
getattr(self, attr).append((value, unit))
return method
I also checked other solutions such as the ones suggested here and I would like to do it "right", so my questions are:
Are/Should these methods (be) actually classmethods or not?
Is there a cost in creating the methods in __init__, and in this case is there an alternative?
Where is the best place to run the for loop and add these methods? Within the class definition? Out of it?
Is the use of metaclasses recommended in this case?
Update
Although Benjamin Hodgson makes some good points, I'm not asking for a (perhaps better) alternative way to do this but for the best way to use the tools that I mentioned. I'm using a simplified example in order not to focus on the details.
To further clarify my questions: the add_to_listN methods are meant to be additional, not to replace setters/getters (so I still want to be able to do l1 = f.list1 and f.list1 = [] with f = Foo()).
You are making a design error. You could override __getattr__, parse the attribute name, and return a closure which does what you want, but it's strange to dynamically generate methods, and strange code is bad code. There are often situations where you need to do it, but this is not one of them.
Instead of generating n methods which each do the same thing to one of n objects, why not just write one method which is parameterised by n? Something roughly like this:
class Foo:
def __init__(self):
self.lists = [
[],
[]
]
def add(self, row, value):
self.lists[row].append(value)
Then foo.add1(x) becomes simply foo.add(1, x); foo.add2(x) becomes foo.add(2, x), and so on. There's one method, parameterised along the axis of variation, which serves all cases - rather than a litany of ad-hoc generated methods. It's much simpler.
Don't mix up the data in your system with the names of the data in your system.

What is the proper way to share a list between class instances?

I have this example code
my_list = ["a","b","c","d"]
class A:
def __repr__(self):
return ', '.join(my_list)
def add(self, num):
my_list.append(num)
class_list = []
for x in range(5):
class_list.append(A())
class_list[x].add("class_%s" % (x))
print class_list[x]
The non-example code of mine is more complicated, but the idea is that I have multiple instances of the classes off doing a "thing". The global my_list is utilized across all instances. When certain logic is met within a class, that instance will modify the list. The rest of the classes will utilize that list to perform their logic as well. Any instance can add to the list, and all instances should be able to utilize the updated value.
Now in this example, the my_list is shared, but is this the correct way to do it?
A class attribute is usually better than a global, because then they're just sharing it with each other, rather than with everyone in the world.
To do that, move the my_list = ["a","b","c","d"] line under the class A:, and change every reference to my_list to self.my_list or A.my_list:
class A(object):
shared_list = []
def add(self, num):
self.my_list.append(num)
However, an instance attribute is often even better. If you assign the same list to a bunch of different variables, it's still just one list; changing it affects all those variables. So, you can do something like this:
class A(object):
def __init__(self, shared_list):
self.shared_list = shared_list
def add(self, num):
self.shared_list.append(num)
Now it's up to the code that uses the A objects to decide whether to give them all the same list. You can even create 20 instances that share one list, and 10 that share a different one:
list1 = []
group1 = [A(list1) for _ in range(20)
list2 = []
group2 = [A(list2) for _ in range(10)
The question is whether the caller, or the A class, or nobody at all is the one who should be making the decision of how "shared" the list is. The answer is different for different applications, so it's hard to give an answer for an abstract example with names like A and my_list.

Categories