Reputation: 4194
How to restrict the creation of an attribute, outside its class/sub-class?
__all__ = ["Employee","Salary","Wage"]
##################################################################################################
class Person(object):
def __init__(self,fname,lname,gender):
self.__setfname(fname) # "We are all adults." vs. "Name mangling."
self.__setlname(lname)
self.__setgender(gender)
def __setfname(self,fname): self.__fname = fname
def __setlname(self,lname): self.__lname = lname
def __setgender(self,gender): self.__gender = gender
def getname(self): return "{} {}".format(self.__fname,self.__lname)
def getformattedname(self):
if(self.__gender.lower() == "m"):
return "Mr. {}".format(self.getname())
if(self.__gender.lower() == "f"):
return "Ms. {}".format(self.getname())
if(self.__gender.lower() == ""):
return "{}".format(self.getname())
class Payment(object):
def __init__(self,amount,currency="INR"): # currency="USD"
self.__setamount(amount)
self.__setcurrency(currency)
def __setamount(self,amount): self.__amount = amount
def __setcurrency(self,currency): self.__currency = currency
def getamount(self): return "{}".format(self.__amount)
def getformattedamount(self): return "{} {}".format(self.getamount(),self.__currency)
##################################################################################################
##################################################################################################
class Employee(Person):
def __init__(self,fname,lname,gender): super(Employee,self).__init__(fname,lname,gender)
def __str__(self): return self.getformattedname()
class Salary(Payment):
def __init__(self,amount,currency="INR"): super(Salary,self).__init__(amount,currency)
def __str__(self): return self.getformattedamount()
class Wage(Payment):
def __init__(self,amount,currency="INR"): super(Wage,self).__init__(amount,currency)
def __str__(self): return self.getformattedamount()
##################################################################################################
I'm OK with this:
e1._Person__fname = "Spam"
s1._Payment__amount = "1000000000000000"
but the following code creates new attributes:
e1.fname = "New"
s1.amount = -10
import re
from com.example.model import Employee,Salary,Wage
def printzip(l1,l2): print(list(zip([str(e) for e in l1],[str(e) for e in l2])))
(e1,e2) = (Employee("Sandeep","Mehta","m"),Employee("Varsha","Mehta","f"))
(s1,s2) = (Salary(3000,"USD"),Salary(3000,"USD"))
printzip([e1,e2],[s1,s2])
e1.fname = "New"
s1.amount = -3000
e1._Person__fname = "Spam"
s1._Payment__amount = "3000000000000000"
for e in enumerate([e for e in dir(e1) if not (re.search(r"^__.*__$",e))]): print(e)
for e in enumerate([e for e in dir(s1) if not (re.search(r"^__.*__$",e))]): print(e)
printzip([e1],[s1])
Upvotes: 2
Views: 1075
Reputation: 250891
You can do something like this:
class A:
def __setattr__(self, attr, val):
try :
class_name, varname = attr.split('__', 1)
if class_name == '_' + self.__class__.__name__:
self.__dict__[attr] = val
else:
raise AttributeError
except ValueError:
raise AttributeError
Demo:
>>> %run so.py
>>> a = A()
>>> a._A__foo = 1
>>> a._A__bar = 2
>>> a._A = 2
Traceback (most recent call last):
File "<ipython-input-28-eace128dbfc5>", line 1, in <module>
a._A = 2
File "/home/monty/py/so.py", line 10, in __setattr__
raise AttributeError
AttributeError
>>> a.A__bar = 2
Traceback (most recent call last):
File "<ipython-input-29-57210782cd6a>", line 1, in <module>
a.A__bar = 2
File "/home/monty/py/so.py", line 8, in __setattr__
raise AttributeError
AttributeError
>>> a._A__spam = 3
>>> a.__dict__
{'_A__foo': 1, '_A__spam': 3, '_A__bar': 2}
Upvotes: 3
Reputation: 599490
Do not try to do this. It's not your business what someone else does with your class. You should document what attributes are expected to be available, and if someone wants to abuse it that's their problem, not yours.
"We're all consenting adults here" is the Pythonic philosophy.
Upvotes: 6
Reputation: 97575
Are you looking for properties?
class Foo(object):
def __init__(self):
self._amount = 0
@property
def amount(self):
return self._amount
f = Foo()
print f.amount # ok
f.amount = 100 # error
Upvotes: 3
Reputation: 523214
We could abuse __slots__
, though the primary purpose of __slots__
is not to avoid creating extra attributes:
class Person(object):
__slots__ = ('_Person__fname', '_Person__lname', '_Person__gender')
...
class Employee(Person):
__slots__ = () # All subclasses also need define __slots__
...
e1._Person__fname = "Spam" # Fine
e1.fname = "New" # AttributeError: 'Employee' object has no attribute 'fname'
Note that with __slots__
, the instance's __dict__
is not created, which may break some code using them.
Upvotes: 4