Python - Proper way of serially reassign/update class members - python

I have a class whose members are lists of numbers built by accumulating values from experimental data, like
class MyClass:
def __init__(self):
container1 = []
container2 = []
...
def accumulate_from_dataset(self,dataset):
for entry in dataset:
container1.append( foo (entry) )
container2.append( bar (entry) )
...
def process_accumulated_data(self):
'''called when all the data is gathered
'''
process1(container1)
process2(container2)
...
Issue: it would be beneficial if I could convert all the lists into numpy arrays.
what I tried: the simple conversion
self.container1 = np.array(self.container1)
works. Although, if I would like to consider "more fields in one shot", like
lists_to_convert = [self.container1, self.container2, ...]
def converter(lists_to_convert):
for list in lists_to_convert:
list = np.array(list)
there is not any effective change since the references to the class members are passed by value.
I am thus wondering if there is a smart approach/workaround to handle the whole conversion process.
Any help appreciated

From The Pragmatic Programmer:
Ask yourself: "Does it have to be done this way? Does it have to be done at all?
Maybe you should rethink your data structure? Maybe some dictionary or a simple list of lists would be easier to handle?
Note that in the example presented, container1 and container2 are just transformations on the initial dataset. It looks like a good place for list comprehension:
foo_data = [foo(d) for d in dataset]
# or even
foo_data = map(foo, dataset)
# or generator version
foo_data_iter = (foo(d) for d in dataset)
If you really want to operate on the instance variables as in the example, have a look at getattr and hasattr built-in functions

There isn't an easy way to do this because as you say python passes "by-reference-by-value"
You could add a to_numpy method in your class:
class MyClass:
def __init__(self):
container1 = []
container2 = []
...
def to_numpy(self,container):
list = self.__getattr__(container)
self.__setattr__(container,np.array(list))
...
And then do something like:
object = MyClass()
lists_to_convert = ["container1", "container2" ...]
def converter(lists_to_convert):
for list in lists_to_convert:
object.to_numpy(list)
But it's not very pretty and this sort of code would normally make me take a step back and think about my design.

Related

Quick way to convert all instance variables in a class, to a list (Python)

I have created a class with around 100+ instance variables (as it will be used in a function to do something else).
Is there a way to translate all the instance variables; into an array list. Without manually appending each instance variable.
For instance:
class CreateHouse(object):
self.name = "Foobar"
self.title = "FooBarTest"
self.value = "FooBarValue"
# ...
# ...
# (100 more instance variables)
Is there a quicker way to append all these items to a list:
Quicker than:
theList = []
theList.append(self.name)
theList.append(self.title)
theList.append(self.value)
# ... (x100 elements)
The list would be used to perform another task, in another class/method.
The only solution (without totally rethinking your whole design - which FWIW might be an option to consider, cf my comments on your question) is to have a list of the attribute names (in the order you want them in the final list) and use getattr
class MonstruousGodClass(object):
_fields_list = ["name", "title", "value", ] #etc...
def as_list(self):
return [getattr(self, fieldname) for fieldname in self._fields_list]
Now since, as I mentionned in a comment, a list is NOT the right datatype here (from a semantical POV at least), you may want to use a dict instead - which makes the code much simpler:
import copy
def as_dict(self):
# we return a deepcopy to avoid unexpected side-effects
return copy.deepcopy(self.__dict__)

Declaring several python classes in a loop?

I have a generic class definition, something like this -
class Foo(object):
property = 1
def __init__(self, ...):
...
I wish to create a large number of classes, each of which has a different value of property, and store these classes in a list. The classes in this list will be subsequently used to create several objects.
What is the best way to do this?
While I doubt that there isn't a better solution to whatever your underlying problem might be, you can create classes dynamically using type:
class Foo(object):
def __init__(self, x):
self.x = x
# class-name---vvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvv--class-attributes
klasses = [type('Foo{}'.format(n), (Foo,), {'property': n}) for n in range(5)]
# parent-classes ^^^^^^
klasses[4]
# <class '__main__.Foo4'>
klasses[4].property
# 4
inst = klasses[4]('bar')
inst.x
# 'bar'
c = []
for i in range(5):
class X(object):
property = i
def __init__(self):
print(self.property)
c.append(X)
c[0]() # will print 0
c[4]() # will print 4
But this has a bunch of drawbacks. I also think that the comment given below the question is remarkable. Very likely you strive for a solution which is not the best for your original problem.
If you really want to do it, then sure you can create classes dynamically using type
class BaseClass(object):
# the code that needs to be common among all classes
properties = [1, 2, 3]
classes = [type("class_{0}".format(i), (BaseClass,), {'property': property}) for i, property in enumerate(properties)]
However, you probably need to think about your design. I don't know about the problem you want to solve, but perhaps keeping the property variable as instance one would make more sense.
I think the best way would just be to iterate i=0 to n-1, appending a new object to the end of the list. You can then use i to index into the list and change the value of property that way.

