Map with no arguments and class method? - python

It seems pretty simple:
# builtins work fine:
>>> map (str, [(), (), ()])
['()', '()', '()']
# but no luck for class methods:
>>> class C (object):
... def m(self):
... return 42
...
>>> c = C()
>>> map(c.m, [(), (), ()])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: m() takes exactly 1 argument (2 given)

You need to add a parameter to your m method, where the argument of the map will be passed.
class C (object):
def m(self, x):
return 42
>>> c = C()
>>> map(c.m, [(), (), ()])
[42, 42, 42]
See, c.m is a bound method, already like calling m(c), you need a placeholder for the additional parameter passed by map
c and the argument passed by map are the 2 arguments to m your stack trace is complaining about:
TypeError: m() takes exactly 1 argument (2 given)

map(f, L) always calls f with a single argument whose values are taken from L. It's always a single argument, never zero. The ()s in the list are not argument lists, they are empty tuples. Outside of a function call, things in parentheses aren't arguments to a function, they are objects called "tuples" (think of them as immutable lists). Check the difference between str() and str(()) - str with no arguments gives '' and not '()'.
If you have tuples of arguments and want to call a callable (function or method) with these arguments, you can use itertools.starmap. In particular, if you pass empty tuples the functions will be called with no arguments. It returns an iterator, so if you need a list you need to explicitly use list() over the result
>>> import itertools
>>> f = lambda: 42
>>> L = [(), (), ()]
>>> values = itertools.starmap(f, L)
>>> print list(values)
[42, 42, 42]
In the general case, it works with any tuple of arguments:
>>> f = lambda *x: sum(x)
>>> L = [(1,2), (4, ), (5,6)]
>>> values = itertools.starmap(f, L)
>>> print list(values)
[3, 4, 11]
If you want to simply call a function multiple times and get the result, you might consider using a list comprehension or a generator expression instead.
>>> f = lambda: 42
>>> [f() for _ in xrange(3)]
[42, 42, 42]
>>> values = (f() for _ in xrange(3))
>>> print list(values)
[42, 42, 42]
If you have a list of empty tuples like in your example, you might use xrange(len(L)) in the place of xrange(3).

