I want to sum up an instance variable of a class object in a list of that class object.
Class A(object):
def __init__(self):
self.a = 20
B = []
for i in range(10):
B.append(A())
# Can this be made more pythonic?
sum = 0
for i in B:
sum += i.a
I was thinking along the lines of using a map function or something? Don't know if that's pushing it too much though. Just curious.
class A(object):
def __init__(self):
self.a = 20
B = []
for i in range(10):
B.append(A())
s = sum(i.a for i in B)
print s
works.
You can use reduce
reduce(lambda acc, c: acc + c, [i.a for i in B])
or sum() with comprehension
sum([i.a for i in B])
Related
assuming I have a class shown below:
class OBJ:
def __init__(self, a):
self.A = a
and I have 2 lists of these objects
# sorry this is a bad example, plz look at the bottom
a = [OBJ(1), OBJ(0), OBJ(20), OBJ(-1)]
b = [OBJ(20), OBJ(-1), OBJ(1), OBJ(0)]
how do I prove that these 2 lists' contents are the same?
I have tried to use the sorted() method but it doesn't seem to work because you cannot logically compare 2 objects. Does anyone have a quick and efficient way of solving this? Thank you!
edit:
sorry the 2 lists are a bad example. When i mean the same i mean they are both refering to the same object. so:
a = OBJ(1)
b = OBJ(-1)
c = OBJ(20)
x = [a,b,c]
y = [c,a,b]
how do i prove x and y are the same?
You need to implement the __eq__ and __lt__ methods to allow you to sort the objects and then compare them:
class OBJ:
def __init__(self, a):
self.A = a
def __eq__(self, other):
if not isinstance(other, OBJ):
# don't attempt to compare against unrelated types
return NotImplemented
return self.A == other.A
def __lt__(self, other):
return self.A < other.A
a = [OBJ(1), OBJ(0), OBJ(20), OBJ(-1)]
b = [OBJ(20), OBJ(-1), OBJ(1), OBJ(0)]
test:
sorted(a) == sorted(b)
Output: True
Edit:
The comment in the question made it so that you wanted to check that the objects were exactly the same, not just the same inputs. To do this, just use id() to see if they point to the same exact object
example:
a = OBJ(1)
b = OBJ(-1)
c = OBJ(20)
x = [a,b,c]
y = [c,a,b]
sorted([id(temp) for temp in x]) == sorted([id(temp) for temp in y])
Output: True
however...
a = OBJ(1)
b = OBJ(-1)
c = OBJ(20)
d = OBJ(20) # Same input value as c, but a different object
x = [a,b,c]
y = [d,a,b]
sorted([id(temp) for temp in x]) == sorted([id(temp) for temp in y])
Output: False
You could compare 2 stand-in lists that are sorted() based on your attribute A:
>>>print(sorted([o.A for o in a]) == sorted([o.A for o in b]))
True
I have list of lists in which I want to count the number of B() and C() instances and am looking for a suitable method to do this. Using collections.Counter() and the .count() function have resulted in strange results, and I suspect I do not fully understand how list of lists work in python, or how lists of class instances work in python.
This is the list of lists:
lst = [[B() for w in range(x)] for h in range(y)]
with
class A():
def __init__(self, name):
self.name = name
class B(A):
def __init__(self, name = "B"):
A.__init__(self, name)
def update(self):
if random.random() < 0.05:
return C()
else: return self
class C(A):
def __init__(self, name = "C"):
A.__init__(self, name)
And, I use the below code to randomly change B() instances in lst into C() instances:
for row in range(y):
for column in range(x):
lst[row][column] = lst[row][column].update()
How do I count the number of B() and C() instances in the list?
You can use isinstance()
You can check what class an element is with isinstance().
Here is an example:
>>> a = C()
>>> isinstance(a, C)
True
So if you have your list, you can do:
occurrences_of_B = sum(isinstance(i, B) for r in list for i in r)
occurrences_of_C = sum(isinstance(i, C) for r in list for i in r)
you can get the occurrences of the B() and C() classes.
Essentially, we are using a generator comprehension to apply the isinstance() function to every element in the list. We then use sum on the generator as True evaluates to 1 and False to 0, so we will get the total count.
As a side note, although I said it is not good practice to name a list 'array', it is actually worse to name it exactly 'list' as this prevents you from being able to use the list() function! Better would probably be lst or l. :)
A question about deepcopy in Python:
I have a class A which contains a list of objects from class B, and a class C which contains a list of objects from class A.
class A(object):
def __init__(S):
self.S = set(S)
class B(object):
def __init__(buddies):
self.buddies = set(buddies)
class C(object):
def __init__(L):
self.L = set(L)
def SplitA(a,b):
left = deepcopy(a)
left.S -= new_b.buddies
right = A(new_b.buddies)
L |= left
L |= right
So I want C to have a Split function which, given an A object a and a B object b in a.S, will make two new A objects: one containing the 'buddies' of b (which are in a.S), one containing of the rest of a.S.
The problem is, I don't know how to figure out what the designated b becomes in the deepcopy. In other words,
how do I find new_b in the above code?
(Note: In my actual code it is important to do it in this order, i.e. adding new_a and then splitting a into left and right would not work.)
The answer is that the designated b does not become anything other than b in deep copy as you are not deep copying b at all. So just replace new_b with b in your example.
This code should do what you were looking for.
from copy import deepcopy
class A(object):
def __init__(self, S):
self.S = set(S)
class B(object):
def __init__(self, buddies):
self.buddies = set(buddies)
class C(object):
def __init__(self, L):
self.L = set(L)
def SplitA(self, a, b):
left = set()
left -= b.buddies # Since the objects in the sets are unchanged
# you can do a set difference that will leave you
# with only the members of a that are not in b
left = deepcopy(left) # Now get a deep copy of left
right = deepcopy(b.S) # and a deep copy of b.S
self.L |= left # and combine them
self.L |= right
class C:
def __init__(self,n,x):
self.n = n
self.x = x
a = C('a',1)
b = C('b',2)
c = C('c',3)
classList = [b,a,c]
for q in classList: print q.n,
classList.sort(lambda a,b: long(a.x - b.x))
for q in classList: print q.n,
Running the code above would get the error TypeError: comparison function must return int, not long.
Is there another clean way to sort class objects by certain class variables?
Use the built-in cmp function: cmp(a.x, b.x)
By the way, you can also utilize the key parameter of sort:
classList.sort(key=lambda c: c.x)
which is faster.
According to wiki.python.org:
This technique is fast because the key function is called exactly once
for each input record.
I dont think you need long
class C:
def __init__(self,n,x):
self.n = n
self.x = x
a = C('a',1)
b = C('b',2)
c = C('c',3)
classList = [b,a,c]
for q in classList: print q.n,
classList.sort(lambda a,b: a.x - b.x)
for q in classList: print q.n,
Output:
b a c a b c
Instead of using a cmp function, use a key function - it is more efficient, and doesn't have this kind of restriction on what types it can return:
classList.sort(key=lambda a: a.x)
This is also more future proof: cmp functions are no longer supported in Python 3, and continue to exist in Python 2 in order to support old code (from before key existed).
You can just add the comparison you want to your class:
class C(object):
def __init__(self,n,x):
self.n = n
self.x = x
def __cmp__(self,other):
return cmp(self.x,other.x)
class A:
def __init__(self, n=[0]):
self.data = n
a = A()
print a.data[0] #print 0
a.data[0] +=1
b = A()
print a.data[0] #print 1, desired output is 0
In the case above, is there any way to provide a default argument with the mutable object (such as list or class) in __init__() class A, but b is not affected by the operation a?
You could try this:
class A:
def __init__(self, n=None):
if n is None:
n = [0]
self.data = n
Which avoids the biggest problem you're facing here, that is, that's the same list for every single object of your type "A."
One possibility is:
class A:
def __init__(self, n=None):
if n is None:
n = [0]
self.data = n
Also:
class A:
def __init__(self, n=[0]):
print id(n)
self.data = n[:]
print id(self.data)
del n
a = A()
print a.data[0] #prints 0
a.data[0] +=1
print a.data[0] #prints 1
print
b = A()
print b.data[0] #prints desired output 0
The principle is that it creates another list. If a long list is passed as argument, there will be two long list in memory. So the inconvenience is that it creates another list.... That's why I delete n.
Don't think it's better, but it may give you comprehension of what happens