Thomas Andrews
Thomas Andrews

Reputation: 1597

Python 2.7 class method assignment oddity - lambda gets the behavior I want

I have the following Python 2.7 code:

from collections import namedtuple

Point = namedtuple('Point',['x','y'])
Point2 = namedtuple('Point2',['x','y'])

Point._revert = tuple
Point2._revert = lambda s: tuple(s)

for pointClass in [Point,Point2]:
    instance = pointClass(x=10,y=20)
    print "{} reverts to {}".format(instance,instance._revert())

The output is;

Point(x=10, y=20) reverts to ()
Point2(x=10, y=20) reverts to (10, 20)

So, what about the lambda is making the Point2._revert call work?


A simpler, standalone, example, requiring no imports:

class A(tuple):

    fTuple = tuple
    fLambda = lambda s: tuple(s)


a = A((1,2))

print repr(a.fTuple())
print repr(a.fLambda())

Upvotes: 1

Views: 73

Answers (2)

Thomas Andrews
Thomas Andrews

Reputation: 1597

The brief answer is that the lambda implements the method __get__:

>>> (lambda s: tuple(s)).__get__
<method-wrapper '__get__' of function object at 0x101a37b90>

See the descriptor documentation for what this means, but basically, when the value stored in an attribute has a __get__ method, reading that attribute returns the result of the __get__ method on the value, not the value itself.

So, in the code:

globalLambda  = lambda s: tuple(s)

class A(object):
    attrLambda = globalLambda

a = A()
print globalLambda # --> <function <lambda> at 0x102137b90>
print A.attrLambda # --> <unbound method A.<lambda>>
print a.attrLambda # --> <bound method A.<lambda> of <__main__.A object at 0x102134790>>

these are three different things.

Since tuple is a type object, it has no __get__ method, the access a.fTuple just returns tuple.

Upvotes: 1

Primusa
Primusa

Reputation: 13498

tuple is a class, that does not require a parameter to be constructed.

Calling instance.tuple() returns tuple() which is just an empty tuple.

The difference with your anonymous function is that it is a function. When calling a class function from outside of the functional scope using instance.function(), self is automatically passed in as a parameter. Classes don't have this same treatment.

What's passed in is ._revert(instance), which calls tuple(instance) which actually reverts your tuple.

from collections import namedtuple

Point = namedtuple('Point',['x','y'])
Point2 = namedtuple('Point2',['x','y'])


class test:
    def __init__(self):
        print(self)

def func(a):
    print(a)


Point._revert = test
Point2._revert = func

instance = Point(x=10,y=20)
instance._revert()
>>><__main__.test object at 0x0000020ECAE1DFD0>

instance = Point2(x=10,y=20)
instance._revert()
>>>Point2(x=10, y=20)

Upvotes: 2

Related Questions