What is the difference between __get__()
and __getattr__()
in Python? I come from a PHP background, where there is only __get()
. When should I use which function?
I've been trying to figure this out for a while. I see plenty of questions like this one, asking about the difference between __getattr__()
and __getattribute__()
, though.
First an foremost, PHP does not have an equivalent to Python's __get__()
– not even close! What you are looking for is most definitely __getattr__()
.
I come from a PHP background, where there is only
__get__
PHP has a magic method called __get()
, which is invoked whenever you are trying to access a property that does not exist.
First, let's clear some things up:
__get__()
__getattr__()
__getattribute__()
__get()
(And for all setter methods respectively.)
Contrary to Achim's assumption, __get()
does not do the same as Python's __getattr__()
!
Python does not distinguish between methods and properties, but PHP does, which is why PHP has a second method: __call()
.
__call()
is executed whenever you try to invoke a method on an object that does not exist. Python does not have an equivalent for this, because a method is simply an object (attribute) that is callable.
An example in PHP:
<?php
$obj = new stdClass();
($obj->test)();
In Python, this would fail with an AttributeError
. In PHP, however, this does not even compile:
Parse error: syntax error, unexpected '(' on line 4
Compare this to Python:
obj.method()
# is eqvuivalent to:
(obj.method)()
This is an important difference. We conclude that the way PHP and Python think about calling methods are completely different.
obj
knows that you call a method
obj
does not.This makes it possible for PHP to have obj.does_not_exist
to evaluate to 3
, but obj.does_not_exist()
to 5
.
To my knowledge, it's impossible to do so in Python. (This would allow us to describe PHP's inconsistency as a feature.)
Thus, we get to extend our "not equivalent"-list by one bullet point:
__call()
/__callStatic()
PHP provides two separate mechanisms:
__get()
for non-existing properties__call()
for calls to non-existing methodsPython has only one mechanism, because it does not distinguish between properties and methods, as far as it is concerned they are all attributes.
__getattr__()
is invoked, when an attribute does not exist.obj.non_existing()
is not special "call syntax", it's an expression to which the call operator ()
is applied: (obj.__getattr__("non_existing"))()
Disclaimer: __getattr__()
is not always called when an attribute does not exist. __getattribute__()
takes the highest precedence in the lookup chain and may thus cause __getattr__()
to be ignored.
__get__()
in Python is something completely different from what has been addressed above.
The documentation describes descriptors to be "a descriptor is an object attribute with “binding behavior”"
. I can sort of form an intuitive understanding what "binding behavior" is supposed to mean, but only because I already understand what descriptors do.
I would choose to describe descriptors as "self-aware attributes" that can be shared across multiple classes.
Let me explain, what I mean by "self-awareness". Such attributes:
These attributes are independent objects: the "descriptor" objects, i.e. objects that adhere to the so called "descriptor protocol", which defines a set of methods along with their corresponding signature that such objects can implement.
An object does not own a descriptor attribute. In fact, they belong to the object's corresponding class (or an ancestor thereof). However, the same descriptor object can "belong" to multiple classes.
Note: "whom they are read via", what is the proper way to refer to obj
in obj.attr
? I would say: attr
is accessed "via" obj
.
You will find detailed documentation for all those methods here.
Coming from PHP, you should first make yourself familiar with the Python object model. It's much richer than PHP, so you should not try to map your PHP knowledge 1:1 to Python. If you want to develop PHP, use PHP. If you want to develop in Python, learn Python.
Coming back to your original question: __getattr__
is probably the function which does the same as the __get
function in PHP. __get__
in Python is used to implement descriptors. Details about descriptors can also be found in the documentation I mentioned above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With