通过 getattr 访问方法

2023-12-22

我偶然发现了这种行为,这表明您可以使用getattr调用类实例上的方法,作为直观命名的替代方法operator.methodcaller:

from operator import methodcaller

class Foo():
    def __init__(self, lst):
        self.lst = lst

    def summer(self):
        return sum(self.lst)

my_obj = Foo(range(11))

res1 = methodcaller('summer')(my_obj)  # 55
res2 = getattr(my_obj, 'summer')()     # 55

assert res1 == res2

我想从内心深处理解,why这有效。是因为所有方法也是属性吗?这seems之所以如此,是因为dir(Foo) or dir(my_obj)包括'summer'。但我从未听说过称为属性类或类实例的,例如这在中没有提到Python 中的“方法”是什么? https://stackoverflow.com/questions/3786881/what-is-a-method-in-python

有一个文档中的解释 https://docs.python.org/3/tutorial/classes.html#method-objects其中提到了我无法理解的“数据属性”和“非数据属性”之间的区别。

Update:@Amadan 的评论已经澄清了上述大部分内容。我唯一不明白的是文档中的摘录:

如果您仍然不明白方法是如何工作的,请查看 实施也许可以澄清问题。当非数据属性 引用实例的类时,会搜索该实例的类。如果 该名称表示作为函数对象的有效类属性, A 方法对象是通过以下方式创建的打包(指向)实例对象和刚刚在一个中找到的函数对象抽象对象: 这是方法对象。

那么,非数据属性是通过检查它是否可调用来确定的,还是有其他方法用于确定它是函数对象?实例对象的“打包指针”是什么意思?什么是抽象对象?


是的,方法只是包含适当形式的函数的属性(它们必须至少接受一个参数,即接收者,通常称为self).

这是一个解释引用段落的示例:

class Foo():
    def a(self):
        print("FOO")

foo = Foo()
foo.a()
# => FOO

So, def实际上附加了一个属性a to Foo作为 Python 2 中的“未绑定方法”(正如您在检查它时看到的那样 - 意思是,它还不知道谁正在接收它),或者只是一个普通的函数值(在 Python 3 中):

Foo.a
# => <unbound method Foo.a>          (Python 2)
# => <function Foo.a at 0x10919fea0> (Python 3)

您可以像调用任何函数一样调用它(...有一个例外,在 Python 2 中:第一个参数must be类型的Foo):

Foo.a(foo)
# => FOO

Foo.a(42)
# => TypeError: unbound method a() must be called with Foo instance as first argument (got int instance instead) (Python 2)
# => 42 (Python 3)

但是,如果您尝试在实例上找到它(“实例属性引用”),它现在报告为“绑定方法”:

foo.a
# => <bound method Foo.a of <__main__.Foo instance at 0x10ba11320>>

这可以说是“将实例对象和函数对象打包(指向)在一起”:有实例对象的引用,<__main__.Foo instance at 0x10ba11320>(又名foo),以及对函数对象的引用,Foo.a,在一个包中我们称之为“绑定方法”。

与 JavaScript 不同,它不纯粹是语法问题。在 JavaScript 中,方法调用和函数调用之间的区别在于调用本身:如果它有一个点,则它是方法调用:

// JavaScript
let foo = new Foo()
foo.a();                // method invocation: `a` is told that the receiver is `foo`
let z = foo.a; z()      // function invocation, `a` doesn't know about `foo`

在 Python 中,绑定函数在其内部携带其接收器:

// Back to Python
foo.a()                 // method invocation: `a` is told that the receiver is `foo`
z = foo.a; z()          // STILL method invocation; `z` knows both `foo` and `Foo.a`

这个电话是如何运作的呢?该段落的其余部分解释:

当使用参数列表调用方法对象时,会根据实例对象和参数列表构造一个新的参数列表,并使用该新参数列表调用函数对象。

所以,当我们说

foo.a()

它将解压foo.a into Foo.a and foo;前置foo,接收者,到参数列表(我这里没有,所以参数列表是[foo] + []最终参数列表[foo]),最终被称为Foo.a(foo)。顺便说一句,这正是您可以手动执行的操作:

Foo.a(foo)
# => FOO

即使使用内置对象:

"-".join(["foo", "bar", "baz"])
# => 'foo-bar-baz'
str.join("-", ["foo", "bar", "baz"])
# => 'foo-bar-baz'

Here, "-".join是一个将接收者打包在一起的绑定方法"-"和函数str.join;当我们调用第一行时,接收者"-"附加在其余参数之前[["foo", "bar", "baz"]]最终参数列表["-", ["foo", "bar", "baz"]],并且它被发送到位于join的属性str (i.e. str.join)。这为我们提供了第一行和第二行之间的清晰翻译。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过 getattr 访问方法 的相关文章

随机推荐