That's not a class method, it lacks the classmethod decorator and self should be cls. But you don't want a class method here anyway, as class methods are methods which operate on classes (you can pass other objects, of course, but that's not the intended use case - the #classmethod would be grossly misleading).
You're looking for the term "unbound method", which you get by refering to a member of the class, not to an instance thereof. Use C.m. Note of course that the method will be called with self as (in your example) a tuple and not an instance of C. Normally, such trickery should be restricted to avoid this (e.g. str.lower and a bunch of strings is O.K.).

First, that's not a class method. A class method takes the class as it's first argument, and is called on the class, not an instance of it:
class C(object):
#classmethod
def m(cls):
return 42
map(C.m, range(10))
However, that will still break because map passes in each item from the iterable to the function, and your method only accepts one argument, the class.
If you change your method to accept the extra argument (def m(cls, arg)), it will work. You could also use an instance method instead of a class method:
class C(object):
def m(self, *args): # or def m(self, n)
return 42
c = C()
map(c.m, range(10))

You forgot your decorator to make it a class method, but you probably want a static method:
static:
class C(object):
#staticmethod
def m(arg):
return 42
class:
class C(object):
#classmethod
def m(cls, arg):
#cls is a reference to the actual "C" type, not an instance of the "C" class.
return 42

m is a 1-argument method that takes an object of type C. The syntax "c.m" is actually equivalent to "m(c)", which is just 42. But 42 is not a function you can map over a list like [(),(),()].
The following should work:
class C (object):
def f(self): return lambda x: x+1
two,three,four = map(C().f(), [1,2,3])
Now C().* returns a function, instead of a constant.

Related

is there a magic method for sorted() in Python?

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

python the difference between class(dict) and class(str)

I am wondering what kind of difference exists between class(dict) and class(str)
Here is my code
class MyDict3(str):
def __init__(self):
self.a = None
class MyDict(dict):
def __init__(self):
self.a = None
These classes are what I made for clarification
and then I type below
>>> mydict['a'] = 1
>>> mydict
{'a': 1}
>>> mydict3['a'] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'MyDict3' object does not support item assignment
Why does my mydict3['a'] make an error?
The difference that I made is only MyDict(dict) and MyDict(str)
As far as I know, the object that I specified(dict, str) is just nothing but constructer like c++,java
Please give me a clear answer on that.
Why does my mydict3['a'] make an error? The difference that I made is only MyDict(dict) and MyDict(str) As far as I know, the object that I specified(dict, str) is just nothing but constructer like c++,java
I believe that you're doing a confusion here, thinking that a class attribute and an item are the same thing, like the following javascript code:
> foo = {'a': 42};
{ a: 42 }
> foo.a
42
> foo['a']
42
> foo.a === foo['a']
true
But in python foo.a and foo['a'] are two different mechanisms. When you call foo.a you're actually accessing the a attribute of a class, which is defined through the class definition:
class Foo:
def __init__(self):
self.a = 42 # declaring and defining the a attribute
so then you can access it using:
>>> foo = Foo()
>>> print(foo.a)
42
But to have foo['a'] working, you have to use the indexing mechanism, which is usually used for dicts or lists:
>>> foo = {'a': 42}
>>> foo['a']
42
That mechanism is being implemented by the __getitem__ method of your class, so you can overload it if you want:
class Foo:
def __getitem__(self, val):
if val == 'a':
return 42
raise KeyError('Unknown key') # when the key is unknown, you raise a key error exception
>>> foo = Foo()
>>> foo['a']
42
>>> foo['b']
KeyError: 'Unknown key'
So, the dict class is a class that implements __getitem__ (and __setitem__ and many others), in order to provide you a proper mapping mechanism called a dictionary. There keys can be any immutable objects, and values anything. For a list, it shall be only integers (which are the positions in the list).
That being said, let's answer your question:
Why does my mydict3['a'] make an error?
obviously it's because you defined mydict3 as being an implementation of a string, which has a special implementation for the __getitem__ method: it's giving you a character at the parameter position like if the list was a list of character (like in C).
So when you're trying to index mydict3 with 'a', python just tells you that what you're asking makes no sense!
So in the end, when you say:
The difference that I made is only MyDict(dict) and MyDict(str)
it's actually a very big difference! A dict and an str do not have the same interface, and thus what you want to do cannot work!
P.S.: Actually, nothing is black or white. The implementation of a class actually is a dict, and you can access all members of a class' instance through the __dict__ member of an instance:
class Foo():
def __init__(self):
self.a = 42
>>> foo = Foo()
>>> foo.__dict__['a']
42
but you shall never directly access the __dict__ instance directly, and use helper functions setattr and getattr:
>>> setattr(foo, 'b', 42)
>>> getattr(foo, 'b')
42
>>> getattr(foo, 'a')
42
This is some advanced python tricks, and they should be use with care. If there's really no other way to do it, then maybe you should use that.
Also, there exists a special class that transform dict items as class members, it's the namedtuple:
>>> from collections import namedtuple
>>> d = {'a': 42, 'b': 69}
>>> SpecialDict = namedtuple('SpecialDict', d.keys())
>>> foo = SpecialDict(**d)
>>> d.a
42
>>> d.b
69
HTH

Python: Subclassing frozenset not iterable?

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

tuple index when invoke with function with dynamic arguments

>>> class Test(object):
>>> def test(self,*arg):
>>> print(arg[0],arg[1])
>>> p = Test()
>>> t = 2,3
>>> p.test(t)
gives me IndexError: tuple index out of range
why is that? and how do i get the value for that tuple?
You passed in just one argument (the whole tuple (2, 3)), so only arg[0] exists; if you meant the tuple values to be separate arguments, apply them with the *args call syntax:
p.test(*t)
The alternative is to not use the *arg catchall argument in your function definition:
def test(self, arg):
Now your function has two normal positional arguments, self and arg. You can only pass in one argument, and if that is your tuple, arg[0] and arg[1] will work as expected.
Using your demo class:
>>> class Test(object):
>>> def test(self,*arg):
>>> print(arg[0],arg[1])
When doing this:
>>> p = Test()
>>> t = 2,3
>>> p.test(t)
arg will have a value of [(1,2),]
When doing this:
>>> p = Test()
>>> t = 2,3
>>> p.test(*t)
arg will have a value of [1,2]
The * in the function means that all remaining arguments (non-keyword) are put into a list for you.
In the first case you send (1,2) has a single argument. In the second case the tuple is made into individual arguments using the * thus you send in 1 and 2.
For complete documentation on this refer to this Python article:
http://docs.python.org/2/reference/expressions.html#calls

map a method over list of different objects

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]

Categories