Skip to content

Instantly share code, notes, and snippets.

@nabeelio
Last active July 10, 2016 14:55
Show Gist options
  • Save nabeelio/22e93a65799a4d7966052e8441410f3f to your computer and use it in GitHub Desktop.
Save nabeelio/22e93a65799a4d7966052e8441410f3f to your computer and use it in GitHub Desktop.
Create properties dynamically without having to create explicit setters and getters
#
# Instead of doing of this for tons of properties:
#
# @property
# def prop(self):
# return self._fields.get('prop')
#
# @prop.setter
# def prop(self, value):
# self._fields['prop'] = value
# self._dirty = True
#
# This base class allows you to pass all of those properties
# into the constructor, and will use the property() call to
# dynamically generate the getters and setters, and then any
# specific logic, etc can be placed into the closure methods
# that are passed back
class LibBase(object):
def __init__(self):
""" """
super(LibBase, self).__init__()
self._id = id
self._hash = None
self._dirty = False
self._fields = {}
def register_attrs(self, cls, *args, **kwargs):
# initial fields
if not kwargs:
kwargs = {}
# dynamically set the properties on this class according
# to the kwargs. So then we don't need to create a whole
# ton of getters and setters for each property
for key in args:
kwargs[key] = None
self._fields[key] = None
for key, val in kwargs.items():
self._fields[key] = val
prop = property(fget=self.__get_method__(key),
fset=self.__set_method__(key),
fdel=self.__del_method__(key),
doc='dynamic attr "%s' % key)
setattr(cls, key, prop)
def __get_method__(self, property_name):
def __getter__(*args):
return self._fields[property_name]
return __getter__
def __set_method__(self, property_name):
def __setter__(*args):
_, val = args
self._fields[property_name] = val
self._dirty = True
return __setter__
def __del_method__(self, property_name):
def __deleter__(*args):
del self._fields[property_name]
self._dirty = True
return __deleter__
import unittest
from base import LibBase
class A(LibBase):
def __init__(self):
super(A, self).__init__()
self.register_attrs(A, 'property1', property2='value2')
self.property1 = 'value1'
class B(LibBase):
def __init__(self):
super(B, self).__init__()
self.register_attrs(B, second_prop1='secondval1',
second_prop2='secondval2')
class LibBaseTest(unittest.TestCase):
def setUp(self):
super(LibBaseTest, self).setUp()
self.base = A()
self.second = B()
def test_has_properties(self):
""" Make sure the properties are added """
self.assertTrue(hasattr(self.base, 'property1'))
self.assertTrue(hasattr(self.base, 'property2'))
self.assertEqual(self.base.property1, 'value1')
self.assertEqual(self.base.property2, 'value2')
def test_doesnt_have_properties(self):
""" test that there's no "cross talk" between instances """
self.assertFalse(hasattr(self.base, 'second_prop1'))
self.assertFalse(hasattr(self.base, 'second_prop2'))
self.assertFalse(hasattr(self.second, 'property1'))
self.assertFalse(hasattr(self.second, 'property2'))
def test_change_properties(self):
self.base.property1 = 'new_value1'
self.assertEqual(self.base.property1, 'new_value1')
self.assertTrue(self.base._dirty)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment