سؤال

Is there any way to set a property in Python using a function call instead of the "a.x = value" syntax?

Explanation: There is a class that I didn't write. I have to query and set various pieces of data from this class. Most of the items have setter and getter functions, like getA() and setA(value). But a few of the items have been declared as properties. I have some code where I get the setter function of the various items, so that I can loop over them. But It does not work for properties because there is not a setter function.

class DataObject(object):
    def __init__(self, data, setterFunc):
        super(DataObject, self).__init__()
        self._data = data
        self._setterFunc = setterFunc

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        if self._setterFunc:
            self._data = value
            self._setterFunc(value)

foo = SomeClassIDidNotWrite()
objectList = []
objectList.append(DataObject(foo.getA(), foo.setA))
objectList.append(DataObject(foo.getB(), foo.setB))
# objectList.append(DataObject(foo.c, foo.c))   <-- This does not work

for dataObj in objectList:
    dataObj.data = 5
هل كانت مفيدة؟

المحلول

You can set attributes dynamically with the setattr() function; this applies to attributes that are really properties too.

You can create a setter with a lambda function or with functools.partial():

objectList.append(DataObject(foo.c, lambda val: setattr(foo, 'c', val)))

or

from functools import partial

objectList.append(DataObject(foo.c, partial(setattr, foo, 'c')))

نصائح أخرى

@MartijnPieters answer is the way to go if possible, but if you need to try to dynamically derive the setters, you can also access the property's descriptor on the class directly, providing the instance as an argument.

foo = SomeClassIDidNotWrite()
SomeClassIDidNotWrite.a.fset(foo, val)

Or even more dynamically:

attr_name = 'a'
getattr(foo.__class__, attr_name).fset(foo, val)

Combining this with your existing get/set code and wrapping it in try/except, you can create a generic handler for all the attributes you need to work with.

for attr_name in ('A', 'B', 'C'):
    try:
        getter = getattr(foo, 'get' + attr_name)
        setter = getattr(foo, 'set' + attr_name)
    except AttributeError:
        descriptor = getattr(foo.__class__, attr_name)
        getter = functools.partial(descriptor.fget, foo)
        setter = functools.partial(descriptor.fset, foo)
    objectList.append(DataObject(getter, setter))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top