Python重写__getattr__以支持嵌套赋值,但不支持引用?
我想要的行为是这样的:
>>> o = SomeClass()
>>> # Works:
>>> o.foo.bar = 'bar'
>>> print o.foo.bar
'bar'
>>> # The in-between object would be of type SomeClass as well:
>>> print o.foo
>>> <__main__.SomeClass object at 0x7fea2f0ef810>
>>> # I want referencing an unassigned attribute to fail:
>>> print o.baz
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
print o.baz
AttributeError: 'SomeClass' object has no attribute 'baz'
换句话说,我想要重写 __getattr__
和 __setattr__
(可能还包括 __getattribute__
),让它们的工作方式类似于 defaultdict。也就是说,我希望可以给任意属性赋值,但如果只是引用了某个属性而没有赋值,就应该像平常一样抛出一个 AttributeError 错误。
这可能吗?
6 个回答
2
我不太明白你的意思。这个语言的功能已经可以让你做到这一点了:
>>> class MyClass(object):
... pass
...
>>> f = MyClass()
>>> f.foo = 5
>>> print f.foo
5
>>> f.baz
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'baz'
>>>
6
在Python中,这是不可能的。
你问的其实是这个:
>>> o = SomeClass()
>>> o.foo.bar = 'bar'
>>> print o.foo.bar
'bar'
>>> a = o.baz
raises AttributeError
这件事是做不到的。没有办法区分
>>> o.foo.bar = 'bar'
和
>>> temp = o.foo
>>> temp.bar = 'bar'
它们在逻辑上是等价的,实际上Python在这两种情况下做的事情是一样的。你无法区分它们,因此不能在后者的情况下抛出异常,而在前者的情况下不抛出。
0
这是我目前的进展:
def raise_wrapper(wrapped_method=None):
def method(tmp_instance, *args, **kawrgs):
raise AttributeError("'%s' object has no attribute '%s'" % (
type(tmp_instance._parent).__name__, tmp_instance._key))
if wrapped_method:
method.__doc__ = wrapped_method.__doc__
return method
class TemporaryValue(object):
def __init__(self, parent, key):
self._parent = parent
self._key = key
def __setattr__(self, key, value):
if key in ('_parent', '_key'):
return object.__setattr__(self, key, value)
newval = ObjectLike()
object.__setattr__(self._parent, self._key, newval)
return object.__setattr__(newval, key, value)
__eq__ = raise_wrapper(object.__eq__)
# __del__ = raise_wrapper()
# __repr__ = raise_wrapper(object.__repr__)
__str__ = raise_wrapper(object.__str__)
__lt__ = raise_wrapper(object.__lt__)
__le__ = raise_wrapper(object.__le__)
__eq__ = raise_wrapper(object.__eq__)
__ne__ = raise_wrapper(object.__ne__)
__cmp__ = raise_wrapper()
__hash__ = raise_wrapper(object.__hash__)
__nonzero__ = raise_wrapper()
__unicode__ = raise_wrapper()
__delattr__ = raise_wrapper(object.__delattr__)
__call__ = raise_wrapper(object.__call__)
class ObjectLike(object):
def __init__(self):
pass
def __getattr__(self, key):
newtmp = TemporaryValue(self, key)
object.__setattr__(self, key, newtmp)
return newtmp
def __str__(self):
return str(self.__dict__)
o = ObjectLike()
o.foo.bar = 'baz'
print o.foo.bar
print o.not_set_yet
print o.some_function()
if o.unset > 3:
print "yes"
else:
print "no"