Online Book Reader

Home Category

Learning Python - Mark Lutz [510]

By Root 1887 0

>>> Subject.attr

<__main__.Descriptor object at 0x0281E690>

None

Notice the arguments automatically passed in to the __get__ method in the first attribute fetch—when X.attr is fetched, it’s as though the following translation occurs (though the Subject.attr here doesn’t invoke __get__ again):

X.attr -> Descriptor.__get__(Subject.attr, X, Subject)

The descriptor knows it is being accessed directly when its instance argument is None.

Read-only descriptors

As mentioned earlier, unlike with properties, with descriptors simply omitting the __set__ method isn’t enough to make an attribute read-only, because the descriptor name can be assigned to an instance. In the following, the attribute assignment to X.a stores a in the instance object X, thereby hiding the descriptor stored in class C:

>>> class D:

... def __get__(*args): print('get')

...

>>> class C:

... a = D()

...

>>> X = C()

>>> X.a # Runs inherited descriptor __get__

get

>>> C.a

get

>>> X.a = 99 # Stored on X, hiding C.a

>>> X.a

99

>>> list(X.__dict__.keys())

['a']

>>> Y = C()

>>> Y.a # Y still inherits descriptor

get

>>> C.a

get

This is the way all instance attribute assignments work in Python, and it allows classes to selectively override class-level defaults in their instances. To make a descriptor-based attribute read-only, catch the assignment in the descriptor class and raise an exception to prevent attribute assignment—when assigning an attribute that is a descriptor, Python effectively bypasses the normal instance-level assignment behavior and routes the operation to the descriptor object:

>>> class D:

... def __get__(*args): print('get')

... def __set__(*args): raise AttributeError('cannot set')

...

>>> class C:

... a = D()

...

>>> X = C()

>>> X.a # Routed to C.a.__get__

get

>>> X.a = 99 # Routed to C.a.__set__

AttributeError: cannot set

* * *

Note


Also be careful not to confuse the descriptor __delete__ method with the general __del__ method. The former is called on attempts to delete the managed attribute name on an instance of the owner class; the latter is the general instance destructor method, run when an instance of any kind of class is about to be garbage collected. __delete__ is more closely related to the __delattr__ generic attribute deletion method we’ll meet later in this chapter. See Chapter 29 for more on operator overloading methods.

* * *

A First Example

To see how this all comes together in more realistic code, let’s get started with the same first example we wrote for properties. The following defines a descriptor that intercepts access to an attribute named name in its clients. Its methods use their instance argument to access state information in the subject instance, where the name string is actually stored. Like properties, descriptors work properly only for new-style classes, so be sure to derive both classes in the following from object if you’re using 2.6:

class Name: # Use (object) in 2.6

"name descriptor docs"

def __get__(self, instance, owner):

print('fetch...')

return instance._name

def __set__(self, instance, value):

print('change...')

instance._name = value

def __delete__(self, instance):

print('remove...')

del instance._name

class Person: # Use (object) in 2.6

def __init__(self, name):

self._name = name

name = Name() # Assign descriptor to attr

bob = Person('Bob Smith') # bob has a managed attribute

print(bob.name) # Runs Name.__get__

bob.name = 'Robert Smith' # Runs Name.__set__

print(bob.name)

del bob.name # Runs Name.__delete__

print('-'*20)

sue = Person('Sue Jones') # sue inherits descriptor too

print(sue.name)

print(Name.__doc__) # Or help(Name)

Notice in this code how we assign an instance of our descriptor class to a class attribute in the client class; because of this, it is inherited by all instances of the class, just like a class’s methods. Really, we must assign the descriptor to a class attribute like this—it won’t work if assigned to a self instance

Return Main Page Previous Page Next Page

®Online Book Reader