Reputation: 113
Problem: I'm creating a class with attributes. Some of them should be only readable and some should be completely inaccessible when trying to print them out. I'm trying to achieve it through __setattribute__
and __getattribute__
functions (I just couldn't think of a better way to do that, if you have any suggestions on how to improve this, I would greatly appreciate it). The problem is that while __getattribute__
function works perfectly fine and raises needed exception, the __setattribute__
doesn't. The program simply passes it.
Code:
class Fruit:
def __setattribute__(self, name, value):
if name in ('exp_date'):
raise AttributeError('Unable to edit')
return __setattribute__(self, name, value)
def __getattribute__(self, name):
if name in ('subtype'):
raise AttributeError('Unable to view this object')
return __getattribute__(self, name)
def __init__(self, sub:str = None, col:str = None, expd:int = None, pkp:int = None, amount:int = None):
self.color, self.price_per_kilo = col, pkp
self.amount = amount
self.exp_date = expd
self.subtype = sub
def estimaterevenue(self):
return self.price_per_kilo * self.amount
Test:
fruit= Fruit()
fruit.exp_date = 123 # __set__ is being passed for some reason
print(fruit.subtype) # AttributeError: Unable to view this object
Upvotes: 0
Views: 1470
Reputation: 42748
__get_attribute__
and __set_attr_
are complicated to understand, better use a simple propert:
class Fruit:
def __init__(self, subtype=None, color=None, exp_date=None, price_per_kilo=None, amount=None):
self.color = color
self.price_per_kilo = price_per_kilo
self.amount = amount
self._exp_date = exp_date
self._subtype = subtype
def estimaterevenue(self):
return self.price_per_kilo * self.amount
@property
def exp_date(self):
return self._exp_date
fruit= Fruit()
fruit.exp_date = 123 # AttributeError
print(fruit.subtype) # AttributeError: Unable to view this object
Upvotes: 2
Reputation: 2962
The following implementation uses a counter so you can set the value once in the constructor but not subsequently.
class Fruit:
def __setattr__(self, name, value):
if name in ('exp_date') and self.count_exp_date>=1:
raise AttributeError('Unable to edit')
# return __setattribute__(self, name, value)
if name in ('exp_date'):
self.count_exp_date+=1
super().__setattr__(name, value)
def __getattribute__(self, name):
if name in ('subtype'):
raise AttributeError('Unable to view this object')
return super().__getattribute__(name)
def __init__(self, sub:str = None, col:str = None, expd:int = None, pkp:int = None, amount:int = None):
self.count_exp_date = 0
self.color, self.price_per_kilo = col, pkp
self.amount = amount
self.exp_date = expd
self.subtype = sub
def estimaterevenue(self):
return self.price_per_kilo * self.amount
Upvotes: 0
Reputation: 39354
There seems to be some confusion about which dunder methods are defined. I don't know either, but I think this does what you want:
class Fruit:
def __setattr__(self, name, value):
print('set attribute', name)
if name in ('exp_date'):
raise AttributeError('Unable to edit')
return super().__setattr__(name, value)
Update:
The above code does not work because exp_date
cannot be set inside __init__()
.
This code works around that:
class Fruit:
def __setattr__(self, name, value):
#print('set attribute', name)
if name in 'attr_gate' or self.attr_gate or name not in 'exp_date':
return super().__setattr__(name, value)
raise AttributeError('Unable to edit')
def __getattribute__(self, name):
#print('get attribute', name)
if name in ('subtype'):
raise AttributeError('Unable to view this object')
return super().__getattribute__(name)
def __init__(self, sub:str = None, col:str = None, expd:int = None, pkp:int = None, amount:int = None):
self.attr_gate = True
self.color, self.price_per_kilo = col, pkp
self.amount = amount
self.exp_date = expd
self.subtype = sub
self.attr_gate = False
Upvotes: 1