有没有办法让Python @property
一次充当setter和getter?
我觉得我以前在某个地方见过这个但是不记得了,也无法自己重新创建解决方案。
例如,而不是:
class A(object):
def __init__(self, b): self.b = b
def get_c(self): return self.b.c
def set_c(self, value): self.b.c = value
c = property(get_c, set_c)
我们可以以某种方式表示对于A
个对象,c
属性对于getter,setter(以及我们喜欢的删除函数)实际上等同于b.c
。
当我们需要A
作为B
对象(其中b
是一个实例)的代理包装器但仅共享数据属性而没有方法时,这将特别有用。这些属性允许A
和B
对象的数据保持完全同步,而两者都由相同的代码使用。
答案 0 :(得分:3)
有decorator syntax for creating properties,然后有完整的custom-defined descriptors,但由于在Python和Python社区中主动阻止了setter / getter伪私有模式,因此实际上并没有广泛分布或常用的方式来做你想要的。
对于代理对象,您可以使用__getattr__
,__setattr__
和__getattribute__
,或尝试通过使用__new__
或{{0}来欺骗过程中的某些内容{1}}。
答案 1 :(得分:2)
如果您尝试将任何A对象的大量属性委托给其b
成员,那么在__getattr__
,__setattr__
和{{{{}}}内部执行该操作可能更容易。 1}},例如:
__delattr__
为简洁起见,我在此处未显示class A(object):
delegated = ['c', 'd', 'e', 'f']
def __getattr__(self, attr):
if attr in A.delegated:
return getattr(self.b, attr)
raise AttributeError()
和__setattr__
定义,并且无需解释__delattr__
和__getattr__
之间的区别。如果您需要更多信息,请参阅the docs。
这很容易扩展到想要将不同属性代理到不同成员的类:
__getattribute__
如果您需要动态委派所有属性,那几乎一样容易。您只是在初始时或通话时获得class A(object):
b_delegated = ['c', 'd', 'e', 'f']
x_delegated = ['y', 'z']
def __getattr__(self, attr):
if attr in A.b_delegated:
return getattr(self.b, attr)
elif attr in A.x_delegated:
return getattr(self.x, attr)
else:
raise AttributeError()
的属性列表(或self.b
)(四种可能性中的哪一种取决于您想要做什么),并使用该列表代替静态列表self.b.__class__
。
您当然可以按名称(例如,删除b_delegated
方法)或按类型或任意谓词(例如,删除任何_private
属性)对此进行过滤。
或者结合以上任何一种。
无论如何,这是在Python中进行(特别是动态)代理的惯用方法。它并不完美,但试图发明一种不同的机制可能不是一个好主意。
事实上,它并不是真正意义上的完美。这是你不应该经常做的事情,不应该在你做的时候试图伪装。很明显,callable
或ctypes.cdll
模块实际上委托给其他人,因为它对用户来说实际上很有用。如果你真的需要将一个类的大多数公共接口委托给另一个类,并且不希望用户知道委托...也许你不需要它。也许最好直接公开私有对象,或者重新组织对象模型,以便用户首先与正确的事物进行交互。
答案 2 :(得分:2)
我认为您正在寻找在ActiveState上发布的this forwardTo class。
此配方允许您透明地将属性访问转发给另一个 你班上的对象。这样,您可以公开某些功能 您的班级实例的成员,例如foo.baz()而不是 foo.bar.baz()。
class forwardTo(object):
"""
A descriptor based recipe that makes it possible to write shorthands
that forward attribute access from one object onto another.
>>> class C(object):
... def __init__(self):
... class CC(object):
... def xx(self, extra):
... return 100 + extra
... foo = 42
... self.cc = CC()
...
... localcc = forwardTo('cc', 'xx')
... localfoo = forwardTo('cc', 'foo')
...
>>> print C().localcc(10)
110
>>> print C().localfoo
42
Arguments: objectName - name of the attribute containing the second object.
attrName - name of the attribute in the second object.
Returns: An object that will forward any calls as described above.
"""
def __init__(self, objectName, attrName):
self.objectName = objectName
self.attrName = attrName
def __get__(self, instance, owner=None):
return getattr(getattr(instance, self.objectName), self.attrName)
def __set__(self, instance, value):
setattr(getattr(instance, self.objectName), self.attrName, value)
def __delete__(self, instance):
delattr(getattr(instance, self.objectName), self.attrName)
要获得更强大的代码,您可以考虑将getattr(instance, self.objectName)
替换为operator.attrgetter(self.objectName)(instance)
。这样,objectName
就会成为虚线名称(例如,您可以让A.c
成为A.x.y.z.d
的代理。)
答案 3 :(得分:1)
def make_property(parent, attr):
def get(self):
return getattr(getattr(self, parent), attr)
def set(self, value):
setattr(getattr(self, parent), attr, value)
return property(get, set)
class A(object):
def __init__(self, b): self.b = b
c = make_property('b', 'c')
答案 4 :(得分:0)
这是另一种方法,静态地将属性从一个对象转发到另一个对象,但要经济。
它允许在两行中转发 get/set 属性,在一行中转发仅区域属性,利用类级别和 lambda 的动态属性定义。
class A:
"""Classic definition of property, with decorator"""
_id = ""
_answer = 42
@property
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
@property
def what(self):
return self._answer
class B:
obj = A()
# Forward "id" from self.obj
id = property(lambda self: self.obj.id,
lambda self, value: setattr(self.obj, "id", value))
# Forward read-only property from self.obj
what = property(lambda self: self.obj.what)