Online Book Reader

Home Category

Learning Python - Mark Lutz [515]

By Root 1946 0
# Force higher to avoid me

By contrast, though, we cannot use the __dict__ trick to avoid loops in __getattribute__:

def __getattribute__(self, name):

x = self.__dict__['other'] # LOOPS!

Fetching the __dict__ attribute itself triggers __getattribute__ again, causing a recursive loop. Strange but true!

The __delattr__ method is rarely used in practice, but when it is, it is called for every attribute deletion (just as __setattr__ is called for every attribute assignment). Therefore, you must take care to avoid loops when deleting attributes, by using the same techniques: namespace dictionaries or superclass method calls.

A First Example

All this is not nearly as complicated as the prior section may have implied. To see how to put these ideas to work, here is the same first example we used for properties and descriptors in action again, this time implemented with attribute operator overloading methods. Because these methods are so generic, we test attribute names here to know when a managed attribute is being accessed; others are allowed to pass normally:

class Person:

def __init__(self, name): # On [Person()]

self._name = name # Triggers __setattr__!

def __getattr__(self, attr): # On [obj.undefined]

if attr == 'name': # Intercept name: not stored

print('fetch...')

return self._name # Does not loop: real attr

else: # Others are errors

raise AttributeError(attr)

def __setattr__(self, attr, value): # On [obj.any = value]

if attr == 'name':

print('change...')

attr = '_name' # Set internal name

self.__dict__[attr] = value # Avoid looping here

def __delattr__(self, attr): # On [del obj.any]

if attr == 'name':

print('remove...')

attr = '_name' # Avoid looping here too

del self.__dict__[attr] # but much less common

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

print(bob.name) # Runs __getattr__

bob.name = 'Robert Smith' # Runs __setattr__

print(bob.name)

del bob.name # Runs __delattr__

print('-'*20)

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

print(sue.name)

#print(Person.name.__doc__) # No equivalent here

Notice that the attribute assignment in the __init__ constructor triggers __setattr__ too—this method catches every attribute assignment, even those within the class itself. When this code is run, the same output is produced, but this time it’s the result of Python’s normal operator overloading mechanism and our attribute interception methods:

fetch...

Bob Smith

change...

fetch...

Robert Smith

remove...

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

fetch...

Sue Jones

Also note that, unlike with properties and descriptors, there’s no direct notion of specifying documentation for our attribute here; managed attributes exist within the code of our interception methods, not as distinct objects.

To achieve exactly the same results with __getattribute__, replace __getattr__ in the example with the following; because it catches all attribute fetches, this version must be careful to avoid looping by passing new fetches to a superclass, and it can’t generally assume unknown names are errors:

# Replace __getattr__ with this

def __getattribute__(self, attr): # On [obj.any]

if attr == 'name': # Intercept all names

print('fetch...')

attr = '_name' # Map to internal name

return object.__getattribute__(self, attr) # Avoid looping here

This example is equivalent to that coded for properties and descriptors, but it’s a bit artificial, and it doesn’t really highlight these tools in practice. Because they are generic, __getattr__ and __getattribute__ are probably more commonly used in delegation-base code (as sketched earlier), where attribute access is validated and routed to an embedded object. Where just a single attribute must be managed, properties and descriptors might do as well or better.

Computed Attributes

As before, our prior example doesn’t really do anything but trace attribute fetches; it’s not much more work to compute an attribute’s value when fetched. As for properties and descriptors, the following creates a virtual attribute X that runs a calculation

Return Main Page Previous Page Next Page

®Online Book Reader