Yes there is! It was called properties.
Read Only Properties
class Test(object): def __init__(self,a,b): self.a = a self.b = b @property def c(self): return self.a + self.b
With the above code, c
now a read-only class of Test
.
Mutable properties
You can also provide a setter property that will make it read / write and allow you to set its value directly. It will look like this:
class Test(object): def __init__(self, c = SomeDefaultValue): self._c = SomeDefaultValue @property def c(self): return self._c @c.setter def c(self,value): self._c = value
However, in this case it would be pointless to have a setter for self.c
, since its value depends on self.a
and self.b
What does @property mean?
The @property
bit is an example of what is called a decorator. The decorator actually wraps a function (or class) that it decorates into another function (decorator function). After the function has been decorated when it is called, it is actually a decorator that is called with the function (and its arguments) as an argument. Usually (but not always!) A decorated function does something interesting, and then calls the original (decorated) function, as usual. For instance:
def my_decorator(thedecoratedfunction): def wrapped(*allofthearguments): print("This function has been decorated!")
This is equivalent to:
def myfunction(arg1, arg2): pass myfunction = my_decorator(myfunction)
So this means that in the example above, instead of using a decorator, you can also do this:
def c(self): return self.a + self.b c = property(c)
This is exactly the same. @property
is just syntactic sugar to replace myobject.c
calls with getter and setter (also removed options).
Wait - how does it work?
You might be wondering why just do it once:
myfunction = my_decorator(myfunction)
... leads to such a drastic change! So now when calling:
myfunction(arg1, arg2)
... you actually call my_decorator(myfunction)
, and arg1, arg2
sent to the wrapped
inner function inside my_decorator
. And not only that, but it all happens, even if you didn't even mention my_decorator
or wrapped
in your function call at all!
All this works by virtue of something called a closure. When a function is passed to the decorator in this way (for example, property(c)
), the name of the function is redefined into the wrapped version of the function instead of the original function, and the original arguments of the function are always passed to wrapped
instead of the original function. This is just how closures work, and there is nothing magical about it. Here is more information on closing .
descriptors
So, to summarize so far: @property
is just a way to wrap a class method inside the property()
function, so the wrapped class method is called instead of the original method of the expanded class. But what is a property function? What is he doing?
The property function adds something called a handle to the class. Simply put, a descriptor is a class of objects that can have separate get, set, and delete methods. When you do this:
@property def c(self): return self._c
... you add a descriptor to the Test
class called c
and define the get method (actually, __get__()
) of the descriptor c
equal to the c(self)
method.
When you do this:
@c.setter def c(self,value): self._c
... you define the set method (actually, __set__()
) of the descriptor c
as equal to the method c(self,value)
.
Summary
An amazing amount of things is achieved by simply adding @property
to your def c(self)
method! In practice, you probably do not need to immediately understand all this in order to start using it. However, I recommend keeping in mind that when using @property
you use decorators, locks, and descriptors, and if you are serious about learning Python, you should take the time to learn each of these topics on their own.