我偶然发现了这种行为,这表明您可以使用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我想从内部理解为什么这会起作用。是因为所有方法也都是属性吗?情况似乎是这样的,因为dir(Foo)或dir(my_obj)包含'summer'。但我从未听说过称为类或类实例的属性的方法,例如,在Python中的“方法”是什么?中没有提到这一点
有一个文档中的解释,它提到了“数据属性”和“非数据属性”之间的区别,但我没有理解。
更新:@Amadan的评论澄清了以上大部分内容。我唯一不明白的部分是从文档中摘录的内容:
如果您仍然不了解方法是如何工作的,那么查看一下实现可能会澄清一些问题。当引用实例的非数据属性时,将搜索实例的类.如果名称表示(一个有效的类属性,即函数对象),则方法对象由打包(指向)实例对象和函数对象创建,该方法对象刚刚在抽象对象中找到:这是方法对象。
那么,非数据属性是通过检查它是否是可调用的,还是有其他方法来确定它是一个函数对象呢?实例对象的“打包指针”意味着什么?什么是抽象对象?
发布于 2018-08-15 13:27:20
是的,方法只是包含适当形式的函数的属性(它们必须至少接受一个参数,即接收方,通常称为self)。
下面是一个例子,解释了引用的段落:
class Foo():
def a(self):
print("FOO")
foo = Foo()
foo.a()
# => FOO因此,在Python2中,def实际上将一个属性a附加到Foo,作为一个“未绑定方法”(当我们检查它时可以看到--意思是,它还不知道是谁接收的),或者仅仅是一个普通的函数值(在Python3中):
Foo.a
# => <unbound method Foo.a> (Python 2)
# => <function Foo.a at 0x10919fea0> (Python 3)你可以像调用任何函数一样调用它(.除了一个例外,在Python2中:第一个参数必须是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> (a.k.a )的引用。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解压缩到Foo.a和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'在这里,"-".join是一个绑定方法,它将接收方"-"和函数str.join打包在一起;当我们调用第一行时,接收方"-"将被附加到["-", ["foo", "bar", "baz"]]的最后参数列表的参数[["foo", "bar", "baz"]]的其余部分,并被发送到位于str的join属性中的函数(即str.join)。这使我们在第一行和第二行之间有了明确的翻译。
发布于 2018-08-15 13:15:55
是的,方法是属性。
属性 一个与对象相关联的值,该值由名称使用虚线表达式引用。例如,如果一个对象o有一个属性a,它将被引用为o.a。
显然,我们可以访问这样的方法,因此它们必须是属性。它们只是碰巧是函数的属性。
此外,在文档中有这句话
getattr(x, 'foobar')等价于x.foobar
直接的结果是,x.foobar()也等同于getattr(x, 'foobar')()。没有理由相信方法在任何方面都是特殊的。
我认为,方法在实践中很少被称为属性的原因有两方面:
最后,关于数据属性和非数据属性:文献资料区分了方法(即可调用属性;“非数据属性”)和数据属性(即所有其他属性)。
有两种有效的属性名称,数据属性和方法。数据属性对应于Smalltalk中的“实例变量”,以及C++中的“数据成员”。另一种实例属性引用是一种方法。
您发布的摘录相当令人困惑,但我认为它试图提供描述符协议的基本解释,它负责将函数转换为方法。让我们再看一遍:
当引用实例的非数据属性时,将搜索实例的类.如果名称表示函数对象的有效类属性,则通过将实例对象和函数对象打包(指向)创建方法对象:这是方法对象。
换句话说:当您执行some_object.some_method时,python从some_object的类中获取函数(!) some_method,然后将其转化为一个绑定方法,其中隐含了self参数。对于我来说,为什么称它为“抽象对象”是个谜。无论如何,有关这个过程的更多细节,请参见函数作为类属性的赋值如何成为Python中的一种方法?或描述符HowTo中的相关部分。
(警告: Python区分数据描述符和非数据描述符。不要混淆这些与数据属性和非数据属性!这是两个完全不相关的概念。)
https://stackoverflow.com/questions/51858649
复制相似问题