__getattr__返回需要一个参数的lambda函数不起作用

时间:2017-05-15 22:04:35

标签: python lambda python-3.6 getattr

我正在学习Python 3,只是遇到了getattr函数。据我所知,当在类定义中找不到属性调用作为函数或变量时,会调用它。

为了理解这种行为,我编写了以下测试类(基于我读过的内容):

class Test(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    def __getattr__(self, itm):
        if itm is 'test':
            return lambda x: "%s%s" % (x.foo, x.bar)
        raise AttributeError(itm)

然后我启动我的对象并调用不存在的函数test,该函数期望返回对函数的引用:

t = Test("Foo", "Bar")    
print(t.test)
<function Test.__getattr__.<locals>.<lambda> at 0x01A138E8>

但是,如果我调用该函数,结果不是预期的&#34; FooBar&#34;,而是一个错误:

print(t.test())
TypeError: <lambda>() missing 1 required positional argument: 'x'

为了获得我期望的结果,我需要使用与第一个参数相同的对象来调用该函数,如下所示:

print(t.test(t))
FooBar

我发现这种行为很奇怪,因为在调用p.some_function()时,据说将p添加为第一个参数。

如果有人能对我头痛的事情发光,我将不胜感激。我在Eclipse中使用PyDev。

5 个答案:

答案 0 :(得分:2)

__getattr__返回值是&#34; raw&#34;,它们不像行属性一样,调用普通方法涉及的描述符协议,导致绑定方法的创建(其中{{ 1}}隐式传递)。要将函数绑定为方法,需要手动执行绑定:

self

types.MethodType is poorly documented(交互式帮助更有帮助),但基本上,你传递一个用户定义的函数和一个类的实例,它返回一个绑定的方法,当被调用时,隐式地将该实例传递为第一个位置参数(import types ... def __getattr__(self, itm): if itm is 'test': # Note: This should really be == 'test', not is 'test' # Explicitly bind function to self return types.MethodType(lambda x: "%s%s" % (x.foo, x.bar), self) raise AttributeError(itm) 参数)。

答案 1 :(得分:1)

要获得你想要的东西,你需要一个不带参数的lambda:

return lambda: "%s%s" % (self.foo, self.bar)

但你真的应该使用property代替。

class Test(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    @property
    def test(self):
        return "{}{}".format(self.foo, self.bar)

t = Test("Foo", "Bar")
print(t.test)
# FooBar

请注意缺少括号。

如果您完全确定必须是一个功能,请执行以下操作:

class Test(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    @property
    def test(self):
        return lambda: "{}{}".format(self.foo, self.bar)

t = Test("Foo", "Bar")
print(t.test())
# FooBar

答案 2 :(得分:1)

  

“我觉得这种行为很奇怪,就像打电话一样   p.some_function(),据说将p添加为第一个参数。“

some_function实际上是一个方法,这就是当方法“绑定到一个对象”时它会隐式传递一个实例的原因。但普通函数不能以这种方式工作,只有类体中定义的函数才能自动将这种魔法应用于它们。实际上,未绑定的方法(通过类直接访问)与普通函数的功能相同!术语“绑定和未绑定”方法不再适用,因为在Python 3中我们只有方法和函数(摆脱了未绑定方法和普通函数之间的区别)。实例化实例时,访问该属性会返回一个方法,它会在调用时隐式调用该实例。

>>> class A:
...     def method(self, x):
...         return x
...
>>> a.method
<bound method A.method of <__main__.A object at 0x101a5b3c8>>
>>> type(a.method)
<class 'method'>

但是,如果您访问类的属性,您会发现它只是一个函数:

>>> A.method
<function A.method at 0x101a64950>
>>> type(A.method)
<class 'function'>
>>> a = A()

现在,观察:

>>> bound = a.method
>>> bound(42)
42
>>> unbound = A.method
>>> unbound(42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() missing 1 required positional argument: 'x'

但这是课堂的魔力。注意,你甚至可以动态地向类添加函数,当你在一个实例上调用它们时,它们会神奇地变成方法:

>>> A.method2 = lambda self, x: x*2
>>> a2 = A()
>>> a2.method2(4)
8

并且,正如人们所希望的那样,行为仍然适用于已经创建的对象!

>>> a.method2(2)
4

注意,如果您动态添加到实例,则无效

>>> a.method3 = lambda self, x: x*3
>>> a.method3(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'

你必须自己做魔术:

>>> from types import MethodType
>>> a.method4 = MethodType((lambda self, x: x*4), a)
>>> a.method4(4)
16
>>>

答案 3 :(得分:1)

您需要创建一个行为类似于绑定方法的东西,您只需使用functools.partial将实例绑定到函数:

from functools import partial

class Test(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    def __getattr__(self, itm):
        if itm == 'test':  # you shouldn't use "is" for comparisons!
            return partial(lambda x: "%s%s" % (x.foo, x.bar), self)
        raise AttributeError(itm)

测试:

t = Test("Foo", "Bar")    
print(t.test)
# functools.partial(<function Test.__getattr__.<locals>.<lambda> at 0x0000020C70CA6510>, <__main__.Test object at 0x0000020C7217F8D0>)
print(t.test())
# FooBar

答案 4 :(得分:0)

请注意,如果您print(t.__getattr__) <bound method Test.__getattr__ of <__main__.Test object at 0x00000123FBAE4DA0>>,则会收到{{1}}之类的内容。关键是在对象上定义的方法被称为“绑定”。所以总是把对象作为第一个参数。你的lambda函数只是一个不受约束的匿名函数。任何东西,所以要访问它需要显式传入的对象。

我认为你只是在尝试使用`__getattr __&#39;,因为你正在做的事情可以通过让你的lambda成为对象上的方法来实现。