Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attribute mapping with a Python property

Tags:

python

Is there a way to make a Python @property act as a setter and getter all at once?

I feel like I've seen this somewhere before but can't remember and can't recreate the solution myself.

For example, instead of:

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)

we could somehow signal that for A objects, the c attribute is really equivalent to b.c for getter, setter (and deleter if we like).

Motivation:

This would be particularly useful when we need A to be a proxy wrapper around B objects (of which b is an instance) but share only the data attributes and no methods. Properties such as these would allow the A and B objects' data to stay completely in sync while both are used by the same code.

like image 731
bossylobster Avatar asked Jun 15 '26 08:06

bossylobster


1 Answers

I think you are looking for this forwardTo class as posted on ActiveState.

This recipe lets you transparently forward attribute access to another object in your class. This way, you can expose functionality from some member of your class instance directly, e.g. foo.baz() instead of 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)

For a more robust code, you may want to consider replacing getattr(instance, self.objectName) with operator.attrgetter(self.objectName)(instance). This would allow objectName to be a dotted name (e.g., so you could have A.c be a proxy for A.x.y.z.d).

like image 181
bfroehle Avatar answered Jun 17 '26 21:06

bfroehle



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!