如何将某些属性从包装值“提升”到包装器?
Posted
技术标签:
【中文标题】如何将某些属性从包装值“提升”到包装器?【英文标题】:How can I 'lift' certain attributes from a wrapped value to the wrapper? 【发布时间】:2014-01-17 12:20:11 【问题描述】:我有一个包装了另一个类的实例的类:
class Wrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
class WrapperList(object):
def __init__(self, wrapped):
self.wrapped = wrapped
class Value(object):
is_square = True
class OtherValue(object):
is_round = True
w = Wrapper(TheValue())
wl = WrapperList([TheValue(), OtherValue()])
我还有其他一些使用 Wrapper 的代码,但需要检查包装值的属性。消费者不知道该值已被包装。
def process(value):
if value.is_square:
do_something(value)
process(w)
但不同的实现并不关心值是否为正方形,它只关心是否为圆形:
def process(value):
if value.is_round:
do_something(value)
process(w2)
我一直在做什么,是这样的:
class WrapperList(object):
is_square = False
is_round = False
def __init__(self, wrapped):
self.wrapped = wrapped
self.is_square = any(w.is_square for w in wrapped if hasattr(w, 'is_square'))
self.is_round = any(w.is_round for w in wrapped if hasattr(w, 'is_round'))
但出于多种原因,这并不理想。一方面,不同的实现可能想知道is_triangle
是否有什么东西。现在 Wrapper 也需要知道该属性。此外,用户可以定义自己的值类型和处理包装值的自己的实现。
我想到了两种方法来处理这个问题,但我并不偏爱任何一种方法,如果有任何意见,我将不胜感激。第一种是使用__getattr__
:
class Wrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
if hasattr(self.wrapped, name):
return getattr(self.wrapped, name)
else:
raise AttributeError
这看起来没问题,但是当我们谈论包装值类型列表的 Wrapper 时就变得有点困难了。
第二个选项更有创意,但对我来说就像一个巨大的黑客,因为它需要知道它正在被包装的价值(这不是一个大问题,它只是闻起来):
class Value(object):
is_square = True
def __init__(self):
self.promoted_attributes = 'is_square': True
class Wrapper(object):
def __init__(self, wrapped):
for attr, val in wrapped.promoted_attributes.items():
setattr(self, attr, val)
第二种方法的好处是能够非常明确地说明哪些值应该被提升到包装器中,但感觉完全不对。
有没有更好的方法来处理这种情况?我正在使用 python 2.7 和 3.3+。
【问题讨论】:
WrapperList
上的属性访问权限是什么样的? __getattr__
方法是要走的路,真的;只需返回getattr(self.wrapped, name)
并让它提高AttributeError
。
@MartijnPieters WrapperList 需要检查所有子节点并返回最大值。如果其中 3 个值具有 False 值,但 1 个具有 True 值,则应提升 True 值。还有其他非布尔值可以提升,但它们不是必需的。目前,只需要布尔类属性。
然后在WrapperList
对象的__getattr__
方法中对该功能进行编码。
【参考方案1】:
我发现您的设计有几个方面存在问题。一个是Wrapper
似乎只是WrapperList
的一个特例,带有一个包装值。以下更正此问题并以通用方式“提升”任何包装对象的属性。如果您只想提升某些属性,也可以对其进行修改以处理该问题,没有太大困难。
class AutoWrapper(object):
wrapped = ()
def __getattr__(self, attrname):
for obj in self.wrapped:
try:
return getattr(obj, attrname)
except AttributeError:
pass
raise AttributeError(attrname)
class Thing(AutoWrapper):
def __init__(self, *args):
self.wrapped = args
class TheValue(object):
is_square = True
class OtherValue(object):
is_round = True
def do_something(obj):
print('do_something to '.format(obj))
def process(value):
if value.is_square:
do_something(value)
thing1 = Thing(TheValue(), OtherValue())
process(thing1) # -> do_something to <__main__.Thing object at 0x020E5350>
【讨论】:
【参考方案2】:在WrapperList
中定义__getattr__
:
def __getattr__(self, attr):
return any(getattr(w, attr, False) for w in self.wrapped)
【讨论】:
以上是关于如何将某些属性从包装值“提升”到包装器?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 @FetchRequest 属性包装器的新 nsPredicate 动态属性与传递给 View 的对象一起使用