Last active
July 10, 2016 14:55
-
-
Save nabeelio/22e93a65799a4d7966052e8441410f3f to your computer and use it in GitHub Desktop.
Create properties dynamically without having to create explicit setters and getters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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__ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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