首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过getattr访问方法

通过getattr访问方法
EN

Stack Overflow用户
提问于 2018-08-15 12:24:41
回答 2查看 1.2K关注 0票数 2

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

代码语言:javascript
复制
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

我想从内部理解为什么这会起作用。是因为所有方法也都是属性吗?情况似乎是这样的,因为dir(Foo)dir(my_obj)包含'summer'。但我从未听说过称为类或类实例的属性的方法,例如,在Python中的“方法”是什么?中没有提到这一点

有一个文档中的解释,它提到了“数据属性”和“非数据属性”之间的区别,但我没有理解。

更新:@Amadan的评论澄清了以上大部分内容。我唯一不明白的部分是从文档中摘录的内容:

如果您仍然不了解方法是如何工作的,那么查看一下实现可能会澄清一些问题。当引用实例的非数据属性时,将搜索实例的类.如果名称表示(一个有效的类属性,即函数对象),则方法对象由打包(指向)实例对象和函数对象创建,该方法对象刚刚在抽象对象中找到:这是方法对象。

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-08-15 13:27:20

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

下面是一个例子,解释了引用的段落:

代码语言:javascript
复制
class Foo():
    def a(self):
        print("FOO")

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

因此,在Python2中,def实际上将一个属性a附加到Foo,作为一个“未绑定方法”(当我们检查它时可以看到--意思是,它还不知道是谁接收的),或者仅仅是一个普通的函数值(在Python3中):

代码语言:javascript
复制
Foo.a
# => <unbound method Foo.a>          (Python 2)
# => <function Foo.a at 0x10919fea0> (Python 3)

你可以像调用任何函数一样调用它(.除了一个例外,在Python2中:第一个参数必须是Foo类型):

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
foo.a
# => <bound method Foo.a of <__main__.Foo instance at 0x10ba11320>>

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

与JavaScript不同的是,这并不是一个纯粹的语法问题。在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中,绑定函数在其内部携带其接收器:

代码语言:javascript
复制
// 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`

这个电话是怎么工作的?该段的其余部分解释:

当使用参数列表调用方法对象时,将从实例对象和参数列表构造新的参数列表,并使用这个新的参数列表调用函数对象。

所以,当我们说

代码语言:javascript
复制
foo.a()

它将把foo.a解压缩到Foo.afoo中;将接收方foo放在参数列表的前面(这里没有参数列表,因此参数列表是[foo]的最终参数列表的[foo] + [] ),最终被调用的是Foo.a(foo)。顺便说一句,这正是您可以手动完成的:

代码语言:javascript
复制
Foo.a(foo)
# => FOO

即使是内置的物体:

代码语言:javascript
复制
"-".join(["foo", "bar", "baz"])
# => 'foo-bar-baz'
str.join("-", ["foo", "bar", "baz"])
# => 'foo-bar-baz'

在这里,"-".join是一个绑定方法,它将接收方"-"和函数str.join打包在一起;当我们调用第一行时,接收方"-"将被附加到["-", ["foo", "bar", "baz"]]的最后参数列表的参数[["foo", "bar", "baz"]]的其余部分,并被发送到位于strjoin属性中的函数(即str.join)。这使我们在第一行和第二行之间有了明确的翻译。

票数 3
EN

Stack Overflow用户

发布于 2018-08-15 13:15:55

是的,方法是属性。

来自python词汇表

属性 一个与对象相关联的值,该值由名称使用虚线表达式引用。例如,如果一个对象o有一个属性a,它将被引用为o.a。

显然,我们可以访问这样的方法,因此它们必须是属性。它们只是碰巧是函数的属性。

此外,在文档中有这句话

getattr(x, 'foobar')等价于x.foobar

直接的结果是,x.foobar()也等同于getattr(x, 'foobar')()。没有理由相信方法在任何方面都是特殊的。

我认为,方法在实践中很少被称为属性的原因有两方面:

  1. 方法的用途与其他属性完全不同。方法用于调用,而其他属性通常只是数据。这也是为什么我们不把函数称为“变量”的原因,尽管它们在技术上是这样的。
  2. “方法”比“可调用的类属性”容易得多。

最后,关于数据属性和非数据属性:文献资料区分了方法(即可调用属性;“非数据属性”)和数据属性(即所有其他属性)。

有两种有效的属性名称,数据属性和方法。数据属性对应于Smalltalk中的“实例变量”,以及C++中的“数据成员”。另一种实例属性引用是一种方法。

您发布的摘录相当令人困惑,但我认为它试图提供描述符协议的基本解释,它负责将函数转换为方法。让我们再看一遍:

当引用实例的非数据属性时,将搜索实例的类.如果名称表示函数对象的有效类属性,则通过将实例对象和函数对象打包(指向)创建方法对象:这是方法对象。

换句话说:当您执行some_object.some_method时,python从some_object的类中获取函数(!) some_method,然后将其转化为一个绑定方法,其中隐含了self参数。对于我来说,为什么称它为“抽象对象”是个谜。无论如何,有关这个过程的更多细节,请参见函数作为类属性的赋值如何成为Python中的一种方法?描述符HowTo中的相关部分

(警告: Python区分数据描述符非数据描述符。不要混淆这些与数据属性非数据属性!这是两个完全不相关的概念。)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51858649

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档