comparison function must return int, not long - python

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)

Related

Comparing contents of lists ignoring order

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

Counting the occurence of instances of a certain class in list of lists

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. :)

Error while defining the class

I'm new to "class" in python. I created a following class in python. The objective of this class is, if pass a list of numbers, if the sum of 2 numbers is 50, it will return me the position of those number in the list.
from itertools import combinations
class numList(object):
def findComb(self):
a = []
b = []
for comb in combinations(self, 2):
a.append(comb)
for i in range(1, len(a)):
if sum(a[i]) == 50:
b.append(a[i])
return b
c = numList()
c.findComb([10,20,10,40,50,60,70])
But I'm getting the following error, when I'm trying to execute it:
TypeError: findComb() takes 1 positional argument but 2 were given
Please let me know where I'm making the mistake.
Thank you!
By design, the first argument of every class function is always a reference to the current instance of the class (always named self).
You are calling findComb with an additional argument when you defined it to only take one (self).
def findComb(self):
...
should be
def findComb(self, myList):
...
All your references to self in your function implementation will need to be updated accordingly to use myList.
Each method within a class takes as positional input the instance of the class itself, unless you add the #staticmethod decorator.
So you are receiving the error because the function findComb receives as input:
the instance (by default)
the list you passed
This should clarify the error you are receiving.
You can fix it in two ways:
Assigning the input list to an attribute of the class and then use the attribute in the function:
class numList(object):
def __init__(self, inp_list):
self.input = inp_list
def findComb(self):
a = []
b = []
for comb in combinations(self.input, 2):
a.append(comb)
for i in range(1, len(a)):
if sum(a[i]) == 50:
b.append(a[i])
return b
c = numList([10,20,10,40,50,60,70])
c.findComb()
Define findComb as a staticmethod, so that it would only use the argument you are passing (without using the instance as first argument):
class numList(object):
#staticmethod
def findComb(inp_list):
a = []
b = []
for comb in combinations(inp_list, 2):
a.append(comb)
for i in range(1, len(a)):
if sum(a[i]) == 50:
b.append(a[i])
return b
c = numList()
c.findComb([10,20,10,40,50,60,70])

From Haskell to functional Python

I want to translate some Haskell code into Python.
The Haskell classes/instances look like:
{-# LANGUAGE MultiParamTypeClasses #-}
module MyModule where
class Example a b where
doSomething :: a -> b -> Bool
doSomethingElse :: a -> b -> Int
instance Example Int Int where
doSomething a b = (a + b * 2) > 5
doSomethingElse a b = a - b * 4
Is there a way in Python to approximate the Haskell class/instance construct?
What is the least offensive way to translate this into Python?
This doesn't really have an analogue in Python, but you can fake it:
def int_int_doSomething(a, b):
return (a + b * 2) > 5
def int_int_doSomethingElse(a, b):
return a - b * 4
Example = {}
Example[(int, int)] = (int_int_doSomething, int_int_doSomethingElse)
def doSomething(a, b):
types = type(a), type(b)
return Example[types][0](a, b)
def doSomethingElse(a, b):
types = type(a), type(b)
return Example[types][1](a, b)
All you have to do is add new values to Example for each type combination you want to have. You could even throw in some extra error handling in doSomething and doSomethingElse, or some other methods to make it easier. Another way would be to make an object that keeps track of all of these and lets you add new types to the map in a more managed way, but it's just more bookkeeping on top of what I've already shown.
Keep in mind that this is essentially how Haskell does it, too, except the checks are performed at compile time. Typeclasses are really nothing more than a dictionary lookup on the type to pick the appropriate functions to insert into the computation. Haskell just does this automatically for you at compile time instead of you having to manage it yourself like you do in Python.
To add that bookkeeping, you could do something like the following, keeping it in its own module and then it'll only (by default) export the symbols in __all__. This keeps things looking more like the Haskell version:
class _Example(object):
def __init__(self, doSomething, doSomethingElse):
self.doSomething = doSomething
self.doSomethingElse = doSomethingElse
ExampleStore = {}
def register(type1, type2, instance):
ExampleStore[(type1, type2)] = instance
def doSomething(a, b):
types = type(a), type(b)
return ExampleStore[types].doSomething(a, b)
def doSomethingElse(a, b):
types = type(a), type(b)
return ExampleStore[types].doSomethingElse(a, b)
def Example(type1, type2, doSomething, doSomethingElse):
register(type1, type2, _Example(doSomething, doSomethingElse))
__all__ = [
'doSomethingElse',
'doSomethingElse',
'Example'
]
Then you can make instances like
Example(int, int,
doSomething=lambda a, b: (a + b * 2) > 5,
doSomethingElse=lambda a, b: a - b * 4
)
Which looks almost like Haskell.
You don't have parametric types in Python, as it's dynamically typed. Also the distinction between classes and instances is clear in Python, but as classes are themselves "live objects", the distinction of usage might be a little bit blurred sometimes...
For your case, a classical implementation might go as:
#you don't really need this base class, it's just for documenting purposes
class Example:
def doSomething(self, a, b):
raise "Not Implemented"
def doSomethingElse(self, a, b):
raise "Not Implemented"
class ConcreteClass(Example):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def doSomething(self, a, b):
return (a + b * self.x) > self.y
def doSomethingElse(self, a, b):
return a - b * self.z
instance = ConcreteClass((2, 5, 4)
but I personally dislike that convoluted style, so you might just go with something more lightweight, like:
from collections import namedtuple
Example = namedtuple('Example', 'doSomething doSomethingElse')
instance = Example((lambda a, b: (a + b * 2) > 5),
(lambda a, b: a - b *4 ))
And of course, rely on duck typing and usually "let it crash". The lack of type safety should be made up with extensive unit testing.

Python, mutable object as default argument, is there any way to solve?

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

Categories