__getattr__ 返回需要一个参数的 lambda 函数不起作用
__getattr__ returning lambda function requiring one argument does not work
我正在学习 Python 3 并且只是 运行 进入 getattr
函数。据我所知,当在 class 定义中找不到作为函数或变量的属性调用时,将调用它。
为了理解这种行为,我编写了以下测试 class(基于我读过的内容):
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
,预计 returns 对函数的引用:
t = Test("Foo", "Bar")
print(t.test)
<function Test.__getattr__.<locals>.<lambda> at 0x01A138E8>
但是,如果我调用该函数,结果不是预期的 "FooBar",而是一个错误:
print(t.test())
TypeError: <lambda>() missing 1 required positional argument: 'x'
为了获得预期的结果,我需要使用与第一个参数相同的对象来调用该函数,如下所示:
print(t.test(t))
FooBar
我发现这种行为相当 st运行ge,因为当调用 p.some_function()
时,据说添加 p
作为第一个参数。
如果有人能对我的这个头痛问题有所启发,我将不胜感激。我在 Eclipse 中使用 PyDev。
要得到你想要的,你需要一个不带参数的 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
请注意,如果您这样做 print(t.__getattr__)
,您会得到类似 <bound method Test.__getattr__ of <__main__.Test object at 0x00000123FBAE4DA0>>
的结果。关键是在对象上定义的方法被称为 'bound' ,因此总是将对象作为第一个参数。您的 lambda 函数只是一个匿名函数而不是 'bound' 任何东西,因此要访问它需要显式传入的对象。
我认为您这样做只是为了试验使用“__getattr__”,因为通过使 lambda 成为对象上的方法,您可以更轻松地实现您正在做的事情。
"I find this behaviour rather strange, as when calling
p.some_function(), is said to add p as the first argument."
some_function
实际上是一个 方法 ,这就是为什么当方法是 "bound to an object." 但是普通函数不是这样工作的,只有在 class 主体中定义的函数 才会自动对它们应用这种魔法 。实际上,未绑定方法(直接通过 class 访问)的功能与普通功能相同!术语 "bound and unbound" 方法不再适用,因为在 Python 3 中我们只有方法和函数(摆脱了未绑定方法和普通函数之间的区别)。实例化实例时,访问属性 returns 一个 方法 ,它在调用时隐式调用实例。
>>> 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'>
但是,如果您访问 class 的属性,您会发现它只是一个函数:
>>> 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'
但这就是 classes 的魔力。请注意,您甚至可以动态地向 classes 添加函数,当您在实例上调用它们时,它们会神奇地变成方法:
>>> 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
>>>
您需要创建一些行为类似于绑定方法的东西,您可以简单地使用 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
__getattr__
return 值是 "raw",它们的行为不像 class 属性,调用普通方法涉及的描述符协议导致创建绑定方法(其中 self
被隐式传递)。要将函数绑定为方法,您需要手动执行绑定:
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)
types.MethodType
is poorly documented(交互式帮助更有帮助),但基本上,您向它传递一个用户定义的函数和一个 class 的实例,它 return 是一个绑定方法调用时,隐式将该实例作为第一个位置参数(self
参数)传递。
请注意,在您的特定情况下,您可以仅依靠闭包范围来使零参数函数继续工作:
def __getattr__(self, itm):
if itm is 'test': # Note: This should really be == 'test', not is 'test'
# No binding, but referring to self captures it in closure scope
return lambda: "%s%s" % (self.foo, self.bar)
raise AttributeError(itm)
现在它根本不是一个绑定方法,只是一个恰好从定义它的范围(__getattr__
调用)捕获了 self
的函数。哪种解决方案最好取决于您的需求;创建一个绑定方法稍微慢一点,但是得到一个真正的绑定方法,而依赖闭包范围(通常,>400ns 中的 ~10ns)更快,但是 returns 是一个普通函数(这可能是一个问题,如果,例如,它作为回调传递给假设它是绑定方法的代码,并且可以分别提取 __self__
和 __func__
。例如)。
我正在学习 Python 3 并且只是 运行 进入 getattr
函数。据我所知,当在 class 定义中找不到作为函数或变量的属性调用时,将调用它。
为了理解这种行为,我编写了以下测试 class(基于我读过的内容):
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
,预计 returns 对函数的引用:
t = Test("Foo", "Bar")
print(t.test)
<function Test.__getattr__.<locals>.<lambda> at 0x01A138E8>
但是,如果我调用该函数,结果不是预期的 "FooBar",而是一个错误:
print(t.test())
TypeError: <lambda>() missing 1 required positional argument: 'x'
为了获得预期的结果,我需要使用与第一个参数相同的对象来调用该函数,如下所示:
print(t.test(t))
FooBar
我发现这种行为相当 st运行ge,因为当调用 p.some_function()
时,据说添加 p
作为第一个参数。
如果有人能对我的这个头痛问题有所启发,我将不胜感激。我在 Eclipse 中使用 PyDev。
要得到你想要的,你需要一个不带参数的 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
请注意,如果您这样做 print(t.__getattr__)
,您会得到类似 <bound method Test.__getattr__ of <__main__.Test object at 0x00000123FBAE4DA0>>
的结果。关键是在对象上定义的方法被称为 'bound' ,因此总是将对象作为第一个参数。您的 lambda 函数只是一个匿名函数而不是 'bound' 任何东西,因此要访问它需要显式传入的对象。
我认为您这样做只是为了试验使用“__getattr__”,因为通过使 lambda 成为对象上的方法,您可以更轻松地实现您正在做的事情。
"I find this behaviour rather strange, as when calling p.some_function(), is said to add p as the first argument."
some_function
实际上是一个 方法 ,这就是为什么当方法是 "bound to an object." 但是普通函数不是这样工作的,只有在 class 主体中定义的函数 才会自动对它们应用这种魔法 。实际上,未绑定方法(直接通过 class 访问)的功能与普通功能相同!术语 "bound and unbound" 方法不再适用,因为在 Python 3 中我们只有方法和函数(摆脱了未绑定方法和普通函数之间的区别)。实例化实例时,访问属性 returns 一个 方法 ,它在调用时隐式调用实例。
>>> 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'>
但是,如果您访问 class 的属性,您会发现它只是一个函数:
>>> 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'
但这就是 classes 的魔力。请注意,您甚至可以动态地向 classes 添加函数,当您在实例上调用它们时,它们会神奇地变成方法:
>>> 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
>>>
您需要创建一些行为类似于绑定方法的东西,您可以简单地使用 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
__getattr__
return 值是 "raw",它们的行为不像 class 属性,调用普通方法涉及的描述符协议导致创建绑定方法(其中 self
被隐式传递)。要将函数绑定为方法,您需要手动执行绑定:
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)
types.MethodType
is poorly documented(交互式帮助更有帮助),但基本上,您向它传递一个用户定义的函数和一个 class 的实例,它 return 是一个绑定方法调用时,隐式将该实例作为第一个位置参数(self
参数)传递。
请注意,在您的特定情况下,您可以仅依靠闭包范围来使零参数函数继续工作:
def __getattr__(self, itm):
if itm is 'test': # Note: This should really be == 'test', not is 'test'
# No binding, but referring to self captures it in closure scope
return lambda: "%s%s" % (self.foo, self.bar)
raise AttributeError(itm)
现在它根本不是一个绑定方法,只是一个恰好从定义它的范围(__getattr__
调用)捕获了 self
的函数。哪种解决方案最好取决于您的需求;创建一个绑定方法稍微慢一点,但是得到一个真正的绑定方法,而依赖闭包范围(通常,>400ns 中的 ~10ns)更快,但是 returns 是一个普通函数(这可能是一个问题,如果,例如,它作为回调传递给假设它是绑定方法的代码,并且可以分别提取 __self__
和 __func__
。例如)。