PDA

View Full Version : [PYTHON] decoratori per permessi agli attributi di una classe


postgres
22-02-2012, 17:53
Qualcuno sa come si possa attraverso i decoratori definire permessi di lettura (r), scrittura (w) ed entrambri (rw) agli attributi di una classe?

è un esercizio di università.

Il main è questo:



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

from selectors import *
@private('data', 'size', 'label')
@rw(’data’)
@r(’size’)
@w(’label’)
class D:
def __init__(self, label, start):
self.label = label
self.data = start
self.size = len(self.data)
def double(self):
for i in range(self.size):
self.data[i] = self.data[i] * 2
def display(self):
print(’{0} => {1}’.format(self.label, self.data))
if __name__ == "__main__":
X = D(’X is’, [1, 2, 3])
X.display(); X.double(); X.display()
try:
print(X.size)
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
try:
print(X.data)
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
print(X.getData())
X.setData([25])
print(X.getSize())
try:
X.setSize(25)
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
try:
print(X.getLabel())
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
X.setLabel(’It has worked!!!’)
X.display()


private rende privati gli attributi.

La stampa è questa:

X is
X is => [1, 2, 3]
X is => [2, 4, 6]
TypeError: private attribute fetch: size
TypeError: private attribute fetch: data
[2, 4, 6]
1
Error: setter inexistent: setSize
Error: getter inexistent: getLabel
It has worked!!! => [25]


Forse vanno usati i setattr e i getattr come in private?
Ho provato così ma non funziona:

def r(*r):
def onDecorator(aClass):
class onInstance:
def __init__(self, *args, **kargs):
self.wrapped = aClass(*args, **kargs)
def getdata(self, attr):
#trace('get:', attr)
if attr in rw:
raise TypeError('getter inexistent: getsize: ' + attr)
else:
return getattr(self.wrapped, attr)
return onInstance # Or use __dict__
return onDecorator

e anche così:

traceMe = False
def trace(*args):
if traceMe: print('[' + ' '.join(map(str, args)) + ']')
def r(*r): # privates in enclosing scope
def onDecorator(aClass):
class onInstance:
def __init__(self, *args, **kargs):
self.wrapped = aClass(*args, **kargs)
def __getattr__(self, attr):
trace('get:', attr)
if attr in pr:
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)
return onInstance
return onDecorator

cdimauro
22-02-2012, 18:45
L'idea dei decoratori è senz'altro corretta, come pure quella di intercettare i metodi speciali __getattr__ e __setattr__.

Comunque sistema un po' il codice iniziale, perché senza l'indentazione corretta in Python è illeggibile.

postgres
22-02-2012, 19:04
si scusa è vero non me ne sono accorto. Ecco la seconda parte indentata


@private('data', 'size', 'label')
#@rw('data')
@r('size')
#@w('label')

class D:
def __init__(self, label, start):
self.label = label
self.data = start
self.size = len(self.data)

def double(self):
for i in range(self.size):
self.data[i] = self.data[i] * 2
def display(self):
print('{0} => {1}'.format(self.label, self.data))


'''
X is
X is => [1, 2, 3]
X is => [2, 4, 6]
TypeError: private attribute fetch: size
TypeError: private attribute fetch: data
[2, 4, 6]
1
Error: setter inexistent: setSize
Error: getter inexistent: getLabel
It has worked!!! => [25]
'''

if __name__ == "__main__":
X = D('X is', [1, 2, 3])
#X.display(); X.double(); X.display()
try:
print(X.size)
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
try:
print(X.data)
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
print(X.getData())
X.setData([25])
print(X.getSize())
try:
X.setSize(25)
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
try:
print(X.getLabel())
except Exception as e: print("{0}: {1}".format(type(e).__name__, e))
X.setLabel('It has worked!!!')
X.display()

cdimauro
23-02-2012, 07:23
Questo è un più complicato e mi richiede più tempo per l'analisi del codice e fare qualche prova.

Soltanto un paio di cose:
from selectors import *
Questo immagino sia il modulo dove sono memorizzati gli altri 3 decoratori (r, w, rw).

Poi cosa intendi esattamente per attributo "privato"? Uno che sia accessibile soltanto tramite un metodo definito all'interno della classe stessa?

Giusto per essere chiari, supponendo che x sia definito come attributo privato:
class c:

def __init__(self):
self.x = 'OK'

def Prova(self):
print self.x

i = c()

i.Prova() # Stampa OK, perché x è accessibile dall'interno della classe

i.x # Genera un errore, perché non ci si deve poter accedere dall'esterno