I have a class with iterable attributes which themselves may have iterable attributes and so on to an arbitrary depth.
A simple example would be the following:
class Foo:
def __init__(self):
self.a = 1
class Bar:
def __init__(self):
self.b = [Foo(), 4]
bar = Bar()
My goal is to get an attribute given its full name as a string, in the example, this could be name = 'bar.c[0].a'. In general, the name is longer and mixes getattr, list and dict indexing arbitrarily, e.g. something like 'a.b[0].c.d.e[5]['i'].f'.
I know this could be achieved using attr = eval(name) but I want to avoid using eval. Without the indexing in between, I would just use operator.attrgetter. Is there a similarly clean and general solution for this problem?
Related
Some times I found useful split bigger class in smaller classes with their methods and attributes that then I access assigning to an attribute of the bigger class an instance of the smaller class. In this way I can organize the class better: when I work using the console I can use nested dot notation instead of seeing a lot of attributes. For instance, I have an instrument with some parameters that can be grouped together and a method that is linked to these parameters. I would structure the class like this:
class params(object):
def __init__(self,P,I,D):
self.P = P
self.I = I
self.D = D
def compute_PID(self):
pass
class instrument(object):
def __init__(self,name,SN,P,I,D):
self.name = name
self.SN = SN
self.params = params(P,I,D)
def swith_on(self):
pass
myinstrument = instrument('blender','123',45,4,3)
myinstrument.params.P
Is there any drawback of this deign patter? I imagine that from the point of view of the memory it requires more memory, but working with the dot notation make the things easier compared to a dictionary.
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.
Consider the following situation:
class A:
def __init__(self):
self.b_list = []
def add_element(b)
self.b_list.append(b)
class B:
def __init__(self,x):
self.x = x
class C:
def __init__(self,a,other_data):
self.a = a
def find_new_b(self):
....
def solve(c):
return c.find_new_b()
Each instance of class A has a list of instances of class B, that are added in an incremental way.
I need to iterate over all the instances of class A and find for each of them a new element of class B.
I'm using multiprocessing to do that
list_of_c_elements = [...]
pool = multiprocessing.Pool()
results = pool.map(solve, list_of_c_elements)
The problem
In results I have a list of results and I would like to understand of which instance of class A the new instance of class B belongs. Each instance of class B is generic, and I would like to keep the two classes decoupled.
Considered solution
Change solve() to:
def solve(c):
return (c.a, c.find_new_b())
I have to compare all the elements of class A I have with the one returned (very inefficient).
I cannot do something like:
for output in results:
output[0].add_element(output[1])
since the instance of class A returned is another instance.
Is there a more efficient way to achieve my goal?
C could additionally hold id(a) in a member and you can generate an index dictionary {id(a): a} if necessary. Be aware that all calls of id() must of course happen in the main process to produce usable results.
I have a class
class MyClass():
def __init__(self):
self.a = 7
self.b = 2
#property
def aAndB(self):
return self.a + self.b
I would like a function that iterates over all properties and returns only class instances having a certain property.
My goal is a function like this:
def findInstances(listOfInstances, instanceVariable, instanceValue):
#return all instances in listOfInstances where instanceVariable==instanceValue
Using instance.__dict__ only gives me a and b, but not aAndB. I would like to have a dict of all properties/methods with their values to loop over, so I can search for instances where a certain property (or method decorated with #property) has a certain value.
Currently, calling the function like this
findInstances(someListOfInstances, 'aAndB', '23')
makes Python complain that aAndB is not in instance.__dict__.
Maybe all of you are right and the answers are there, but I still don't get it. All the answers in the mentioned questions get lists, not dictionaries. I want all the properties (including methods with the #property decorator) and their values. Is there a way to iterate over the values of the keys in dir(myClass)? The dir command only contains the names of the attributes, not their values.
I need something like
for a in dir(myClass):
print a, myClass.(a) # get the value for an attribute stored in a variable
To be even more clear: The following achieves exactly what I want but there is probably a better way that I don't know.
for a in dir(myClass):
print a, eval("myClass.{}".format(a))
There's actually a very simple way to do this, using getattr in place of eval:
myClass = MyClass()
for a in dir(myClass):
if(a[:2] != "__"): #don't print double underscore methods
print a, getattr(myClass, a)
Output:
a 7
aAndB 9
b 2
This has the very big advantage of not needing to hard code in the name of your instance into the string, as is required using eval("myClass.{}".format(a))
How can I iterate over an object and assign all it properties to a list
From
a = []
class A(object):
def __init__(self):
self.myinstatt1 = 'one'
self.myinstatt2 = 'two'
to
a =['one','two']
Don't create a full fledged class if you just want to store a bunch of attributes and return a list so that your API can consume it. Use a namedtuple instead. Here is an example.
>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
If your API just expects a sequence (not specifically a list), you can pass p directly. If it needs a list specifically, it is trivial to convert the Point object to a list.
>>> list(p)
[1, 2]
You can even subclass the newly created Point class and add more methods (documentation has details). If namedtuple doesn't meet your needs, consider sub-classing abc.Sequence Abstract Base Class or using it as a mixin.
One approach is to make your class behave like a list by implementing some or all of the container API. Depending on how the external API you're using works, you might only need to implement __iter__. If it needs more, you could always pass it list(a), which will build a list using an iterator.
Here's an example of how easy it can be to add an __iter__ method:
class A(object):
def __init__(self):
self.myAttr1 = "one"
self.myAttr2 = "two"
def __iter__(self):
yield self.myAttr1
yield self.myAttr2