Online Book Reader

Home Category

Learning Python - Mark Lutz [516]

By Root 1634 0
when fetched:

class AttrSquare:

def __init__(self, start):

self.value = start # Triggers __setattr__!

def __getattr__(self, attr): # On undefined attr fetch

if attr == 'X':

return self.value ** 2 # value is not undefined

else:

raise AttributeError(attr)

def __setattr__(self, attr, value): # On all attr assignments

if attr == 'X':

attr = 'value'

self.__dict__[attr] = value

A = AttrSquare(3) # 2 instances of class with overloading

B = AttrSquare(32) # Each has different state information

print(A.X) # 3 ** 2

A.X = 4

print(A.X) # 4 ** 2

print(B.X) # 32 ** 2

Running this code results in the same output that we got earlier when using properties and descriptors, but this script’s mechanics are based on generic attribute interception methods:

9

16

1024

As before, we can achieve the same effect with __getattribute__ instead of __getattr__; the following replaces the fetch method with a __getattribute__ and changes the __setattr__ assignment method to avoid looping by using direct superclass method calls instead of __dict__ keys:

class AttrSquare:

def __init__(self, start):

self.value = start # Triggers __setattr__!

def __getattribute__(self, attr): # On all attr fetches

if attr == 'X':

return self.value ** 2 # Triggers __getattribute__ again!

else:

return object.__getattribute__(self, attr)

def __setattr__(self, attr, value): # On all attr assignments

if attr == 'X':

attr = 'value'

object.__setattr__(self, attr, value)

When this version is run, the results are the same again. Notice the implicit routing going on in inside this class’s methods:

self.value=start inside the constructor triggers __setattr__

self.value inside __getattribute__ triggers __getattribute__ again

In fact, __getattribute__ is run twice each time we fetch attribute X. This doesn’t happen in the __getattr__ version, because the value attribute is not undefined. If you care about speed and want to avoid this, change __getattribute__ to use the superclass to fetch value as well:

def __getattribute__(self, attr):

if attr == 'X':

return object.__getattribute__(self, 'value') ** 2

Of course, this still incurs a call to the superclass method, but not an additional recursive call before we get there. Add print calls to these methods to trace how and when they run.

__getattr__ and __getattribute__ Compared

To summarize the coding differences between __getattr__ and __getattribute__, the following example uses both to implement three attributes—attr1 is a class attribute, attr2 is an instance attribute, and attr3 is a virtual managed attribute computed when fetched:

class GetAttr:

attr1 = 1

def __init__(self):

self.attr2 = 2

def __getattr__(self, attr): # On undefined attrs only

print('get: ' + attr) # Not attr1: inherited from class

return 3 # Not attr2: stored on instance

X = GetAttr()

print(X.attr1)

print(X.attr2)

print(X.attr3)

print('-'*40)

class GetAttribute(object): # (object) needed in 2.6 only

attr1 = 1

def __init__(self):

self.attr2 = 2

def __getattribute__(self, attr): # On all attr fetches

print('get: ' + attr) # Use superclass to avoid looping here

if attr == 'attr3':

return 3

else:

return object.__getattribute__(self, attr)

X = GetAttribute()

print(X.attr1)

print(X.attr2)

print(X.attr3)

When run, the __getattr__ version intercepts only attr3 accesses, because it is undefined. The __getattribute__ version, on the other hand, intercepts all attribute fetches and must route those it does not manage to the superclass fetcher to avoid loops:

1

2

get: attr3

3

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

get: attr1

1

get: attr2

2

get: attr3

3

Although __getattribute__ can catch more attribute fetches than __getattr__, in practice they are often just variations on a theme—if attributes are not physically stored, the two have the same effect.

Management Techniques Compared

To summarize the coding differences in all four attribute management schemes we’ve seen in this chapter, let’s quickly step through a more comprehensive computed-attribute

Return Main Page Previous Page Next Page

®Online Book Reader