Descriptors are classes that, at the very least, implement the __get__ method. If it only implements that method, it’s called a non-data descriptor.
If the __delete__ and __set__ are implemented as well, the descriptor is called to be a data descriptor
So, what’s the idea? Whenever you access an attribute in some other class, and turns out that the attribute is a descriptor, the __get__ method get called and, as it’s a method, you can do whatever you wanna do.
What gets called.
class MyDescriptor(object): """ This is a data descriptor because it implements all three __get__, __delete__ and __set__ methods. """ def __get__(self, instance, owner): print "Hello, this is the __get__ method" def __delete__(self, instance): print "Hello, this is the __delete__ method" def __set__(self, instance, value): print "Hello, this is the __set__ method" class MyClass(object): """ Just a class that defines a single static attribute. """ static_variable = MyDescriptor() anInstance = MyClass()
Whenever you get the anInstance.static_variable attribute, then the static_variable.__get__(anInstance, MyClass) method gets called.
Replacing the descriptor for another thing
If you ever wanted to change the descriptor for, let’s say a number, you can. But you’ll have to do is at the class level, not at the instance one, and it’s not that straighforward as one would expect it to be.
If you do it at the instance level and it turns out that the __set__ method is implemented in the descriptor… this is what will happen:
>>> anInstance.static_variable = 1 Hello, this is the __set__ method
And this is obviously not what you wanted. Trying to detach the descriptor from the static_variable will call the descriptor’s __set__ value itself.
You could do the following mistaken hack:
>>> anInstance.__dict__['static_variable'] = 1 >>> anInstance.__dict__['static_variable'] 1 >>> anInstance.static_variable Hello, this is the __get__ method
And it obviously does not work because there is instance attribute. static_variable is an static member.
Here you added a new attribute to the instance.
The static_variable is associated to the MyClass’s __dict__ and not to the instance’s one.
So instead, we could try the following thing:
>>> MyClass.__dict__['static_variable'] = 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'dictproxy' object does not support item assignment >>> type(MyClass.__dict__) <type 'dictproxy'>
What the hell is that? dictProxy? Wasn’t __dict__ a normal dict?
dictProxy is a wrapper for MyClass’s __dict__ and it avoids, at least in CPython, the accidental modification of it’s attributes. Actually, as far as I know, it denies you the posibility to modify the __dict__ content.
Instead, we just do the following and finally we can remove this descriptor
>>> MyClass.static_variable Hello, this is the __get__ method >>> MyClass.static_variable = 1 >>> MyClass.static_variable 1 >>> anInstance.static_variable 1 >>>