比较PHP的__get()与Python的__get__()和__getattr__()
在Python中,__get__()
和__getattr__()
有什么区别呢?我之前是做PHP的,PHP里只有__get()
这个东西。我应该什么时候用哪个函数呢?
我一直在努力搞明白这个问题。我看到很多类似的提问,比如这个,是在问__getattr__()
和__getattribute__()
之间的区别。
2 个回答
首先要说明的是,PHP并没有类似于Python的__get__()
的功能——根本没有!你要找的其实是__getattr__()
。
我之前是用PHP的,在那里只有
__get__
。
PHP有一个叫做魔术方法的东西,名为__get()
,当你试图访问一个不存在的属性时,它会被调用。
非等价的简短列表
首先,让我们澄清一些事情:
- PHP并没有类似于Python的
__get__()
- PHP并没有类似于Python的
__getattr__()
- PHP并没有类似于Python的
__getattribute__()
- Python并没有类似于PHP的
__get()
(还有所有的设置方法也是如此。)
与Achim的假设相反,__get()
并不等同于Python的__getattr__()
!
Python不区分方法和属性,而PHP是区分的,这就是为什么PHP有第二个方法:__call()
。
当你试图调用一个不存在的方法时,__call()
会被执行。Python没有类似的功能,因为方法本身就是一个可以调用的对象(属性)。
下面是一个PHP的例子:
<?php
$obj = new stdClass();
($obj->test)();
在Python中,这会因为AttributeError
而失败。然而在PHP中,这根本无法编译:
解析错误:第4行的语法错误,意外的'('
与Python相比:
obj.method()
# is eqvuivalent to:
(obj.method)()
这是一个重要的区别。我们可以得出结论,PHP和Python在调用方法的方式上完全不同。
PHP的“调用意识”
- PHP的
obj
知道你在调用一个方法- 你可以明确处理对不存在方法的调用
- 这是因为PHP的表达式模型非常不一致(不过PHP 5.4有了一点进步)
- 但Python的
obj
并不知道。
这使得PHP可以让obj.does_not_exist
的结果为3
,而obj.does_not_exist()
的结果为5
。
据我所知,在Python中这是不可能的。(这让我们可以把PHP的不一致性视为一种特性。)
因此,我们可以在“非等价”列表中再增加一条:
- Python并没有类似于PHP的
__call()
/__callStatic()
总结
PHP提供了两种不同的机制:
__get()
用于处理不存在的属性__call()
用于处理不存在的方法调用
而Python只有一种机制,因为它不区分属性和方法,在它看来它们都是属性。
__getattr__()
在属性不存在时被调用。obj.non_existing()
并不是特殊的“调用语法”,它是一个表达式,调用操作符()
被应用于这个表达式:(obj.__getattr__("non_existing"))()
免责声明:__getattr__()
并不总是在属性不存在时被调用。__getattribute__()
在查找链中优先级最高,因此可能会导致__getattr__()
被忽略。
描述符
Python中的__get__()
与上面讨论的内容完全不同。
文档中将描述符定义为"描述符是具有“绑定行为”的对象属性"
。我可以大致理解“绑定行为”是什么意思,但这仅仅是因为我已经理解了描述符的作用。
我会把描述符描述为“自我意识的属性”,这些属性可以在多个类之间共享。
让我解释一下“自我意识”的意思。这些属性:
- 知道它们何时被访问或读取
- 知道它们何时被写入
- 知道通过谁被读取/写入
- 知道何时被删除
这些属性是独立的对象:即“描述符”对象,遵循所谓的“描述符协议”,该协议定义了一组方法及其对应的签名,这些对象可以实现。
一个对象并不拥有描述符属性。实际上,它们属于对象对应的类(或其祖先)。然而,同一个描述符对象可以“属于”多个类。
注意:“通过谁被读取”,在obj.attr
中,如何正确地称呼obj
?我会说:attr
是“通过”obj
访问的。
你可以在这里找到所有这些方法的详细说明。
如果你是从PHP转过来的,首先要了解一下Python的对象模型。Python的对象模型比PHP要复杂得多,所以不要试图把你在PHP学到的知识直接照搬到Python上。如果你想开发PHP,就用PHP。如果你想用Python开发,那就好好学Python。
回到你最初的问题:__getattr__
这个函数大概和PHP里的__get
函数是一样的。Python中的__get__
是用来实现描述符的。关于描述符的详细信息,你可以在我刚才提到的文档中找到。