Accessing methods via getattr
我偶然发现了这种行为,这表明您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
我想从内部了解一下为什么这样做有效。是因为所有方法都是属性吗?这似乎是因为
文档中有一个解释提到了"数据属性"和"非数据属性"之间的区别,但我没有理解。
更新:由@amadan发表的评论澄清了上述大部分内容。我唯一不明白的一点是从文档中摘录的:
If you still don’t understand how methods work, a look at the
implementation can perhaps clarify matters. When a non-data attribute
of an instance is referenced, the instance’s class is searched. If
the name denotes a valid class attribute that is a function object, a
method object is created by packing (pointers to) the instance object
and the function object just found together in an abstract object:
this is the method object.
那么,非数据属性是通过检查它是否可调用来确定的,还是有其他方法用来确定它是一个函数对象?"打包指向实例对象的指针"是什么意思?什么是抽象对象?
是的,方法只是包含适当形式的函数的属性(它们必须接受至少一个参数,即接收器,通常称为
下面是一个解释引用段落的示例:
1 2 3 4 5 6 7 | class Foo(): def a(self): print("FOO") foo = Foo() foo.a() # => FOO |
因此,
1 2 3 | Foo.a # => <unbound method Foo.a> (Python 2) # => <function Foo.a at 0x10919fea0> (Python 3) |
您可以像调用任何函数一样调用它(…有一个例外,在python 2中:第一个参数必须是
1 2 3 4 5 6 | 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) |
但是,如果您试图在实例("实例属性引用")上找到它,它现在报告为"绑定方法":
1 2 | foo.a # => <bound method Foo.a of <__main__.Foo instance at 0x10ba11320>> |
这可以说是"将实例对象和函数对象打包在一起":在一个包中有一个对实例对象的引用,即
与JavaScript不同,它不是纯粹的句法问题。在javascript中,方法调用和函数调用的区别在于调用本身:如果它有一个点,则它是一个方法调用:
1 2 3 4 | // 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中,绑定函数在其内部携带其接收器:
1 2 3 | // 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` |
这个电话怎么用?段落的其余部分解释了:
When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
所以,当我们说
1 | foo.a() |
它将把
1 2 | Foo.a(foo) # => FOO |
即使有内置对象:
1 2 3 4 | "-".join(["foo","bar","baz"]) # => 'foo-bar-baz' str.join("-", ["foo","bar","baz"]) # => 'foo-bar-baz' |
这里,
是的,方法是属性。
从python词汇表中:
attribute
A value associated with an object which is referenced by name using dotted expressions. For example, if an object o has an attribute
a it would be referenced as o.a.
显然,我们可以访问这样的方法,因此它们必须是属性。它们只是恰好是函数的属性。
此外,在
getattr(x, 'foobar') is equivalent tox.foobar
因此,
我认为,方法在实践中很少被称为属性的原因有两个方面:
最后,关于数据属性与非数据属性:文档对方法(即可调用属性;"非数据属性")和数据属性(即其他所有属性)进行了区分。
There are two kinds of valid attribute names, data attributes and methods.
data attributes correspond to"instance variables" in Smalltalk, and to"data members" in C++.
The other kind of instance attribute reference is a method.
您发布的摘录相当令人困惑,但我认为它试图提供描述符协议的基本解释,描述符协议负责将函数转换为方法。我们再来看看:
When a non-data attribute of an instance is referenced, the instance’s
class is searched. If the name denotes a valid class attribute that is
a function object, a method object is created by packing (pointers to)
the instance object and the function object just found together in an
abstract object: this is the method object.
换句话说:当您执行
(警告:python区分数据描述符和非数据描述符。不要将这些与数据属性和非数据属性混淆!这是两个完全无关的概念。)