I went through some questions but could not find one that really helps.
Lets say I have a list of objects
[[Cheese(3), Cheese(2), Cheese(1)], []]
and I need to write a function that will find the index of Cheese(1) in there
I tried this:
def location (search):
return self.list.index(Cheese(1))
Which didnt work, I thought list.index(search) returns the index of the searched item in a list?
For the above list the index should have been list[0][2] for Cheese(1)
You need to do two things:
Give your Cheese() class an __eq__ method so that Python knows when two instances hold the same value:
class Cheese(object):
def __init__(self, id):
self.id = id
def __eq__(self, other):
if isinstance(other, Cheese):
return self.id == other.id
return NotImplemented
With this implementation, two Cheese() instances are equal if they have the same id value.
Without __eq__ two references to Cheese() instances are only equal if it concerns the same object (identity).
list.index() does not search nested lists; you need to do so explicitly:
search = Cheese(1)
try:
return next((i, sublist.index(search)) for i, sublist in enumerate(self.list) if search in sublist)
except StopIteration:
raise IndexError('{} not found in the list'.format(Cheese(1))
would return a tuple with 2 indices into the outer and inner lists, representing the first location where Cheese(1) is found.
Demo:
>>> class Cheese(object):
... def __init__(self, id):
... self.id = id
... def __eq__(self, other):
... if isinstance(other, Cheese):
... return self.id == other.id
... return NotImplemented
...
>>> Cheese(1) == Cheese(1)
True
>>> Cheese(1) == Cheese(2)
False
>>> lst = [[Cheese(3), Cheese(2), Cheese(1)], []]
>>> next((i, sublist.index(Cheese(1))) for i, sublist in enumerate(lst) if Cheese(1) in sublist)
(0, 2)
Related
What magic method do I have to modify to support the in operator. Here's an example of what I'm trying to do:
class DailyPriceObj:
def __init__(self, date, product_id=None):
self.date = date
self.product_id = product_id
self.sd_buy = None
l = list()
l.append(DailyPriceObj(date="2014-01-01"))
DailyPriceObj(date="2014-01-01") in l # how to get this to return True?
In other words, I want my object to "act like" the date property, so I can use that to see if that obj is in an interable (date should be a unique field here).
You need to implement __eq__ (and __hash__ for the sake of completeness):
class DailyPriceObj:
def __init__(self, date, product_id=None):
self.date = date
self.product_id = product_id
self.sd_buy = None
def __eq__(self, other):
return isinstance(other, self.__class__) and self.date == other.date
def __hash__(self):
return hash(self.date)
l = [DailyPriceObj(date="2014-01-01")]
s = {DailyPriceObj(date="2014-01-01")}
print(DailyPriceObj(date="2014-01-01") in l)
print(DailyPriceObj(date="2014-01-01") in s)
Output
True
True
From the documentation on __hash__:
Called by built-in function hash() and for operations on members of
hashed collections including set, frozenset, and dict. __hash__()
should return an integer. The only required property is that objects
which compare equal have the same hash value; it is advised to mix
together the hash values of the components of the object that also
play a part in comparison of objects by packing them into a tuple and
hashing the tuple.
You can implement __eq__ in such a way that both two ways of checking will work:
class DailyPriceObj:
def __init__(self, date, product_id=None):
self.date = date
self.product_id = product_id
self.sd_buy = None
def __eq__(self, other):
return self.date == other
l = list()
l.append(DailyPriceObj(date="2014-01-01"))
# both ways work:
print(DailyPriceObj(date="2014-01-01") in l) # True
print("2014-01-01" in l) # True
How can I reuse the python "smart" reporting for objects?
E.g.:
class Foo(object):
def __init__(self, text, sequence):
self.text = text
self.sequence = sequence
def __eq__(self, other):
return type(self) == type(other) and self.text == other.text and self.sequence == other.sequence
def __ne__(self, other):
return not self.__eq__(other)
What I want is to get the assertion report, just as I would assert the primitive fields, e.g. (of course makes sense to not print attributes that satisfy the comparison operator - and concatenate together the failing attribute reports). Something like:
def test_magic():
> assert Foo(text='a', sequence=[1]) == Foo(text='a', sequence=[1, 2])
E assert Foo(text='a', sequence=[1]) == Foo(text='a', sequence=[1, 2])
E attribute `sequence`:
E [1] == [1, 2]
E Right contains more items, first extra item: 2
E Use -v to get the full diff
Can I do this by writing some plugin, etc and calling in that the internal comparator? Or what is the recommended solution?
In building a class with an out line like below I would like the behaviour of the for loops to, if done once: just give the keys as normal an then move on to the next line of code. But if a second loop is set up inside the first loop it would give the keys on the first loop and then ea value in the sequences in the second loop. The problem I can't figure out is how to set up this under iter.
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
def __iter__(self):
pass # ???????
Something like this:
dct = dict(container1=[5,6,7,8], container2=('a','b','c')
if one loop is used:
for ea in dct:
print(ea)
print("Howdy")
'containter1'
'containter2'
Howdy
If a nest loop is used:
for ea in dct:
print(ea)
for i in dct.get(ea):
print(i)
'container1'
5
6
...
'container2'
a
b
c
To answer your immediate question, you could just copy how dictionaries implement dict.get and dict.__iter__:
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
def __iter__(self):
for attr in dir(self):
if not attr.startswith('_') and attr != 'get':
yield attr
def get(self, key):
return getattr(self, key)
It's not a very good approach, however. Looking at the attributes of your object at runtime isn't a good idea, because it will break when you subclass and it will add needless complexity. Instead, just use a dictionary internally:
class MyClass():
def __init__(self):
self.container = {
'cont1': [1, 2, 3, 4],
'cont2': ('a', 'b', 'c')
}
def __iter__(self):
return iter(self.container)
def get(self, key):
return self.container.get(key)
You can do this with a second class like this
class MyClass():
def __init__(self):
self.data = [MyClass2({'cont1' : [1,2,3,4]}),MyClass2({'cont2' : ('a','b','c')})]
def __iter__(self):
for item in self.data:
yield item
class MyClass2():
def __init__(self, mydict):
self.d = mydict
def __iter__(self):
for item in self.d.values():
for value in item:
yield value
def __repr__(self):
return(list(self.d.keys())[0])
m = MyClass()
for k in m:
print(k)
for val in k:
print(val)
You cannot do that simply by implementing __iter__. __iter__ should return an iterator, that is, an object that keeps the state of an iteration (the current position in a sequence of items) and has a method next that returns with each invocation the next item in the sequence.
If your object has nested sequences you can implement an iterator that will traverse only the external sequence, or one that will traverse both
the external and the internal sequences - in a depth-first or a breath-first fashion - but it does not make sense to use nested loops on the same iterable:
# iterate over every item in myobj
for x in myobj:
...
# iterate over every item again? not likely what you want!
for y in myobj:
A more likely situation is:
for x in myob:
...
for y in x:
...
How would you feel about this:
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
self.conts = {'container1':self.cont1, 'container2':self.cont2}
def __iter__(self):
return self.conts.iteritems()
dct = MyClass()
print('One loop')
for mi in dct:
print(mi)
print('='*40)
print('Nested loops')
for name, values in dct:
print(name)
for i in values:
print(i)
Which outputs:
One loop
container1
container2
========================================
Nested loops
container1
1
2
3
4
container2
a
b
c
Update
I don't know that I would really recommend this, but this seems to more closely fit what the OP wants:
class MyIterator(object):
def __init__(self, name, values):
self.vals = iter(values)
self.name = name
def __iter__(self):
return self.vals
def __str__(self):
return self.name
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
self.conts = [MyIterator('container1', self.cont1),
MyIterator('container2', self.cont2)]
def __iter__(self):
return iter(self.conts)
dct = MyClass()
for mi in dct:
print(mi)
for i in mi:
print(i)
This is the only way I can think of to be able to print the name and then iterate over it as the values list. This works by overriding the __str__ method to change how the object gets "stringified". But as I said earlier, I think you would be better served with the first part of the answer.
Sorry, just realized nauer's answer already showed something like this.
Borrowing the documentation from the __contains__ documentation
print set.__contains__.__doc__
x.__contains__(y) <==> y in x.
This seems to work fine for primitive objects such as int, basestring, etc. But for user-defined objects that define the __ne__ and __eq__ methods, I get unexpected behavior. Here is a sample code:
class CA(object):
def __init__(self,name):
self.name = name
def __eq__(self,other):
if self.name == other.name:
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
obj1 = CA('hello')
obj2 = CA('hello')
theList = [obj1,]
theSet = set(theList)
# Test 1: list
print (obj2 in theList) # return True
# Test 2: set weird
print (obj2 in theSet) # return False unexpected
# Test 3: iterating over the set
found = False
for x in theSet:
if x == obj2:
found = True
print found # return True
# Test 4: Typcasting the set to a list
print (obj2 in list(theSet)) # return True
So is this a bug or a feature?
For sets and dicts, you need to define __hash__. Any two objects that are equal should hash the same in order to get consistent / expected behavior in sets and dicts.
I would reccomend using a _key method, and then just referencing that anywhere you need the part of the item to compare, just as you call __eq__ from __ne__ instead of reimplementing it:
class CA(object):
def __init__(self,name):
self.name = name
def _key(self):
return type(self), self.name
def __hash__(self):
return hash(self._key())
def __eq__(self,other):
if self._key() == other._key():
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
This is because CA doesn't implement __hash__
A sensible implementation would be:
def __hash__(self):
return hash(self.name)
A set hashes it's elements to allow a fast lookup. You have to overwrite the __hash__ method so that a element can be found:
class CA(object):
def __hash__(self):
return hash(self.name)
Lists don't use hashing, but compare each element like your for loop does.
I'm trying to extend some "base" classes in Python:
class xlist (list):
def len(self):
return len(self)
def add(self, *args):
self.extend(args)
return None
class xint (int):
def add(self, value):
self += value
return self
x = xlist([1,2,3])
print x.len() ## >>> 3 ok
print x ## >>> [1,2,3] ok
x.add (4, 5, 6)
print x ## >>> [1,2,3,4,5,6] ok
x = xint(10)
print x ## >>> 10 ok
x.add (2)
print x ## >>> 10 # Not ok (#1)
print type(x) ## >>> <class '__main__.xint'> ok
x += 5
print type(x) ## >>> <type 'int'> # Not ok (#2)
It works fine in the list case because the append method modifies the object "in place", without returning it. But in the int case, the add method doesn't modify the value of the external x variable. I suppose that's fine in the sense that self is a local variable in the add method of the class, but this is preventing me from modifying the initial value assigned to the instance of the class.
Is it possible to extend a class this way or should I define a class property with the base type and map all the needed methods to this property?
Your two xint examples don't work for two different reasons.
The first doesn't work because self += value is equivalent to self = self + value which just reassigns the local variable self to a different object (an integer) but doesn't change the original object. You can't really get this
>>> x = xint(10)
>>> x.add(2)
to work with a subclass of int since integers are immutable.
To get the second one to work you can define an __add__ method, like so:
class xint(int):
def __add__(self, value):
return xint(int.__add__(self, value))
>>> x = xint(10)
>>> type(x)
<class '__main__.xint'>
>>> x += 3
>>> x
13
>>> type(x)
<class '__main__.xint'>
int is a value type, so each time you do an assignment, (e.g. both instances of += above), it doesn't modify the object you have on the heap, but replaces the reference with one of the result of the right hand side of the assignment (i.e. an int)
list isn't a value type, so it isn't bound by the same rules.
this page has more details on the differences: The Python Language Reference - 3. Data model
IMO, yes, you should define a new class that keeps an int as an instance variable
i expanded you xlist class just a bit, made it so you could find all index points of a number making it so you can extend with multiple lists at once making it initialize and making it so you can iterate through it
class xlist:
def __init__(self,alist):
if type(alist)==type(' '):
self.alist = [int(i) for i in alist.split(' ')]
else:
self.alist = alist
def __iter__(self):
i = 0
while i<len(self.alist):
yield self.alist[i]
i+=1
def len(self):
return len(self.alist)
def add(self, *args):
if type(args[0])==type([1]):
if len(args)>1:
tmp = []
[tmp.extend(i) for i in args]
args = tmp
else:args = args[0]
if type(args)==type(''):args = [int(i) for i in args.split(' ')]
(self.alist).extend(args)
return None
def index(self,val):
gen = (i for i,x in enumerate(self.alist) if x == val)
return list(gen)
Ints are immutable and you can't modify them in place, so you should go with option #2 (because option #1 is impossible without some trickery).
I wrote an example of a mutable integer class that implements some basic methods from the list of operator methods. It can print properly, add, subtract, multiply, divide, sort, and compare equality.
If you want it to do everything an int can you'll have to implement more methods.
class MutablePartialInt:
def __init__(self, value):
self.value = value
def _do_relational_method(self, other, method_to_run):
func = getattr(self.value, method_to_run)
if type(other) is MutablePartialInt:
return func(other.value)
else:
return func(other)
def __add__(self, other):
return self._do_relational_method(other, "__add__")
def __sub__(self, other):
return self._do_relational_method(other, "__sub__")
def __mul__(self, other):
return self._do_relational_method(other, "__mul__")
def __truediv__(self, other):
return self._do_relational_method(other, "__truediv__")
def __floordiv__(self, other):
return self._do_relational_method(other, "__floordiv__")
def __eq__(self, other):
return self._do_relational_method(other, "__eq__")
def __neq__(self, other):
return self._do_relational_method(other, "__neq__")
def __lt__(self, other):
return self._do_relational_method(other, "__lt__")
def __gt__(self, other):
return self._do_relational_method(other, "__gt__")
def __str__(self):
return str(self.value)
def __repr__(self):
return self.__str__()