The question refers to which one is preferable to be used in which use case, not about the technical background.
In python, you can control the access of attributes via a property, a descriptor, or magic methods. Which one is most pythonic in which use case? All of them seem to have the same effect (see the examples below).
I am looking for an answer like:
- Property: Should be used in case of …
- Descriptor: In the case of … it should be used instead of a property.
- Magic method: Only use if ….
Example
A use case would be an attribute that might not be able to be set in the __init__
method, for example because the object is not present in the database yet, but at a later time. Each time the attribute is accessed, it should be tried to be set and returned.
As an example that works with Copy&Paste in the Python shell, there is a class that wants to present its attribute only the second time it is asked for it. So, which one is the best way, or are there different situations one of them is preferable? Here are the three ways to implement it:
With Property::
class ContactBook(object):
intents = 0
def __init__(self):
self.__first_person = None
def get_first_person(self):
ContactBook.intents += 1
if self.__first_person is None:
if ContactBook.intents > 1:
value = 'Mr. First'
self.__first_person = value
else:
return None
return self.__first_person
def set_first_person(self, value):
self.__first_person = value
first_person = property(get_first_person, set_first_person)
With __getattribute__
::
class ContactBook(object):
intents = 0
def __init__(self):
self.first_person = None
def __getattribute__(self, name):
if name == 'first_person' \
and object.__getattribute__(self, name) is None:
ContactBook.intents += 1
if ContactBook.intents > 1:
value = 'Mr. First'
self.first_person = value
else:
value = None
else:
value = object.__getattribute__(self, name)
return value
Descriptor::
class FirstPerson(object):
def __init__(self, value=None):
self.value = None
def __get__(self, instance, owner):
if self.value is None:
ContactBook.intents += 1
if ContactBook.intents > 1:
self.value = 'Mr. First'
else:
return None
return self.value
class ContactBook(object):
intents = 0
first_person = FirstPerson()
Each one of it has this behavior::
book = ContactBook()
print(book.first_person)
# >>None
print(book.first_person)
# >>Mr. First
Basically, use the simplest one you can. Roughly speaking, the order of complexity/heavy-duty-ness goes: regular attribute, property
, __getattr__
, __getattribute__
/descriptor. (__getattribute__
and custom descriptors are both things you probably won't need to do very often.) This leads to some simple rules of thumb:
- Don't use a
property
if a normal attribute will work. - Don't write your own descriptor if a
property
will work. - Don't use
__getattr__
if aproperty
will work. - Don't use
__getattribute__
if__getattr__
will work.
Stated a bit more specifically: use a property to customize handling of one or a small set of attributes; use __getattr__
to customize handling of all attributes, or all except a small set; use __getattribute__
if you were hoping to use __getattr__
but it doesn't quite work; write your own descriptor class if you are doing something very complicated.
You use a property
when you have one or a small set of attributes whose getting/setting you want to hook into. That is, you want things like obj.prop
and obj.prop = 2
to secretly call a function that you write to customize what happens.
You would use __getattr__
when you want to do that for so many attributes that you don't actually want to define them all individually, but rather want to customize the whole attribute-access process as a whole. In other words, instead of hooking into obj.prop1
, and obj.prop2
, etc., you have so many that you want to be able to hook into obj.<anything>
, and handle that in general.
However, __getattr__
still won't let you override what happens for attributes that really do exist, it just lets you hook in with a blanket handling for any use of attributes that would otherwise raise an AttributeError. Using __getattribute__
lets you hook in to handle everything, even normal attributes that would have worked without messing with __getattribute__
. Because of this, using __getattribute__
has the potential to break fairly basic behavior, so you should only use it if you considered using __getattr__
and it wasn't enough. It also can have a noticeable performance impact. You might for instance need to use __getattribute__
if you're wrapping a class that defines some attributes, and you want to be able to wrap those attributes in a custom way, so that they work as usual in some situations but get custom behavior in other situations.
Finally, I would say writing your own descriptor is a fairly advanced task. property
is a descriptor, and for probably 95% of cases it's the only one you'll need. A good simple example of why you might write your own descriptor is given here: basically, you might do it if you would otherwise have to write several property
s with similar behavior; a descriptor lets you factor out the common behavior to avoid code repetition. Custom descriptors are used, for instance, to drive systems like like Django and SQLAlchemy. If you find yourself writing something at that level of complexity you might need to write a custom descriptor.
In your example, property
would be the best choice. It is usually (not always) a red flag if you're doing if name == 'somespecificname'
inside __getattribute__
. If you only need to specially handle one specific name, you can probably do it without stooping to the level of __getattribute__
. Likewise, it doesn't make sense to write your own descriptor if all you write for its __get__
is something you could have written in a property's getter method.
__getattribute__
is the hook that enables property
(and other descriptors) to work in the first place and is called for all attribute access on an object. Consider it a lower-level API when a property or even a custom descriptor is not enough for your needs. __getattr__
is called by __getattribute__
when no attribute has been located through other means, as a fallback.
Use property
for dynamic attributes with a fixed name, __getattr__
for attributes of a more dynamic nature (e.g. a series of attributes that map to values in an algorithmic manner).
Descriptors are used when you need to bind arbitrary objects to an instance. When you need to replace method objects with something more advanced for example; a recent example is a class-based decorator wrapping methods that needed to support additional attributes and methods on the method object. Generally, when you are still thinking in terms of scalar attributes, you don't need descriptors.
来源:https://stackoverflow.com/questions/22616559/use-cases-for-property-vs-descriptor-vs-getattribute