我正在尝试跟踪可变python对象(例如,list tor dictionary)的条目的外部修改。这种能力在以下两种情况下特别有用:
1)当人们想避免将不需要的值分配给mutable python对象时。这是一个简单的例子,其中x必须只是一个整数列表:
class foo(object):
def __init__(self,x):
self.x = x
def __setattr__(self,attr_name,attr_value):
# x must be a list of integers only
if attr_name == 'x' and not isinstance(attr_value,list):
raise TypeError('x must be a list!')
elif attr_name == 'x' and len([a for a in attr_value if not isinstance(a,int)]) > 0:
raise TypeError('x must be a list of integers only')
self.__dict__[attr_name] = attr_value
# The following works fine and it throws an error because x has a non-integer entry
f = foo(x = ['1',2,3])
# The following assigns an authorized list to x
f = foo(x = [1,2,3])
# However, the following does not throw any error.
#** I'd like my code to throw an error whenever a non-integer value is assigned to an element of x
f.x[0] = '1'
print 'f.x = ',f.x
2)在修改可变Python对象后需要更新许多其他变量时。这是一个示例,其中x是字典,只要对x_vals
进行任何更改(例如删除条目或为特定键分配新值),x
就需要更新:
class foo(object):
def __init__(self,x,y = None):
self.set_x(x)
self.y = y
def set_x(self,x):
"""
x has to be a dictionary
"""
if not isinstance(x,dict):
raise TypeError('x must be a dicitonary')
self.__dict__['x'] = x
self.find_x_vals()
def find_x_vals(self):
"""
NOTE: self.x_vals needs to get updated each time one modifies x
"""
self.x_vals = self.x.values()
def __setattr__(self,name,value):
# Any Changes made to x --> NOT SURE HOW TO CODE THIS PART! #
if name == 'x' or ...:
raise AttributeError('Use set_x to make changes to x!')
else:
self.__dict__[name] = value
if __name__ == '__main__':
f = foo(x={'a':1, 'b':2, 'c':3}, y = True)
print f.x_vals
# I'd like this to throw an error asking to use set_x so self.x_vals
# gets updated too
f.x['a'] = 5
# checks if x_vals was updated
print f.x_vals
# I'd like this to throw an error asking to use set_x so self.x_vals gets updated too
del f.x['a']
print f.x_vals
答案 0 :(得分:1)
您无法使用property
,因为您要保护的内容是可变的,property
仅对get
,set
和{{1}有帮助对象本身,而不是对象内部状态。
你可以做的是创建一个delete
子类(如果你只需要几个dict
能力,那么只是看起来像一个)来管理访问。然后,您的自定义类可以管理dict
,__getitem__
和__setitem__
方法。
问题修订的更新
我的原始答案仍然有效 - 无论您使用__delitem__
还是property
1 ,您仍然遇到基本问题:一旦您移交了检索到的属性,您就没有了控制它发生了什么以及它做了什么。
您有两种方法可以解决此问题:
创建要保护的类的子类,并在其中加入限制(来自我的原始答案),或
创建一个通用包装器作为网关。
网关包装器的一个非常粗略的例子:
__getattribute__
和一个简单的例子:
class Gateway():
"use this to wrap an object and provide restrictions to it's data"
def __init__(self, obj, valid_key=None, valid_value=None):
self.obj = obj
self.valid_key = valid_key
self.valid_value = valid_value
def __setitem__(self, name, value):
"""
a dictionary can have any value for name, any value for value
a list will have an integer for name, any value for value
"""
valid_key = self.valid_key
valid_value = self.valid_value
if valid_key is not None:
if not valid_key(name):
raise Exception('%r not allowed as key/index' % type(name))
if valid_value is not None:
if not valid_value(value):
raise Exception('%r not allowed as value' % value)
self.obj[name] = value
要使用huh = Gateway([1, 2, 3], valid_value=lambda x: isinstance(x, int))
huh[0] = '1'
Traceback (most recent call last):
...
Exception: '1' not allowed as value
,您需要覆盖更多方法,例如Gateway
(append
)。
1 不建议使用list
,因为它是控制所有属性查找方面的部分。这很容易出错。
答案 1 :(得分:1)
你可以x_vals
这样的属性:
@property
def x_vals(self):
return self.x.values()
每次访问时,它都会使x_vals
保持最新状态。它会更快,因为每次更改x
时都不必更新它。
如果您唯一的问题是让x_vals
保持最新状态,那么它就会解决问题,并为您节省子类化内容的麻烦。