以下是我正在寻找的行为:
>>> 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'
换句话说,我希望以类似于defaultdict的方式覆盖__getattr__
和__setattr__
(以及可能__getattribute__
),允许分配任意属性,但如果是属性只是被引用但未分配给它,它会像往常一样抛出AttributeError。
这可能吗?
答案 0 :(得分: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都在做同样的事情。你无法区分它们以便在后一种情况下引发异常而不是前者。
答案 1 :(得分:2)
怎么样:
class AutoVivifier(object):
def __getattr__(self, key):
value = type(self)()
object.__setattr__(self,key,value)
return value
o=AutoVivifier()
o.foo.bar='baz'
print(o.foo.bar)
# baz
print(o.foo.baz)
# <__main__.AutoVivifier object at 0xb776bb0c>
o.foo.baz='bing'
print(o.foo.baz)
# bing
这不会引发任何AttributeErrors,但很容易判断属性链何时没有先前赋值 - 表达式将是Autovivifier
的实例。也就是说,isinstance(o.foo.baz,AutoVivifier)
为True。
我认为实现更加清晰,而不是定义各种特殊方法,如__str__
和__eq__
来提升AttributeErrors。
我仍然不清楚为什么你需要首先引发AttributeErrors,但也许使用AutoVivifier你可以编写实现目标的函数或方法,isinstance(...,AutoVivifier)
测试替换try...except AttributeError
块
答案 2 :(得分: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'
>>>
答案 3 :(得分:0)
[~/.src/pyusb-1.0.0-a1]
|4>class SomeClass: pass
...:
[~/.src/pyusb-1.0.0-a1]
|5>o = SomeClass()
[~/.src/pyusb-1.0.0-a1]
|6>o.foo='bar'
[~/.src/pyusb-1.0.0-a1]
|7>print o.foo
bar
[~/.src/pyusb-1.0.0-a1]
|8>print o.baz
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
AttributeError: SomeClass instance has no attribute 'baz'
[~/.src/pyusb-1.0.0-a1]
|9>
答案 4 :(得分:0)
这真的很讨厌,但也许是你想要的开始:
class SomeClass(object):
def __init__(self):
object.__setattr__(self, "_SomeClass__children", {})
object.__setattr__(self, "_SomeClass__empty", True)
def __getattr__(self, k):
if k not in self.__children:
self.__children[k] = SomeClass()
return self.__children[k]
def __setattr__(self, k, v):
object.__setattr__(self, "_SomeClass__empty", False)
object.__setattr__(self, k, v)
def __str__(self):
if not self.__hasvalue():
raise AttributeError("Never truly existed")
return object.__str__(self)
def __hasvalue(self):
if not self.__empty:
return True
return any(v.__hasvalue() for v in self.__children.itervalues())
o = SomeClass()
o.foo.bar = 'bar'
print o.foo.bar
print o.foo
print o.baz
输出:
bar
<__main__.SomeClass object at 0x7f2431404c90>
Traceback (most recent call last):
File "spam.py", line 29, in <module>
print o.baz
File "spam.py", line 17, in __str__
raise AttributeError("Never truly existed")
AttributeError: Never truly existed
答案 5 :(得分: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"