Apologies if someone has already addressed this, but I couldn't find an answer.
I have a problem where I overrode __cmp__ for a custom class, similar to this:
class MyClass(object):
def __init__(self, prop):
self.prop = prop
def __cmp__(self, other):
return cmp(self.prop, other.prop)
a = MyClass(1)
b = MyClass(2)
c = MyClass(3)
d = MyClass(1)
my_list = [a, b, c]
print a in my_list
print b in my_list
print c in my_list
print d in my_list
# Output:
#
# True
# True
# True
# True
Reading the docs tells me that this is expected, because:
For the list and tuple types, x in y is true if and only if there exists an index i such that x == y[i] is true.
My question is two-fold:
Why does Python (2.7) use == rather than is to test list membership?
Is there a better way to be doing this, such that I can test whether a specific instance of my class is actually in a container and be able to compare instances based on their attributes?
Why does Python (2.7) use == rather than is to test list membership?
Because equality is usually more interesting than identity for containment checking, especially when two different objects can have the same value.
>>> ('Hello, ' + 'world!') is 'Hello, world!'
False
>>> ('Hello, ' + 'world!') == 'Hello, world!'
True
Is there a better way to be doing this, such that I can test whether a specific instance of my class is actually in a container and be able to compare instances based on their attributes?
Not especially.
The list contains a value equal to the variable, not a reference to the variable. Think about it this way. Are these two code samples the same? What makes them different?
class Person:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
# sample one
sam = Person('Sam')
bob = Person('Bob')
people = [sam, bob]
The first sample is when you should use the is keyword. You're trying to test if the object in the list is the object outside of the list, meaning the sam inside the list is stored in the same place in memory as the sam outside the list.
# sample two
sam = Person('Sam')
bob = Person('Bob')
people = [Person('Sam'), Person('Bob')]
This sample is when you should use the double equals keyword. You're trying to test if the object in the list has the value when compared to the object outside of the list. Notice, that because we instantiated to different people with the name Sam, they will be equal in value but not the same object.
Related
NOTE: I am aware of this exact same question here and here. However, I have tried the solutions proposed by the answers there and they do not work for me (see sample code below).
A B object has a list of A. A is composed by a tuple of only two integers and an integer.
I am trying to use B objects as keys in a dictionary. However, even after implementing my own __eq__() and __hash__() methods, the length of my dictionary increases even after adding the same object to it.
See code below:
class A:
def __init__(self, my_tuple, my_integer):
self.my_tuple = my_tuple
self.my_integer = my_integer
def __eq__(self, other):
return self.my_tuple == other.my_tuple and self.my_integer == other.my_integer
class B:
def __init__(self):
self.list_of_A = []
def add(self, my_tuple, my_integer):
new_A = A(my_tuple, my_integer)
self.list_of_A.append(new_A)
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
for i in range(len(self.list_of_A)):
if self.list_of_A[i] != other.list_of_A[i]:
return False
return True
b_1 = B()
b_1.add((1,2), 3)
b_2 = B()
b_2.add((1,2), 3)
my_dict = {}
my_dict[b_1] = 'value'
print(len(my_dict))
my_dict[b_2] = 'value_2'
print(len(my_dict))
The output I am getting is
12
And the expected output is
11
Because I am adding the same object (i.e.:same properties values).
The hashes aren't equal because the repr()s aren't equal. Consider the following example I just did on my python console using your code:
>>> x = B()
>>> y = B()
>>> repr(x)
'<__main__.B object at 0x7f7b3a20c358>'
>>> repr(y)
'<__main__.B object at 0x7f7b3aa197b8>'
Obviously, x and y will have different hashes.
All you need to do, then, is overwrite __repr__() so that it outputs a deterministic value based on the contents of the object, rather than its memory address, and you should be good to go. In your case, that may look something like this:
class A:
...
def __repr__(self):
return f"A(my_tuple:{self.my_tuple}, my_integer:{self.my_integer})"
class B:
...
def __repr__(self):
return f"B(list_of_a:{self.list_of_a})"
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. :)
I have written the following code:
class FigureOut:
def setName(self, name):
fullname = name.split()
self.first_name = fullname[0]
self.last_name = fullname[1]
def getName(self):
return self.first_name, self.last_name
f = FigureOut()
f.setName("Allen Solly")
name = f.getName()
print (name)
I get the following Output:
('Allen', 'Solly')
Whenever multiple values are returned from a function in python, does it always convert the multiple values to a list of multiple values and then returns it from the function?
Is the whole process same as converting the multiple values to a list explicitly and then returning the list, for example in Java, as one can return only one object from a function in Java?
Since the return statement in getName specifies multiple elements:
def getName(self):
return self.first_name, self.last_name
Python will return a container object that basically contains them.
In this case, returning a comma separated set of elements creates a tuple. Multiple values can only be returned inside containers.
Let's use a simpler function that returns multiple values:
def foo(a, b):
return a, b
You can look at the byte code generated by using dis.dis, a disassembler for Python bytecode. For comma separated values w/o any brackets, it looks like this:
>>> import dis
>>> def foo(a, b):
... return a,b
>>> dis.dis(foo)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 BUILD_TUPLE 2
9 RETURN_VALUE
As you can see the values are first loaded on the internal stack with LOAD_FAST and then a BUILD_TUPLE (grabbing the previous 2 elements placed on the stack) is generated. Python knows to create a tuple due to the commas being present.
You could alternatively specify another return type, for example a list, by using []. For this case, a BUILD_LIST is going to be issued following the same semantics as it's tuple equivalent:
>>> def foo_list(a, b):
... return [a, b]
>>> dis.dis(foo_list)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 BUILD_LIST 2
9 RETURN_VALUE
The type of object returned really depends on the presence of brackets (for tuples () can be omitted if there's at least one comma). [] creates lists and {} sets. Dictionaries need key:val pairs.
To summarize, one actual object is returned. If that object is of a container type, it can contain multiple values giving the impression of multiple results returned. The usual method then is to unpack them directly:
>>> first_name, last_name = f.getName()
>>> print (first_name, last_name)
As an aside to all this, your Java ways are leaking into Python :-)
Don't use getters when writing classes in Python, use properties. Properties are the idiomatic way to manage attributes, for more on these, see a nice answer here.
From Python Cookbook v.30
def myfun():
return 1, 2, 3
a, b, c = myfun()
Although it looks like myfun() returns multiple values, a tuple is actually being created. It looks a bit peculiar, but it’s actually the comma that forms a tuple, not the parentheses
So yes, what's going on in Python is an internal transformation from multiple comma separated values to a tuple and vice-versa.
Though there's no equivalent in java you can easily create this behaviour using array's or some Collections like Lists:
private static int[] sumAndRest(int x, int y) {
int[] toReturn = new int[2];
toReturn[0] = x + y;
toReturn[1] = x - y;
return toReturn;
}
Executed in this way:
public static void main(String[] args) {
int[] results = sumAndRest(10, 5);
int sum = results[0];
int rest = results[1];
System.out.println("sum = " + sum + "\nrest = " + rest);
}
result:
sum = 15
rest = 5
Here It is actually returning tuple.
If you execute this code in Python 3:
def get():
a = 3
b = 5
return a,b
number = get()
print(type(number))
print(number)
Output :
<class 'tuple'>
(3, 5)
But if you change the code line return [a,b] instead of return a,b and execute :
def get():
a = 3
b = 5
return [a,b]
number = get()
print(type(number))
print(number)
Output :
<class 'list'>
[3, 5]
It is only returning single object which contains multiple values.
There is another alternative to return statement for returning multiple values, use yield( to check in details see this What does the "yield" keyword do in Python?)
Sample Example :
def get():
for i in range(5):
yield i
number = get()
print(type(number))
print(number)
for i in number:
print(i)
Output :
<class 'generator'>
<generator object get at 0x7fbe5a1698b8>
0
1
2
3
4
Python functions always return a unique value. The comma operator is the constructor of tuples so self.first_name, self.last_name evaluates to a tuple and that tuple is the actual value the function is returning.
Whenever multiple values are returned from a function in python, does it always convert the multiple values to a list of multiple values and then returns it from the function??
I'm just adding a name and print the result that returns from the function.
the type of result is 'tuple'.
class FigureOut:
first_name = None
last_name = None
def setName(self, name):
fullname = name.split()
self.first_name = fullname[0]
self.last_name = fullname[1]
self.special_name = fullname[2]
def getName(self):
return self.first_name, self.last_name, self.special_name
f = FigureOut()
f.setName("Allen Solly Jun")
name = f.getName()
print type(name)
I don't know whether you have heard about 'first class function'. Python is the language that has 'first class function'
I hope my answer could help you.
Happy coding.
mentioned also here, you can use this:
import collections
Point = collections.namedtuple('Point', ['x', 'y'])
p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2
In this case, I want that the program print "X = changed"
class Clase:
def __init__(self,variable):
self.var = variable
def set_var(self):
self.var = 'changed'
X = 'unchanged'
V = Clase(X)
V.set_var()
print "X = ",X
All values are objects and are passed by reference in Python, and assignment changes the reference.
def myfunc(y):
y = 13
x = 42 # x now points at the integer object, 42
myfunc(y) # inside myfunc, y initially points to 42,
# but myfunc changes its y to point to a
# different object, 13
print(x) # prints 42, since changing y inside myfunc
# does not change any other variable
It's important to note here that there are no "simple types" as there are in other languages. In Python, integers are objects. Floats are objects. Bools are objects. And assignment is always changing a pointer to refer to a different object, whatever the type of that object.
Thus, it's not possible to "assign through" a reference and change someone else's variable. You can, however, simulate this by passing a mutable container (e.g. a list or a dictionary) and changing the contents of the container, as others have shown.
This kind of mutation of arguments through pointers is common in C/C++ and is generally used to work around the fact that a function can only have a single return value. Python will happily create tuples for you in the return statement and unpack them to multiple variables on the other side, making it easy to return multiple values, so this isn't an issue. Just return all the values you want to return. Here is a trivial example:
def myfunc(x, y, z):
return x * 2, y + 5, z - 3
On the other side:
a, b, c = myFunc(4, 5, 6)
In practice, then, there is rarely any reason to need to do what you're trying to do in Python.
In python list and dict types are global and are passed around by reference. So if you change the type of your variable X to one of those you will get the desired results.
[EDIT: Added use case that op needed]
class Clase:
def __init__(self,variable):
self.var = variable
def set_var(self):
self.var.test = 'changed'
class ComplicatedClass():
def __init__(self, test):
self.test = test
X = ComplicatedClass('unchanged')
print('Before:', X.test)
V = Clase(X)
V.set_var()
print("After:",X.test)
>>> Before: unchanged
>>> After: changed
strings are immutable so you could not change X in this way
... an alternative might be reassigning X in the global space... this obviously will fail in many many senarios (ie it is not a global)
class Clase:
def __init__(self,variable):
self.var = variable
def set_var(self):
globals()[self.var] = 'changed'
X = 'unchanged'
V = Clase('X')
V.set_var()
print "X = ",X
the other alternative is to use a mutable data type as suggested by Ashwin
or the best option is that this is probably not a good idea and you should likely not do it...
I am new to python and i am not sure how this is working. Code is as below:
class test():
d=0
def __init__(self):
self.d=self.d+1;
D=test()
print D.d
D1=test()
print D1.d
D2=test()
print D2.d
Output is
1,1,1 # This should not be
Now using this :
class test():
d=[]
def __init__(self):
self.d.apend("1");
D=test()
print D.d
D1=test()
print D1.d
D2=test()
print D2.d
Result is (This should be)
['1']
['1', '1']
['1', '1', '1']
So i am not sure why integer value is not being treated as class variable while list is being treated.
In the first example,
self.d = self.d + 1
rebinds self.d, making it independent of test.d.
In the second example,
self.d.append("1")
modifies test.d.
To see that for yourself, print id(self.d) at the end of both constructors.
If you modified the second example to match the first:
self.d = self.d + ["1"]
you'd see that the behaviour would also change to match.
If you want to modify a class variable, do:
class test(object):
d=0
def __init__(self):
type(self).d=self.d+1;
D=test()
print D.d
D1=test()
print D1.d
D2=test()
print D2.d
You don't need the type on the right hand side of the assignment, because this way you never create an instance variable d. Note that new-style classes are necessary to this.
type is a function (actually a callable - it is also a class; but don't worry about that for now) which returns the class of its argument. So, type(self) returns the class of self. Classes are first class objects in Python.
Demo here: http://ideone.com/JdNpiV
Update: An alternative would be to use a classmethod.
To address a class variable use class_name.variable_name, giving :
class test(object):
d=0
def __init__(self):
test.d = test.d + 1;
NPE's answer tells you what is going wrong with your code. However, I'm not sure that it really tells you how to solve the issue properly.
Here's what I think you want, if each test instance should have a different d value in an instance variable:
class test(object): # new style class, since we inherit from "object"
_d = 0 # this is a class variable, which I've named _d to avoid confusion
def __init__(self):
self.d = test._d # assign current value of class variable to an instance variable
test._d += 1 # increment the class variable
Now, you can create multiple instances and each one will get a unique value for d:
>>> D0 = test()
>>> D1 = test()
>>> D2 = test()
>>> print D0.d
0
>>> print D1.d
1
>>> print D2.d
2