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
Related
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?
I asked about this yesterday, but I botched writing up my question so much that by the time I realized what I typed, all the replies were solutions to a different miss-worded problem I didn't have. Sorry for the foolish type up last time.
I have two Classes, and I want them to able to share a common list without having to pass it as a parameter. I also want to create a method that will scramble that list, and I want the list to be the same newly scrambled list in both Class A and Class B.
I figured this was a case for inheritance, so I made a Parent Class and set the list as a class attribute and made a method to scramble, but the list variable is oddly enough being now treated as an instance variable of the children.
class A:
lst = []
target = 0
def generateNewLst(self, randomRange, listSize):
self.lst = [random.randint(*randomRange) for i in range(listSize)]
class B(A):
pass
class C(A):
pass
My inherited method works just fine:
a = B()
a.generateNewLst((0, 10), 3)
a.lst # => [2,5,7]
but when I create another B:
b = B()
b.lst # => [] not shared when I want it to be
This CANNOT be solved with a class attribute in B, because that won't solve the more important below issue...
c = C()
c.lst # => [] not shared when I want it to be
TL;DR: I want a Class attribute that shares between every instance of both classes. I want a.lst == b.lst == c.lst every time I run generateNewList on ONE of any of those instances.
How should I reorganize my setup to work the way I want it to?
You need a static variable. To do so make the method generateNewLst static and let him update the static variable lst and not a member variable lst that would belong to the instance of the class and not to the class itself.
class A:
lst = []
#staticmethod
def generateNewLst(randomRange, listSize):
A.lst = [random.randint(*randomRange) for i in range(listSize)]
class B(A):
pass
class C(A):
pass
Then once you generate the lst you will have it for all classes.
a = B()
B.generateNewLst((0, 10), 3)
# the same list is available for all classes
print(A.lst)
print(B.lst)
print(C.lst)
I have a class successors that contains a list of successors. [1, 2, 3, 4]
node_successors = Successors()
When the class is assigned to a variable, for example:
neigbour = node_successors
I want neighbour to be set to the list I have stored within the class (without having to call get_successor_list() for example)
Similar to:
def __str__(self):
return "This class can be used as a string now"
Maybe will be useful for you to read this section in Python3 reference docs: Emulating container types
With your example it seems you want to get a list from your object (not the properties), you would do:
If you want to extract a list from your object, you can implement the iterator protocol (__iter__()). Then you can do list(yourInstance)
Alternatively you can implement bound indexing (__len__() and __getitem__()) either.
neighbour = node_successors.get_successor_list() if this function returns a list.
you can make the list inside the class global.
class SomeClass:
global some_list
some_list = []
some_list.append('x')
print(some_list)
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.
In Python, how is it possible to reuse existing equal immutable objects (like is done for str)? Can this be done just by defining a __hash__ method, or does it require more complicated measures?
If you want to create via the class constructor and have it return a previously created object then you will need to provide a __new__ method (because by the time you get to __init__ the object has already been created).
Here is a simple example - if the value used to initialise has been seen before then a previously created object is returned rather than a new one created:
class Cached(object):
"""Simple example of immutable object reuse."""
def __init__(self, i):
self.i = i
def __new__(cls, i, _cache={}):
try:
return _cache[i]
except KeyError:
# you must call __new__ on the base class
x = super(Cached, cls).__new__(cls)
x.__init__(i)
_cache[i] = x
return x
Note that for this example you can use anything to initialise as long as it's hashable. And just to show that objects really are being reused:
>>> a = Cached(100)
>>> b = Cached(200)
>>> c = Cached(100)
>>> a is b
False
>>> a is c
True
There are two 'software engineering' solutions to this that don't require any low-level knowledge of Python. They apply in the following scenarios:
First Scenario: Objects of your class are 'equal' if they are constructed with the same constructor parameters, and equality won't change over time after construction. Solution: Use a factory that hashses the constructor parameters:
class MyClass:
def __init__(self, someint, someotherint):
self.a = someint
self.b = someotherint
cachedict = { }
def construct_myobject(someint, someotherint):
if (someint, someotherint) not in cachedict:
cachedict[(someint, someotherint)] = MyClass(someint, someotherint)
return cachedict[(someint, someotherint)]
This approach essentially limits the instances of your class to one unique object per distinct input pair. There are obvious drawbacks as well: not all types are easily hashable and so on.
Second Scenario: Objects of your class are mutable and their 'equality' may change over time. Solution: define a class-level registry of equal instances:
class MyClass:
registry = { }
def __init__(self, someint, someotherint, third):
MyClass.registry[id(self)] = (someint, someotherint)
self.someint = someint
self.someotherint = someotherint
self.third = third
def __eq__(self, other):
return MyClass.registry[id(self)] == MyClass.registry[id(other)]
def update(self, someint, someotherint):
MyClass.registry[id(self)] = (someint, someotherint)
In this example, objects with the same someint, someotherint pair are equal, while the third parameter does not factor in. The trick is to keep the parameters in registry in sync. As an alternative to update, you could override getattr and setattr for your class instead; this would ensure that any assignment foo.someint = y would be kept synced with your class-level dictionary. See an example here.
I believe you would have to keep a dict {args: object} of instances already created, then override the class' __new__ method to check in that dictionary, and return the relevant object if it already existed. Note that I haven't implemented or tested this idea. Of course, strings are handled at the C level.