Online Book Reader

Home Category

Learning Python - Mark Lutz [547]

By Root 1947 0
class, but still allows the class itself to access those names freely within its methods. It’s not exactly C++ or Java, but it provides similar access control as an option in Python.

We saw an incomplete first-cut implementation of instance attribute privacy for changes only in Chapter 29. The version here extends this concept to validate attribute fetches too, and it uses delegation instead of inheritance to implement the model. In fact, in a sense this is just an extension to the attribute tracer class decorator we met earlier.

Although this example utilizes the new syntactic sugar of class decorators to code attribute privacy, its attribute interception is ultimately still based upon the __getattr__ and __setattr__ operator overloading methods we met in prior chapters. When a private attribute access is detected, this version uses the raise statement to raise an exception, along with an error message; the exception may be caught in a try or allowed to terminate the script.

Here is the code, along with a self test at the bottom of the file. It will work under both Python 2.6 and 3.0 because it employs 3.0 print and raise syntax, though it catches operator overloading method attributes in 2.6 only (more on this in a moment):

"""

Privacy for attributes fetched from class instances.

See self-test code at end of file for a usage example.

Decorator same as: Doubler = Private('data', 'size')(Doubler).

Private returns onDecorator, onDecorator returns onInstance,

and each onInstance instance embeds a Doubler instance.

"""

traceMe = False

def trace(*args):

if traceMe: print('[' + ' '.join(map(str, args)) + ']')

def Private(*privates): # privates in enclosing scope

def onDecorator(aClass): # aClass in enclosing scope

class onInstance: # wrapped in instance attribute

def __init__(self, *args, **kargs):

self.wrapped = aClass(*args, **kargs)

def __getattr__(self, attr): # My attrs don't call getattr

trace('get:', attr) # Others assumed in wrapped

if attr in privates:

raise TypeError('private attribute fetch: ' + attr)

else:

return getattr(self.wrapped, attr)

def __setattr__(self, attr, value): # Outside accesses

trace('set:', attr, value) # Others run normally

if attr == 'wrapped': # Allow my attrs

self.__dict__[attr] = value # Avoid looping

elif attr in privates:

raise TypeError('private attribute change: ' + attr)

else:

setattr(self.wrapped, attr, value) # Wrapped obj attrs

return onInstance # Or use __dict__

return onDecorator

if __name__ == '__main__':

traceMe = True

@Private('data', 'size') # Doubler = Private(...)(Doubler)

class Doubler:

def __init__(self, label, start):

self.label = label # Accesses inside the subject class

self.data = start # Not intercepted: run normally

def size(self):

return len(self.data) # Methods run with no checking

def double(self): # Because privacy not inherited

for i in range(self.size()):

self.data[i] = self.data[i] * 2

def display(self):

print('%s => %s' % (self.label, self.data))

X = Doubler('X is', [1, 2, 3])

Y = Doubler('Y is', [−10, −20, −30])

# The followng all succeed

print(X.label) # Accesses outside subject class

X.display(); X.double(); X.display() # Intercepted: validated, delegated

print(Y.label)

Y.display(); Y.double()

Y.label = 'Spam'

Y.display()

# The following all fail properly

"""

print(X.size()) # prints "TypeError: private attribute fetch: size"

print(X.data)

X.data = [1, 1, 1]

X.size = lambda S: 0

print(Y.data)

print(Y.size())

"""

When traceMe is True, the module file’s self-test code produces the following output. Notice how the decorator catches and validates both attribute fetches and assignments run outside of the wrapped class, but does not catch attribute accesses inside the class itself:

[set: wrapped <__main__.Doubler object at 0x02B2AAF0>]

[set: wrapped <__main__.Doubler object at 0x02B2AE70>]

[get: label]

X is

[get: display]

X is => [1, 2, 3]

[get: double]

[get: display]

X is => [2, 4, 6]

[get: label]

Y is

[get: display]

Y is => [−10, −20, −30]

[get: double]

[set: label

Return Main Page Previous Page Next Page

®Online Book Reader