2015-12-21 56 views
4

我试图跟踪可变python对象(例如,list tor字典)的条目的外部修改。这种能力在以下两种情况下特别有用:捕获作为实例类变量的可变python对象的外部修改

1)当想避免将不需要的值分配给可变的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做得到更新x_vals

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 

回答

1

不能使用property因为你试图保护的东西是可变的,而property只能帮助对象本身,而不是对象内部状态的get ing,set ing和delete

你可以做的是创建一个dict子类(或者只是一个看起来像是如果你只需要几个dict能力)来管理访问。然后,您的自定义班级可以管理__getitem__,__setitem____delitem__方法。


更新问题修订

我原来的答复仍然是有效的 - 不管你用property__getattribute__ 你仍然有基本的问题:一旦你交出属性检索你有不能控制它发生了什么,也不能控制它发生了什么。

你有两个选择,以解决此问题:

  1. 创建要保护类的子类,并把限制在它们(从我原来的答复),或

  2. 创建作为网关的通用包装器。

一个网关包装的很粗糙例如:

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(对于list)。


使用__getattribute__是不建议,因为它是控制所有属性查找的各方面的片。很容易出错。

+0

你的意思是使用'property'就像我建议的那样? –

+0

@IlyaPeterov:不可以。但是,如果OP唯一担心的是保持'x_vals'更新,那么您的解决方案就可以正常工作。 –

+0

实际上,除了更新x_vals之外,还需要捕获不需要的值赋值给可变python对象的条目(请参阅修改后的问题中的第一个示例)。我修改了这个问题,并试图使它更一般。提供一个例子说明你如何做到让你的答案更清晰。 – user3076813

1

你可以做x_vals属性那样:

@property 
def x_vals(self): 
    return self.x.values() 

而且,它还将在每次访问时间保持x_vals最新的。事件会更快,因为每次更改x时都不必更新它。

如果你的唯一的问题是保持x_vals最新的,它要解决它,并节省您的子类的东西的麻烦。

+0

对不起,第一次写错了,编辑过。 –