Aggregate nested lists without nested loops in Python 3.x

I'm about to work my way into python's 3.5 lambda notation and I'm wondering wether nested loops can simply be replaced with a lambda one-liner.
e.g.:
I have this simple dummy class hierarchy:
class Resource:
def __init__(self, name="foo"):
self.name = name
class Course:
def __init__(self):
self.resources = list()
class College:
def __init__(self):
self.courses = list()
I have an instance of Collegewith multiple Courses and Resources as my starting point.
college = College()
Now if I want a listof all the Resources in my College I could easily do this with 2 for-loops:
all_resources = list()
for course in college.courses:
for resource in course.resources:
all_resources.append(resource)
This is indeed very simple but I wondered whether I could also achieve this by doing something like this:
all_resources = list(map(lambda r: r, [c.resources for c in college.courses]))
But unfortunately this gives me a listof lists and not a listof Resources, what I wanted to achieve. Is lambda suitable for something like that?
What would be the most pythonic way for a operation like this?
First of all, please stop creating empty lists by calling list() — it's both simpler and quicker to use a literal empty list [] (cheeck this with timeit if you don't believe me).
Secondly, there is absolutely no need to use lambda to create the list you want. A simple list comprehension will do the job:
all_resources = [r for r in course.resources for course in college.courses]
Don't overthink it. Keep it as simple as you can.
You shouldn't be using lambdas for this. You should be list comprehensions.
all_resources = [resource for resource for course in college.courses in course.resources]
Lambdas would be completely unnecessary here. If you absolutely wanted to, you could do something like:
all_resources = []
[(lambda c: [all_resources.append(r) for r in c])(x) for x in college.courses)]
But I'd recommend the first one. An alternative giving a different order would be
all_resources = [resource for resource in course.resources for course in college.courses]

Python: how to automatically create instance object?

I want to create instance objects automatically as I explained in the following:
Class MyClass:
def __init__(self,x):
self.x = x
list = ["A","B"]
I want to create the following but automatically, means to loop through the list and create identical object for each element:
A = MyClass(text)
B = MyClass(text)
e.g. like the following which doesn't work:
# this doesn't work but explains more what I need
for i in list:
i = MyClass(text)
Thanks to all of your help!
In general, you can't and shouldn't shove things into your namespace like that. It's better to store those instances in a dict or a list
Class MyClass:
def __init__(self,x):
self.x = x
lst = ["A","B"] # don't use list as an identifier
myclasses = {k: MyClass(text) for k in lst}
Now your instances are
myclasses['A'], myclasses['B'] etc.
If you really want to create a handful of variables in your namespace:
A, B = (MyClass(text) for x in range(2))
note that this means you need to be explicit. You can't get the A,B from a file or user input etc.
Don't be tempted to use exec to pull this off. It's probably the wrong way to go about solving your problem. Tell us why you think you need to do it instead.

Generate Class Instance In Python

I have been having trouble getting python to generate a (non-predetermined) number of class instances. Basically have classes be able to reproduce themselves.
class foo:
def __init__(self, name):
self.name = name
while True:
newinstance(foo) #what would the code be for this?
#or maybe
foo.newinstance #just something that could update itself
Basically generate a new instance any number of times. Thanks ahead of time.
This will do what you're asking for, but you'll want to hold onto the values somehow:
while True:
foo(some_name)
This will loop forever, so a more realistic option might be:
names = ["Ned", "Felix", "Guy"]
fooses = [foo(name) for name in names]
Use a list comprehension:
instances_of_foo = [foo("bar") for i in range(number_of_instances)]
Also, if you would like to pass different arguments to each instance, you can create of list of args instead of using range().
list_of_args = [args_for_instance_one, args_for_instance_two,...]
instances_of_foo = [foo(arg) for arg in list_of_args]

Categories

Resources