tags:

views:

57

answers:

3

Maybe I get this completely wrong but I ran into some odd issues with lambda calling @classmethod.

I have the following class:

class MyClass:
  LAMBDA = lambda: MyClass.ClassMethod()

  @classmethod
  def ClassMethod(cls):
    pass

But this fails whenever LAMBDA is called with:

TypeError: unbound method <lambda>() must be called with MyClass instance as first argument (got nothing instead)

I don't really understand why this is so. I have already spent some time trying to get that working. I need some class attributes being populated by that lambda and self-referencing the class is obviously not possible at that stage.

+1  A: 

You must understand, that your code is exactly equivalent to

class MyClass:

  def LAMBDA():
    return MyClass.ClassMethod()

  @classmethod
  def ClassMethod(cls):
    pass

You, then, apparently call it like MyClass.LAMBDA(). But look, LAMBDA is an instance method. And you are calling it without an actual instance in place.

What exactly are you trying to do?

I reckon, if you are giving a name to a lambda construct, you might as well declare it the 'normal' way - def. From my experience, there are only a few isolated cases when using lambas actually contributes to the code quality.

shylent
LAMBDA is a *class member* if defined like that. Don't let the `lambda` distract you. Consider `LAMBDA = 4`.
KennyTM
bah, that lack of `return` was a real shame
shylent
@KennyTM, "class member" isn't Python terminology, so it seems like an odd platform to choose to nitpick on. When shylent says it's a method, he means it like we constantly do: he's talking about the semantic thing, not the name.
Mike Graham
Like all Python instance methods, you store a function object on the class. The function objects made by lambda and def are the same sort of thing. When that object is looked up, the descriptor protocol is invoked and they turn into bound methods when looked up on instances and unbound methods if looked up on their classes. This—the part that makes a method here when a plain function was wanted—is the key to understanding the problem, so it is why shylent talked about it.
Mike Graham
+1  A: 

LAMBDA here is just a normal method. When you look it up on the class, you get an unbound method, which is a sort of silly thing that takes your function and imposes that--though self is not passed yet--the first argument to it must be an instance of the class in question.

To reiterate, LAMBDA = lambda: MyClass.ClassMethod() is not any different than def LAMBDA(): return MyClass.ClassMethod(). In the latter case, it's more clear that you have a sort of broken method definition. A lambda function and a def function form the exact same kinds of object, and Python turns them into methods with the same rules on-the-fly at lookup time.

I suspect the code you might want is

class MyClass(object):
    @classmethod
    def ClassMethod(cls):
        pass
MyClass.LAMBDA = MyClass.ClassMethod

or

class MyClass(object):
    @classmethod
    def ClassMethod(cls):
        pass

    @classmethod
    def LAMBDA(cls):
        return cls.ClassMethod()

(Note this last one could be written with a lambda as you originally did, but there's no reason to. I would never use = lambda in any of my code, personally. Also note that ClassMethod and LAMBDA don't follow normal Python capitalization style rules.)

Mike Graham
A: 

I need some class attributes being populated by that lambda and self-referencing the class is obviously not possible at that stage.

But from your given code, all LAMBDA will do when it is called is itself call MyClass.ClassMethod(), which means the method doing the populating is ClassMethod (this is, of course, assuming that the body of ClassMethod is not actually that sole pass you have shown, because if it is then doing all this is an exercise in futility). Unless there's some aspect of your code that prevents you from referring to the class before using it for anything, why can't you call MyClass.ClassMethod() after the class definition?

And if you're trying to modify attributes of the class inside the definition (that is, calling LAMBDA within the definition of MyClass, outside of any methods/functions/whatever), you may want to take a look at the section of the documentation about metaclasses.

On a side note, if you want to assign attributes to a class, you don't even need to do so within the body of the class itself.

>>> class cls(object):  # or just "class cls:"
...     pass
...
>>> cls.value = 10
>>> cls.name = 'class'
>>> cls.value
10
>>> cls.name
'class'
>>> dir(cls)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'name', 'value']
JAB