开发者

lambda calling @classmethod fails

开发者 https://www.devze.com 2023-01-06 19:37 出处:网络
Maybe I get this completely wrong but I ran into some odd issues with lambda calling @classmethod. I have the following class:

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 a开发者_开发问答rgument (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.


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


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.


There's another direct solution for this. Maybe this isn't the cleanest thing, but I think it is what you were wondering. I just ran into a similar situation myself. You can deliberately unbind it with staticmethod().

Here's the erroneous version:

def bar(): return 0

class foo(object):
  x = lambda:bar()
  @classmethod
  def grok(klass): 
    return klass.x()

foo().grok()

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<console>", line 4, in grok
TypeError: unbound method <lambda>() must be called with foo instance as first argument (got nothing instead)

And the slightly adjusted version:

def bar(): return 0

class foo(object):
  x = staticmethod(lambda:bar())
  @classmethod
  def grok(klass): 
    return klass.x()

foo().grok()

0

Note that both of the above also occur for:

  x = bar

Instead. I was just including the lambda to be a direct response to the question. All the above suggestions also work, this is just a direct way to alter the code as little as possible.

As for why one would ever want to make something like the above work with lambda's? In my case, I was making a factory class that had event handlers that would occasionally use class-wide data to get information, and some of that information was of a value-or-callable nature. (This was for a Django form.) So the "callable" was a straight lambda handed in by the user, which had to be called with self.value() or klass.value() but value was an unbound function (lambda or otherwise).


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']
0

精彩评论

暂无评论...
验证码 换一张
取 消