问题
If I have a class that contains a dictionary that I need to reuse among various instance methods, but I want to make that dictionary only writable from a specific instance method, is that possible?
In this code:
class GoodClass:
def __init__(self):
# name mangling enabled!
self.__dict = {}
def __repr__(self):
return str(self.__dict)
def add_to_dict(self, key, item):
# this should be the only method that can write to self.__dict
try:
self.__dict[key] += [item]
except KeyError:
self.__dict[key] = [item]
def make_items(self, n_items=3, important_param=1):
# do the important stuff
for i in range(n_items):
item = "param: {}".format(important_param)
self.add_to_dict("important_items", item)
def this_is_bad(self):
# don't want this to be possible
self.__dict["this is bad"] = ["quite bad"]
c = GoodClass()
c.make_items()
c.this_is_bad()
# c.__dict["can't do this"] = ["thanks mangling!"]
print(c)
# {'important_items': ['param: 1', 'param: 1', 'param: 1'], 'this is bad': ['quite bad']}
Is there a way to ensure that add_to_dict
is the only method that can write to the dictionary in the same way that mangling prevents writing to it from outside the class?
My use case is in a hosted version of IronPython, so inspect.currentframe
doesn't work as mentioned in a couple answers below. Though, that should work for unhosted IronPython or other versions.
回答1:
Although I have no idea why you would want to do this, I think this could work for you :
First approach :
class my_class:
def __init__(self):
pass
@property
def some_variable(self):
return self.__some_variable
@some_variable.setter
def some_variable(self, value):
current_frame = inspect.currentframe()
calling_function = inspect.getouterframes(current_frame, 2)[1][3]
if (calling_function != "allowed_func_name"):
raise Exception()
self.__some_variable = value
Second approach :
class my_class:
def __init__(self):
self.some_variable_set_flag = False
pass
def some_func(self, value):
self.some_variable_set_flag = True
try :
self.some_variable = value
finally :
self.some_variable_set_flag = False
@property
def some_variable(self):
return self.__some_variable
@some_variable.setter
def some_variable(self, value):
if (not self.some_variable_set_flag):
raise Exception()
self.__some_variable = value
- This approach wont give you complete protection but would require manual overriding.
Its in an abstract way (if anyone else would look for this some time).
回答2:
Here's how you can make the dict only allow setting values from the right place:
import inspect
class GoodDict(dict):
__allowed_codes = {GoodClass.add_to_dict.__code__}
def __setitem__(self, key, value):
frame = inspect.currentframe()
while frame:
if frame.f_code in GoodDict.__allowed_codes:
break
frame = frame.f_back
else:
raise Exception('__setitem__ called from non-allowed function')
super().__setitem__(key, value)
If deleting from the dict is a concern, you should also implement __delitem__
.
Then use it in GoodClass
: self.__dict = GoodDict()
.
Finally you should make the values tuples instead of lists to ensure immutability.
来源:https://stackoverflow.com/questions/50010964/python-possible-to-make-attribute-access-strictly-private