diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f5e7651839ed8786161c27344e281a5d9cf2ebe..d00e56986db626a22c4689ee99bb5ce326c852a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,3 @@
 # v2.18.5
 
-
+* added `cached_property`
diff --git a/docs/coding/decorators.rst b/docs/coding/decorators.rst
index 52020b492423541999125e7431a576aa3276fbb9..ffa3727fb06304283b8348596b162b9c807560e8 100644
--- a/docs/coding/decorators.rst
+++ b/docs/coding/decorators.rst
@@ -17,6 +17,8 @@ Decorators
 
 .. autofunction:: satella.coding.decorators.cache_memoize
 
+.. autofunction:: satella.coding.decorators.call_with_arguments
+
 .. autofunction:: satella.coding.decorators.queue_get
 
 .. autofunction:: satella.coding.decorators.copy_arguments
diff --git a/satella/__init__.py b/satella/__init__.py
index 55439584313806dde534530c3e94be98ed824605..8f1a63771ab8d1ddebe82fde27c307562c879f4b 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.18.5a1'
+__version__ = '2.18.5'
diff --git a/satella/coding/decorators/__init__.py b/satella/coding/decorators/__init__.py
index 3fc063c0f938cee8d73029c01e7754f7d7d9db98..f524f8fadc99916ecfe01c37b5647fe3c5507529 100644
--- a/satella/coding/decorators/__init__.py
+++ b/satella/coding/decorators/__init__.py
@@ -1,6 +1,7 @@
 from .arguments import auto_adapt_to_methods, attach_arguments, for_argument, \
     execute_before, copy_arguments, replace_argument_if, transform_result, \
-    transform_arguments, execute_if_attribute_none, execute_if_attribute_not_none
+    transform_arguments, execute_if_attribute_none, execute_if_attribute_not_none, \
+    cached_property
 from .decorators import wraps, chain_functions, has_keys, short_none, memoize, return_as_list, \
     default_return, cache_memoize, call_method_on_exception
 from .flow_control import loop_while, queue_get
@@ -13,4 +14,5 @@ __all__ = ['retry', 'transform_result', 'transform_arguments',
            'attach_arguments', 'for_argument', 'loop_while', 'memoize',
            'copy_arguments', 'replace_argument_if', 'return_as_list',
            'default_return', 'cache_memoize', 'call_method_on_exception',
-           'execute_if_attribute_none', 'execute_if_attribute_not_none']
+           'execute_if_attribute_none', 'execute_if_attribute_not_none',
+           'cached_property']
diff --git a/satella/coding/decorators/arguments.py b/satella/coding/decorators/arguments.py
index e7fa1308811e391abd596cff94b292560ddd98de..8e33ad4b7623944c5ef111861655973727a340a5 100644
--- a/satella/coding/decorators/arguments.py
+++ b/satella/coding/decorators/arguments.py
@@ -410,3 +410,42 @@ def execute_if_attribute_not_none(attribute: str):
         return inner
     return outer
 
+
+def cached_property(prop_name: str, assume_not_loaded = None):
+    """
+    A decorator to use to create cached properties.
+
+    You job is to only write the value returner. If the value is
+    currently assume_not_loaded (None by default) your property
+    method will be called. Otherwise it will be served from
+    cached attribute, whose value you provide as parameter.
+
+    Use as follows:
+
+    >>> class Example:
+    >>>     def __init__(self):
+    >>>         self._a = None
+    >>>     @property
+    >>>     @cached_property('_a')
+    >>>     def a(self) -> str:
+    >>>         return 'abc'
+    >>> a = Example()
+    >>> assert a.a == 'abc'
+    >>> assert a._a == 'abc'
+
+    :param prop_name: Name of property to store the value in
+    :param assume_not_loaded: Value if currently the attribute is
+        equal to this, it is assumed to not have been loaded
+    """
+    def outer(fun):
+        @wraps(fun)
+        def inner(self, *args, **kwargs):
+            attr_v = getattr(self, prop_name)
+            if attr_v == assume_not_loaded:
+                attr_v = fun(self, *args, **kwargs)
+                setattr(self, prop_name, attr_v)
+                return attr_v
+            else:
+                return attr_v
+        return inner
+    return outer
diff --git a/tests/test_coding/test_decorators.py b/tests/test_coding/test_decorators.py
index 425f384fcb756ca0dc63325f391a137a297b2623..cb69d32b2961425980f2388564036d61a5d0d729 100644
--- a/tests/test_coding/test_decorators.py
+++ b/tests/test_coding/test_decorators.py
@@ -10,7 +10,7 @@ from satella.coding.decorators import auto_adapt_to_methods, attach_arguments, \
     execute_before, loop_while, memoize, copy_arguments, replace_argument_if, \
     retry, return_as_list, default_return, transform_result, transform_arguments, \
     cache_memoize, call_method_on_exception, execute_if_attribute_none, \
-    execute_if_attribute_not_none
+    execute_if_attribute_not_none, cached_property
 from satella.coding.predicates import x
 from satella.exceptions import PreconditionError
 
@@ -19,6 +19,23 @@ logger = logging.getLogger(__name__)
 
 class TestDecorators(unittest.TestCase):
 
+    def test_cached_property(self):
+        class Example:
+            def __init__(self):
+                self._a = None
+                self.called = 0
+
+            @property
+            @cached_property('_a')
+            def a(self):
+                self.called += 1
+                return 'abc'
+        a = Example()
+        self.assertEqual(a.a, 'abc')
+        self.assertEqual(a.called, 1)
+        self.assertEqual(a.a, 'abc')
+        self.assertEqual(a.called, 1)
+
     def test_execute_if_attribute_not_none(self):
         class ExecIfAttrNone:
             def __init__(self):