2

Let's have a simple class with values and methods to set them:

class Object:
  def __init__(self, id):
      self.__id = id
      self.__value1 = None
      self.__value2 = None
      ... etc

  def set_value1(self, value1):
      self.__value1 = value1
  def set_value2(self, value2):
      self.__value2 = value2
      ... etc

Can I somehow merge these .set_valueX(valueX) -functions to be one, how does that happen and is it easily done without import libraries?

  • So is it possible or do I have to write the same repeating .set_value() functions for every __init__ separately? – user3573481 Apr 25 '14 at 21:41

4 Answers4

0

It can be done using setatter:

class obj(object):

    def __setter(self,**kwargs):
        key,value = kwargs.items()[0]
        setattr(self,"__"+key,value)

    def __init__(self,id):
        self.id = id
        self.__value1 = None
        self.__value2 = None
        #You should assign setter as the set funciton like this:
        self.set_value1 = self.__setter
        self.set_value2= self.__setter
        #and so on...

The init method could be done shorter, using the same technique.

Yotam Vaknin
  • 676
  • 4
  • 19
0

If you are willing to compromise and use one function that handles the logic for all setters, you can do

class Test:
    def __init__(self):
        self._value1 = 1
        self._value2 = 2
        self._value3 = 3

    def set_value(self, attr, value):
        setattr(x, attr, value)

and access each method setter via

test_obj = Test()
test_obj.set_value("_value1", 10)

However, I wouldn't recommend doing either approach. Instead, you may want to use getters and setters via @property decorators.

It is a more "Pythonic" approach and more information can be found in

https://docs.python.org/2/library/functions.html#property

DanGar
  • 3,018
  • 17
  • 17
0

I'll preface my answer by saying that getters and setters are not very pythonic and seem in this case unnecessary. If you need to customize the get/set behavior consider using properties (Python @property versus getters and setters), otherwise there's no reason not to get and set in the typical way (obj.foo = 'bar', x = obj.foo).

But I suppose if you really wanted to...a little __getattr__ magic could do it:

class A(object):
    def __init__(self, id):
        self.__id = id
        self.__value1 = None
        self.__value2 = None

    def _setter(self, name):
        def set(value):
            setattr(self, name, value)
        return set

    def __getattr__(self, name):
        if name.startswith('set_'):
            varname = name.split('_', 1)[-1]
            return self._setter('__' + varname)
        return super(A, self).__getattr__(name)

allows for...

In [1]: a = A('123')

In [2]: a.set_value1(5)

In [3]: a.__value1
Out[3]: 5

In [4]: a.set_value2(7)

In [5]: a.__value2
Out[5]: 7
Community
  • 1
  • 1
danf1024
  • 421
  • 3
  • 3
0

It's simple enough to do, though it would help a lot to know why you are trying to do it. The most Pythonic approach would possibly be to have a metaclass that creates all your accessor functions. If you were trying to do this right, I would look up the python property paradigm. However, if all of your accessor functions are identical, why do you need separate functions for them? You can easily just set values via:

x = Object()
x.value = value

... So long as you dispense with the entirely unnecessary double-underscore, at least. If you really need to alter every call to every attribute, you can override __setattr__ (Python Data Model Reference). As a side note, in every group I've worked with, the __variable approach has been highly discouraged. It doesn't stop anyone from actually accessing the value (which you can do via instance._Object__value1), but it makes subclassing horrible down the line.

If you want to just do the same pre-processing for every property get or set, you could always do something like:

class Object:
   _ATTR_NAMES = ('value1', 'value2', ...)
   def __init__(self):
      for name in self._ATTR_NAMES:
          setattr(self, "_" + name, None)

   def set_attr(self, name, value):
      if name in self._ATTR_NAMES:
         setattr(self, name[1:], value)
      else:
         raise AttributeError("No attribute named %s found"%name)

Python's setattr is a function that sets a property with a given name on the attribute. In this case, you can simply have a list of properties, set them based on that, and retrieve them based on that using the corresponding 'getattr' function. Again though, I am wary of your reasons for doing this. Typically explicit is better than implicit. It's seldom hard to just make an extra get/set function that makes referencing much more clear.

If you truly must, you can also generate get/set functions using techniques very similar to the ones I just showed for properties (i.e., create functions on the fly that wrap set_attr), but again, it usually makes code much harder to maintain and read. I would avoid many of these techniques unless you really have a strong and clear need to do so. If you really have to do it, something like this would work:

class Object:
   _ATTR_NAMES = ('value1', 'value2', ...)
   def __init__(self):
      for name in self._ATTR_NAMES:
          setattr(self, "_" + name, None)

   def _set_attr(self, name, value):
      if name in self._ATTR_NAMES:
         setattr(self, name[1:], value)
      else:
         raise AttributeError("No attribute named %s found"%name)

# This binds named set functions to the class
for name in Object._ATTR_NAMES:
   def tempSetter(self, value):
       self._set_attr(name, value)
   setattr('set_'+name, tempSetter)
del name
del tempSetter

This is a hacky way of doing it. It is more elegant to do this sort of processing in a metaclass. However, as it takes a lot of background to understand, I'm going to skip that. If you need to do pre-processing before each set function, it is generally much preferred to do something along the lines of:

def _preprocessing(self, value):
   # Do something here
   return value

def set_value1(self, value):
   value = self._preprocessing(value)
   self._value1 = value

def set_value2(self, value):
   value = self._preprocessing(value)
   self._value2 = value

Finally, if all you are worried about is the time to generate boilerplate get/set functions, then just write a script that writes the skeleton of your Python class as a text file. I have these types of scripts that outline my unit test cases based on the original modules. Or have a script macro in your text editor. Code is read many more times than it is written, so don't make complex code just to save a few keystrokes.

Namey
  • 1,172
  • 10
  • 28