Online Book Reader

Home Category

Learning Python - Mark Lutz [511]

By Root 1387 0
attribute instead. When the descriptor’s __get__ method is run, it is passed three objects to define its context:

self is the Name class instance.

instance is the Person class instance.

owner is the Person class.

When this code is run the descriptor’s methods intercept accesses to the attribute, much like the property version. In fact, the output is the same again:

fetch...

Bob Smith

change...

fetch...

Robert Smith

remove...

--------------------

fetch...

Sue Jones

name descriptor docs

Also like in the property example, our descriptor class instance is a class attribute and thus is inherited by all instances of the client class and any subclasses. If we change the Person class in our example to the following, for instance, the output of our script is the same:

...

class Super:

def __init__(self, name):

self._name = name

name = Name()

class Person(Super): # Descriptors are inherited

pass

...

Also note that when a descriptor class is not useful outside the client class, it’s perfectly reasonable to embed the descriptor’s definition inside its client syntactically. Here’s what our example looks like if we use a nested class:

class Person:

def __init__(self, name):

self._name = name

class Name: # Using a nested class

"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

name = Name()

When coded this way, Name becomes a local variable in the scope of the Person class statement, such that it won’t clash with any names outside the class. This version works the same as the original—we’ve simply moved the descriptor class definition into the client class’s scope—but the last line of the testing code must change to fetch the docstring from its new location:

...

print(Person.Name.__doc__) # Differs: not Name.__doc__ outside class

Computed Attributes

As was the case when using properties, our first descriptor example of the prior section didn’t do much—it simply printed trace messages for attribute accesses. In practice, descriptors can also be used to compute attribute values each time they are fetched. The following illustrates—it’s a rehash of the same example we coded for properties, which uses a descriptor to automatically square an attribute’s value each time it is fetched:

class DescSquare:

def __init__(self, start): # Each desc has own state

self.value = start

def __get__(self, instance, owner): # On attr fetch

return self.value ** 2

def __set__(self, instance, value): # On attr assign

self.value = value # No delete or docs

class Client1:

X = DescSquare(3) # Assign descriptor instance to class attr

class Client2:

X = DescSquare(32) # Another instance in another client class

# Could also code 2 instances in same class

c1 = Client1()

c2 = Client2()

print(c1.X) # 3 ** 2

c1.X = 4

print(c1.X) # 4 ** 2

print(c2.X) # 32 ** 2

When run, the output of this example is the same as that of the original property-based version, but here a descriptor class object is intercepting the attribute accesses:

9

16

1024

Using State Information in Descriptors

If you study the two descriptor examples we’ve written so far, you might notice that they get their information from different places—the first (the name attribute example) uses data stored on the client instance, and the second (the attribute squaring example) uses data attached to the descriptor object itself. In fact, descriptors can use both instance state and descriptor state, or any combination thereof:

Descriptor state is used to manage data internal to the workings of the descriptor.

Instance state records information related to and possibly created by the client class.

Descriptor methods may use either, but descriptor state often makes it unnecessary to use special naming conventions to avoid name collisions for descriptor data stored on an instance. For example, the following descriptor

Return Main Page Previous Page Next Page

®Online Book Reader