python拦截修改数据包_再谈python的属性拦截

__setattr__是python重载协议中的内容,用于在实例中设置属性。

实际使用方式:

class Old(object):

def __init__(self):

pass

def __setattr__(self, key, value):

print "__setattr__: ", key, value

object.__setattr__(self, key, value)

def __getattribute__(self, key):

print "__getattribute__: ", key

return object.__getattribute__(self, key)

def __getattr__(self, key):

print "run __getattr__ in undefinied param: ", key

return object.__getattribute__(self, key)

o = Old()

o.ok = 123

print o.ok

print o.undef

运行结果:

__setattr__: ok 123

__getattribute__: ok

123

__getattribute__: undef

run __getattr__ in undefinied param: undef

Traceback (most recent call last):

File "dict_object.py", line 36, in

print o.undef

File "dict_object.py", line 31, in __getattr__

return object.__getattribute__(self, key)

AttributeError: 'Old' object has no attribute 'undef'

o.undef 是一个为定义的实例属性,会被__getattr__捕捉到,但是在实例上没有这个属性,最后还是会报错。

但是如果我们现在的需求是这样,预先在实例的__init__函数上上设置一个变量,例如:self.data = {}

我们让接下来的__setattr__和__getattribute__或__getattr__的存取属性都在self.data的字典中进行,如果还是使用上面的代码,将会带来极大的麻烦。

因为我们首先要取得实例的data属性,即self.data,但是这个动作又必须经过__getattr__或__getattribute__操作,可能会这样写:

class Old(object):

def __init__(self):

self.data = {}

def __setattr__(self, key, value):

print "__setattr__: ", key, value

data = object.__getattribute__(self, 'data')

data[key] = value

def __getattribute__(self, key):

print "__getattribute__: ", key

return object.__getattribute__(self, key)

def __getattr__(self, key):

print "run __getattr__ in undefinied param: ", key

return object.__getattribute__(self, key)

o = Old()

o.ok = 123

print dir(o)

运行结果:

__setattr__: data {}

Traceback (most recent call last):

File "dict_object.py", line 35, in

o = Old()

File "dict_object.py", line 19, in __init__

self.data = {}

File "dict_object.py", line 23, in __setattr__

data = object.__getattribute__(self, 'data')

AttributeError: 'Old' object has no attribute 'data'

我们甚至连self.data都没有赋值成功! 为什么呢? 还是因为这一句:

data = object.__getattribute__(self, 'data')

因为__init__里面赋值data给self要经过__getattribute__的拦截,但是此时实例上还没有data属性,上面一行代码当然会报属性错误!

OK,我们再加上异常捕获怎么样? 尝试忽略这个异常,让我们试试:(不过在尝试之前,大家可能想到结果了)

class Old(object):

def __init__(self):

self.data = {}

def __setattr__(self, key, value):

print "__setattr__: ", key, value

try:

data = object.__getattribute__(self, 'data')

data[key] = value

except:

pass

def __getattribute__(self, key):

print "__getattribute__: ", key

return object.__getattribute__(self, key)

def __getattr__(self, key):

print "run __getattr__ in undefinied param: ", key

return object.__getattribute__(self, key)

o = Old()

o.ok = 123

print dir(o)

运行结果:

__setattr__: data {}

__setattr__: ok 123

__getattribute__: __dict__

__getattribute__: __members__

run __getattr__ in undefinied param: __members__

__getattribute__: __methods__

run __getattr__ in undefinied param: __methods__

__getattribute__: __class__['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

OK,运行成功,代码没有报错。中间淡色的部分可以忽略,它是dir函数获取实例上属性的过程。 注意加粗的部分,实例上根本没有data这个属性!

所以这个方法行不通。

那咋办捏?

有一个办法,在《Python学习手册》第4版中,提到了用实例的__dict__命名空间来预先分配好实例的属性:

class Old(object):

def __init__(self):

self.__dict__['data'] = {}

def __setattr__(self, key, value):

print "__setattr__: ", key, value

#data = object.__getattribute__(self, 'data')

#data[key] = value

# 或者直接这样:

self.data[key] = value

def __getattribute__(self, key):

print "__getattribute__: ", key

return object.__getattribute__(self, key)

def __getattr__(self, key):

print "run __getattr__ in undefinied param: ", key

return object.__getattribute__(self, key)

o = Old()

o.ok = 123

print o.data

OK,这样没问题了。

如果我们既想简单模拟类似dict类的工作方式,又想实现以属性的方式访问,我们可以直接重载__getitem__和__setitem__和__delitem__方法,并使用上面的代码:

class Old(object):

def __init__(self):

self.__dict__['data'] = {}

def __setattr__(self, key, value):

self.data[key] = value

def __getattr__(self, key):

# 这里我们用了一个取巧的方法

# 因为self.__dict__还是会被__getattribute__拦截到,导致无限递归

# 所以不能用它来实现

return self.data[key]

def __setitem__(self, key, value):

self.data[key] = value

def __getitem__(self, key):

try:

return self.data[key]

except IndexError:

pass

def __delitem__(self, key):

del self.data[key]

o = Old()

o.first = 123

print o.first

o['second'] = 456

print o['second']

特别说明:

在上面的代码中,属性访问使用了__getattr__,虽然只有在实例未定义该属性时才会被__getattr__拦截。

但是因为我们的__setattr__已经把属性保存在了self.data里面,所以每次__getattr__都会被调用,算是一种投机取巧的方式。

最终的原因还是因为,self.__dict__也会被__getattribute__拦截,所以不能在__getattribute__中访问self.__dict。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值