This popular question addresses setting instance attributes with keyword arguments. However, I'd like to construct a class whose instances all have the same attributes based on some dictionary. How can this be achieved?
Here's my attempt. It seems I haven't quite understood something about class definitions.
d = {'x': 1, 'y': 2}
# Here's what I'd like to do
class A:
__dict__ = d
# Answer from the linked question that works
class B:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
a = A()
b = B(**d)
# print(A.x) # Has no attribute x
# print(a.x) # Has no attribute x
print(b.x)
This is curious, because both a.__dict__ and b.__dict__ return the same thing.
The type function can take arguments that allow it to create a new class dynamically:
B = type('B', (), d)
b = B()
print(b.x, b.y)
Output:
1 2
The first argument is the name of the generated class. The second is a tuple containing its base classes. In particular, the following two snippets are (roughly) equivalent:
class D(A, B, C):
pass
and:
D = type('D', (A, B, C), {})
The last argument is a dict mapping names to attributes (both methods and values).
When going from one API to another it can sometimes be helpful to map between similar keywords in each API, allowing one controller API to flexibly dispatch to other libraries without needing the user to fuss around with the different API's under the hood.
Assume some library, other_api, has a method called "logarithm", and the keyword argument for the base is something I need to factor out of my code, like "log_base_val"; so that to use it from other_api I need to type (for example):
other_api.logarithm(log_base_val=math.e)
Consider a toy class like this:
import other_api
import math
import functools
class Foo(object):
_SUPPORTED_ARGS = {"base":"log_base_val"}
def arg_binder(self, other_api_function_name, **kwargs):
other_api_function = getattr(other_api, other_api_function_name)
other_api_kwargs = {_SUPPORTED_ARGS[k]:v for k,v in kwargs.iteritems()}
return functools.partial(other_api_function, **other_api_kwargs)
With Foo, I can map some other API, where this argument is always called base, like this:
f = Foo()
ln = f.arg_binder("logarithm", base=math.e)
and ln is logically equivalent to (with log_base_val=math.e in kwargs, from functools):
other_api.logarithm(*args, **kwargs)
However, manually making the same argument bind by invoking functools will lead to different function objects:
In [10]: import functools
In [11]: def foo(a, b):
....: return a + b
....:
In [12]: f1 = functools.partial(foo, 2)
In [13]: f2 = functools.partial(foo, 2)
In [14]: id(f1)
Out[14]: 67615304
In [15]: id(f2)
Out[15]: 67615568
So testing for f1 == f2 won't succeed as intended:
In [16]: f1 == f2
Out[16]: False
So the question is: what is the prescribed way to test whether the argument binding function has resulted in the correct output function object?
The func attribute on the partial() object is a reference to the original function object:
f1.func is f2.func
Function objects themselves don't implement a __eq__ method, so you may as well just use is to test for identity.
Similarly, the partial().args and partial().keywords contain the arguments and keyword arguments to be passed to the function when called.
Demo:
>>> from functools import partial
>>> def foo(a, b):
... return a + b
...
>>> f1 = partial(foo, 2)
>>> f2 = partial(foo, 2)
>>> f1.func is f2.func
True
>>> f1.args
(2,)
>>> f2.args
(2,)
>>> f1.keywords is None
True
>>> f2.keywords is None
True
>>> 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
class Test:
def c(self, args):
print args
def b(self, args):
args.append('d')
def a(self):
args = ['a', 'b', 'c']
self.b(args)
self.c(args)
Test().a()
Why doesn't this print ['a', 'b', 'c']?
When you pass a list to a function, you're really passing it a pointer to the list and not a copy of the list. So b is appending a value to the original args, not its own local copy of it.
The parameter you pass to methods b and c is a reference to the list args, not a copy of it. In method b, you append to the same list you created in method a.
See this answer for a more detailed explanation on parameter passing in 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.