Skip to content

Instantly share code, notes, and snippets.

@carymrobbins
Last active August 29, 2015 13:56
Show Gist options
  • Save carymrobbins/8853737 to your computer and use it in GitHub Desktop.
Save carymrobbins/8853737 to your computer and use it in GitHub Desktop.
Python class properties, instance properties, and optional laziness.
# noinspection PyPep8Naming
class classproperty(object):
def __init__(self, function, name=None):
self.__name__ = name or function.__name__
self._class_property = function
self._class_property_is_lazy = False
self._cached_class_property_result = None
self._instance_property = None
self._instance_property_is_lazy = False
self._cached_instance_property_result = None
def __get__(self, instance, owner):
if instance is not None:
if not self._instance_property:
return getattr(owner, self.__name__)
if self._cached_instance_property_result:
return self._cached_instance_property_result
result = self._instance_property(instance)
if self._instance_property_is_lazy:
self._cached_instance_property_result = result
return result
elif owner is not None:
if self._cached_class_property_result:
return self._cached_class_property_result
result = self._class_property(owner)
if self._class_property_is_lazy:
self._cached_class_property_result = result
return result
return self
@classmethod
def lazy(cls, function, name=None):
prop = cls(function, name)
prop._class_property_is_lazy = True
return prop
@property
def instanceproperty(self):
return self._InstancePropertyProxy(self)
class _InstancePropertyProxy(object):
def __init__(self, prop):
""" :type prop: classproperty """
self._prop = prop
def __call__(self, function):
self._prop._instance_property = function
return self._prop
def lazy(self, function):
self._prop._instance_property = function
self._prop._instance_property_is_lazy = True
return self._prop
# noinspection PyPep8Naming
class and_instanceproperty(object):
def __init__(self, function, name=None):
self._prop = classproperty(function, name)
self._prop._instance_property = function
def __get__(self, instance, owner):
if not instance and not owner:
return self
return self._prop.__get__(instance, owner)
@classmethod
def lazy(cls, function, name=None):
prop = classproperty(function, name)
prop._class_property = function
prop._instance_property = function
prop._class_property_is_lazy = True
prop._instance_property_is_lazy = True
return prop
# Example usage:
class Class(object):
@classproperty
def a(cls):
return cls, 'class.a'
@a.instanceproperty
def a(self):
return self, 'instance.a'
@classproperty.lazy
def b(cls):
cls.b_class_eval = getattr(cls, 'b_class_eval', 0) + 1
return cls, 'class.b', cls.b_class_eval
@b.instanceproperty.lazy
def b(self):
self.b_instance_eval = getattr(self, 'b_instance_eval', 0) + 1
return self, 'instance.b', self.b_instance_eval
@classproperty
def c(cls):
return cls, 'class.c'
@classproperty.and_instanceproperty
def d(obj):
if isinstance(obj, type):
return obj, 'class.d'
return obj, 'instance.d'
@classproperty.and_instanceproperty.lazy
def e(obj):
if isinstance(obj, type):
obj.e_class_eval = getattr(obj, 'e_class_eval', 0) + 1
return obj, 'class.e', obj.e_class_eval
obj.e_instance_eval = getattr(obj, 'e_instance_eval', 0) + 1
return obj, 'instance.e', obj.e_instance_eval
def assert_equal(x, y):
if x != y:
raise AssertionError('{!r} != {!r}'.format(x, y))
instance = Class()
assert_equal(Class.a, (Class, 'class.a'))
assert_equal(instance.a, (instance, 'instance.a'))
assert_equal(Class.b, (Class, 'class.b', 1))
assert_equal(Class.b, (Class, 'class.b', 1))
assert_equal(instance.b, (instance, 'instance.b', 1))
assert_equal(instance.b, (instance, 'instance.b', 1))
assert_equal(Class.c, (Class, 'class.c'))
assert_equal(instance.c, (Class, 'class.c'))
assert_equal(Class.d, (Class, 'class.d'))
assert_equal(instance.d, (instance, 'instance.d'))
assert_equal(Class.e, (Class, 'class.e', 1))
assert_equal(Class.e, (Class, 'class.e', 1))
assert_equal(instance.e, (instance, 'instance.e', 1))
assert_equal(instance.e, (instance, 'instance.e', 1))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment