比较PHP的__get()与Python的__get__()和__getattr__()

11 投票
2 回答
2449 浏览
提问于 2025-04-17 06:59

在Python中,__get__()__getattr__()有什么区别呢?我之前是做PHP的,PHP里只有__get()这个东西。我应该什么时候用哪个函数呢?

我一直在努力搞明白这个问题。我看到很多类似的提问,比如这个,是在问__getattr__()__getattribute__()之间的区别。

2 个回答

10

首先要说明的是,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访问的。

5

你可以在这里找到所有这些方法的详细说明。

如果你是从PHP转过来的,首先要了解一下Python的对象模型。Python的对象模型比PHP要复杂得多,所以不要试图把你在PHP学到的知识直接照搬到Python上。如果你想开发PHP,就用PHP。如果你想用Python开发,那就好好学Python。

回到你最初的问题:__getattr__这个函数大概和PHP里的__get函数是一样的。Python中的__get__是用来实现描述符的。关于描述符的详细信息,你可以在我刚才提到的文档中找到。

撰写回答