Eric Brunel wrote: > Hi all, > > I just stepped on a thing that I can't explain. Here is some code > showing the problem: > > ----------------------------- > class C:
Do yourself a favour : use new-style classes. class C(object) > f = None > def __init__(self): > if self.f is not None: > self.x = self.f(0) > else: > self.x = 0 > > class C1(C): > f = int > > class C2(C): > f = lambda x: x != 0 > > o1 = C1() > print o1.x > > o2 = C2() > print o2.x > ----------------------------- > > Basically, I want an optional variant function across sub-classes of > the same class. > > I did it like in C1 for a start, then I needed > something like C2. The result is... surprising: > > 0 > Traceback (most recent call last): > File "func-vs-meth.py", line 18, in ? > o2 = C2() > File "func-vs-meth.py", line 5, in __init__ > self.x = self.f(0) > TypeError: <lambda>() takes exactly 1 argument (2 given) Not surprising at all. Functions implement the descriptor protocol[1]. When bound to a class and looked up via an instance, it's the __get__ method of the function object that get called - with the instance as param, as defined by the descriptor protocol. This method then return the function wrapped - with the instance - in an Method object - which itself, when called, returns the result of calling the function *with the instance as first parameter*. Which is how methods can work on the instance, and why one has to explicitly declare the instance parameter in "functions to be used as methods", but not explicitly pass it at call time. (please some guru correct me if I missed something here, but AFAIK it must be a correct enough description of method invocation mechanism in Python). [1] about descriptors, see: http://docs.python.org/ref/descriptors.html http://www.geocities.com/foetsch/python/new_style_classes.htm#descriptors > So the first works and o1.x is actually 0. int is not a function. >>> type(int) <type 'type'> int is a type. A Python type is a callable object, and act as a factory for instances of it. If the type doesn't implement the descriptor protocol, when bound to a class and looked up via an instance, normal lookup rules apply. So the type object is returned as is. In your case, since int does'nt implement the descriptor protocol, once looked up (and returned as is), it's called with a correct argument - so everything runs fine. Try this: class Obj(object): def __new__(cls, val, *args, **kw): print "in Obj.__new__" print "- called with :" print " cls :", cls print " val :", val print " args: %s" % str(args) print " kw : %s" % kw obj = object.__new__(cls, *args, **kw) print "got : %s - %s" % (obj, dir(obj)) return obj def __init__(self, *args, **kw): print "in Obj.__init__" print "- called with :" print " args: %s" % str(args) print " kw : %s" % kw class C4(C): f = Obj > But the second fails because > self is also being passed as the first argument to the lambda. Of course. It's a function, and it's bound to a class, and looked up via an instance of the class. Try this: def truc(*args, **kw): print "in truc()__" print "- called with :" print " args: %s" % str(args) print " kw : %s" % kw if len(args) > 1: return args[1] class C6(C): f = truc > Defining > a "real" function doesn't help: the error is the same. What' a "real" function ?-) lambdas *are* real functions. >>> type(lambda x: x) <type 'function'> >>> > My actual question is: why does it work in one case and not in the > other? cf above. > As I see it, int is just a function with one parameter, Nope, it's a type. Functions are just one kind of callable. Types are callables too, as are any object overloading the call operator - which is '()' - by implementing the __call__(self, ...) method. class NotAFunc(object): def __call__(self): print "I'm not a function" return 42 func = NotAFunc() func() > and the > lambda is just another one. True. And functions implement the descriptor protocol. > So why does the first work, and not the > second? What 'black magic' takes place so that int is not mistaken for > a method in the first case? cf above. If you understood all my explanations, you now know how to solve the problem. Else, here the solution: class C3(C): f = lambda self, x: return x -- bruno desthuilliers python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for p in '[EMAIL PROTECTED]'.split('@')])" -- http://mail.python.org/mailman/listinfo/python-list