Subclassing frozenset and set doesn't seem to work the same when it comes to iterables. Try to run the following MWE:
class MonFrozenSet(frozenset):
def __new__(self, data):
super(MonFrozenSet,self).__init__(data)
return self
class MonSet(set):
def __init__(self, data):
super(MonSet,self).__init__(data)
x=(1,2,3,4)
A=MonSet(x)
B=MonFrozenSet(x)
for y in A: #Works
print y
for y in B: #Doesn't work
print y
The second for returns:
for y in B:
TypeError: 'type' object is not iterable
Any idea on how I can solve this?
If you are asking yourselves why I would like to use frozenset, the anwer is that I am trying to create a set of sets of tuples. The sets of tuples will be frozenset and the set of sets of tuples will be a set.
I use Python-2.7
When overriding __new__ you need to call the superclass's __new__, not its __init__. Also, you need to pass self (better named cls), since __new__ is a classmethod. Also, you need to return the result, since __new__ actually creates an object, it doesn't modify self. So:
class MonFrozenSet(frozenset):
def __new__(cls, data):
return super(MonFrozenSet,cls).__new__(cls, data)
Then:
>>> a = MonFrozenSet([1, 2, 3])
>>> for item in a:
... print item
1
2
3
Related
Is there a way to enable using the standard type constructors such as int, set, dict, list, tuple, etc. to coerce an instance of a user-defined class to one of those types in a user-defined way? For example
class Example:
def __init__(self):
self.a=1
self.b=2
and then having
>>> ex = Example()
>>> dict(ex)
{"a":1, "b":2}
I don't know if that's possible, and if it is, what I would need to add in the class definition. Right now I need this and I implement a "as_dict" method which I call on the object, but it doesn't look as natural.
Make your type iterable by adding an __iter__() method. Trivially:
class Example:
def __init__(self):
self.a = 1
self.b = 2
def __iter__(self):
yield "a", self.a
yield "b", self.b
This yields a sequence of tuples containing name/value pairs, which dict() is happy to consume.
dict(Example()) # {'a': 1, 'b': 2}
Of course, there's a lot of repeating yourself in there. So you could instead write __iter__() to work with a predefined list of attributes:
def __iter__(self):
names = "a", "b"
for name in names:
yield name, getattr(self, name)
You could also have it introspect all the attributes from the instance, omitting attributes whose values are callable:
def __iter__(self):
names = dir(self)
for name in names:
value = getattr(self, name)
if not callable(value):
yield name, value
Or have it yield from the instance's __dict__ attribute, which contains only the attributes stored directly on the instance (the dir() method above also finds inherited attributes):
def __iter__(self):
yield from self.__dict__.items()
You need to make your object iterable.
Both list and tuple accept an iterable as their argument, which they will repeatedly consume until to construct the new collection. To enable your class to work with this mechanism, you will need to define at least the __iter__ method, and possibly also the __next__ method, depending on the specific semantics of your class.
Your implementation of __iter__ will need to return an object that implentents the iterator protocol. If you internally use an iterable collection, this can be as simple as returning that collection.
In your case, it seems you want your object to behavior like an iterable collection of tuples. A possible implementation for the behavior you're looking for would be
class Example:
def __init__(self):
self.a=1
self.b=2
def __iter__(self):
for elem in ('a', 'b'):
yield (elem, getattr(self, elem))
>>> dict(Example())
{'a': 1, 'b': 2}
Here, we make use of a generator to produce an iterable that will yield the tuples ('a', self.a) and ('b', self.b) in turn.
Is there a way to enable using the standard type constructors such as int, set, dict, list, tuple, etc. to coerce an instance of a user-defined class to one of those types in a user-defined way? For example
class Example:
def __init__(self):
self.a=1
self.b=2
and then having
>>> ex = Example()
>>> dict(ex)
{"a":1, "b":2}
I don't know if that's possible, and if it is, what I would need to add in the class definition. Right now I need this and I implement a "as_dict" method which I call on the object, but it doesn't look as natural.
Make your type iterable by adding an __iter__() method. Trivially:
class Example:
def __init__(self):
self.a = 1
self.b = 2
def __iter__(self):
yield "a", self.a
yield "b", self.b
This yields a sequence of tuples containing name/value pairs, which dict() is happy to consume.
dict(Example()) # {'a': 1, 'b': 2}
Of course, there's a lot of repeating yourself in there. So you could instead write __iter__() to work with a predefined list of attributes:
def __iter__(self):
names = "a", "b"
for name in names:
yield name, getattr(self, name)
You could also have it introspect all the attributes from the instance, omitting attributes whose values are callable:
def __iter__(self):
names = dir(self)
for name in names:
value = getattr(self, name)
if not callable(value):
yield name, value
Or have it yield from the instance's __dict__ attribute, which contains only the attributes stored directly on the instance (the dir() method above also finds inherited attributes):
def __iter__(self):
yield from self.__dict__.items()
You need to make your object iterable.
Both list and tuple accept an iterable as their argument, which they will repeatedly consume until to construct the new collection. To enable your class to work with this mechanism, you will need to define at least the __iter__ method, and possibly also the __next__ method, depending on the specific semantics of your class.
Your implementation of __iter__ will need to return an object that implentents the iterator protocol. If you internally use an iterable collection, this can be as simple as returning that collection.
In your case, it seems you want your object to behavior like an iterable collection of tuples. A possible implementation for the behavior you're looking for would be
class Example:
def __init__(self):
self.a=1
self.b=2
def __iter__(self):
for elem in ('a', 'b'):
yield (elem, getattr(self, elem))
>>> dict(Example())
{'a': 1, 'b': 2}
Here, we make use of a generator to produce an iterable that will yield the tuples ('a', self.a) and ('b', self.b) in turn.
I understand that there are magic methods in python that can be overwritten by classes to control the way certain built in functions treat the members of these classes. For example, the behavior of len() and str() can be overwritten via magic methods __len__() and __str__():
class EmptySet(object):
def __len__(self):
return 0
def __str__(self):
return '[]'
>>> e = EmptySet()
>>> str(e)
[]
>>> len(e)
0
There are also __cmp__() and __ge__(), __le__() etc methods to control how these objects can be compared and how a list of them should be ordered by list.sort(). My question is not about customizing the ordering of objects in a list but about sorting the object itself. Suppose the set weren't empty and I want to use sorted() to sort it:
class SetOfTwo(object):
def __init__(self, a , b):
el_0 = a
el_1 = b
def __len__(self):
return 2
def __str__(self):
return '[{}, {}]'.format(el_0, el_1)
Is there a magic method I can implement to have sorted() flip the elements if they aren't in order? I'm picturing the following behavior:
>>> s = SetOfTwo(2, 1)
>>> str(s)
[2, 1]
>>> t = sorted(s)
>>> str(t)
[1, 2]
>>> type(t)
>>> SetOfTwo
You should definitely read the official documentation of how to emulate container types. Basically a class supposed to work as a container (list, dict etc.) needs to implement methods to set or get members __getitem__(), __setitem__() and iterate over items __iter__() and to get the number of items - method __len__(). This is the minimum. But you can also add the ability to delete items and other operations.
The behaviour of sorted() built-in function is to iterate over elements of your container and compare them using methods you mentioned __cmp__(), __ge__(), __le__() which should be defined for items and not the container as you know already. Then a new list instance is created with items sorted and this new instance is returned. You can pass it to the constructor of your custom container then or you can wriap sorted() with a custom function which will return the desired class instance.
As some have said in the comments, sets are unordered but I don't think your question is really about sets.
Python uses the data model methods you mentioned, ge, le, and cmp to determine how a class behaves when sorted() is called on it. You can see how I try to call it here, but Python objects and asks me to implement <.
>>> class a(object):
... pass
...
>>> b = a()
>>> c = a()
>>> d = [b, c]
>>> sorted(d)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'a' and 'a'
Hope this helps. Aslo, as other people said, it's a good idea to subclass something in collections.abc. I'd read Item 28 in effective python that talks about this to get a good idea.
len() and str() are functions who take an object as parameter and return an integer (resp. string). The object can personalize the way the len is calculated, or the string is generated, via the __len__() and __str__() magic methods.
Similarly, sorted() is a function that takes a list (or any iterable) of objects and returns a list of the sorted objects. The objects can personalize the way they get compared through the __lt__() magic method.
Some confusion arises when we think of `sorted(my_list) as a function that "sorts the list", rather than "sorts the elements of the list".
You don't want to sort your objects (i.e. make an ordered list of objects), but only sort some data in their internal representation. So you need an instance method on your object that will update that internal representation. You can name it as you wish, .sort() if you'd like, but you will have to call it on your one object, and it will not be involved in comparing objects.
You have to implement comparison operator magic methods. Python will automatically take care of the sort and sorted.
class Edge:
def __init__(self,source, dest, weight=float('inf')):
self.source = source
self.dest = dest
self.weight = weight
def __repr__(self):
return f"Edge: ({self.source}, {self.dest}, {self.weight})"
def __lt__(self, other):
return self.weight < other.weight
e1 = Edge(0, 1, 2)
e2 = Edge(1, 2, 3)
e3 = Edge(2, 3, 10)
e4 = Edge(2, 4, 10)
e5 = Edge(2, 4, 0)
l = [e1, e3, e4, e2, e5]
print(l)
print(e3 > e2)
print(e3 == e4)
print(sorted(l))
Take this super simple class:
class Foo():
def __init__(self, iden):
self.iden = iden
def __hash__(self):
return hash(self.iden)
def __repr__(self):
return str(self.iden)
The goal is to create instances of the class to use as dict keys. If __repr__ is omitted, the keys are the standard object address. With __repr__ a printable representation might be:
f = Foo(1)
g = Foo(2)
d = {f:'a', g:'b'}
print(d)
>>> {1:'a', 2:'b'}
When attempting to access the dict by key though, it does not appear to be immediately obvious how to utilize the __repr__ (or __str__ for that matter) representation as the key.
print(d[1])
>>> KeyError
First thing's first: __repr__() is a red herring. It only affects how the object is displayed. It has nothing to do with what you're trying to do.
If you want to have two separate objects refer to the same slot in a dict, you need two things (reference):
The objects must have the same hash (hash(obj1) == hash(obj2)).
The objects must compare equal (obj1 == obj2).
Your above implementation does the former, but not the latter. You need to add an __eq__() method (which is actually required by the documentation when you define __hash__(), anyway).
class Foo():
def __init__(self, iden):
self.iden = iden
def __hash__(self):
return hash(self.iden)
def __eq__(self, other):
return self.iden == other
>>> d = {Foo(1) : 'a'}
>>> d[1]
'a'
I would like to map a method over a list of objects instantiating different classes. All the objects inherit from the same base class and define a method of the desired name.
To make it clearer consider the following code:
class A:
def __init__(self, x):
self.x = x
def foo (self):
return self.x
class B(A):
def foo (self):
return self.x+1
class C(A):
def foo (self):
return self.x-1
Now consider a list of objects instantiating the classes B and C. I would like to do something like that:
result = []
for obj in [B(1), C(1)]:
result.append(obj.foo())
How would you proceed to map the method foo on each element of the list? Is it at all possible? The best I could come up with is something like that:
map(A.foo, [B(1), C(1)])
but clearly it doesn't return my desired result. How can I specify the method related to the object?
I hope I made myself clear.
NB: I work primarily with Python2.7, but I would equally be interested in solutions valid for "newer" versions.
Map(A.foo, [B(1), C(1)]) is basically doing A.foo(B(1)) and A.foo(C(1)) which isn't what you are looking for.
Using your classes from above, I would just do:
In: objs = [B(1), C(1)]
In: [x.foo() for x in objs]
Out: [2, 0]
Amjith has a pure map implementation if you'd prefer that.
>>> map(lambda x: x.foo(), [B(1), C(1)])
>>> [2, 0]
The lambda function will take each object in the list and call foo() on that object. Thus the resulting list will have the results returned by the corresponding object's foo().
For most practical purposes, I'd recommend #AlG's list comprehension, but you can do this with map as well:
>>> import operator
>>> map(operator.methodcaller("foo"), [B(1), C(1)])
[2, 0]