我正在学习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。
答案 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成为对象上的方法来实现。