Overload a property setter in Python [duplicate] - python

So, I'm trying to figure out the best (most elegant with the least amount of code) way to allow overriding specific functions of a property (e.g., just the getter, just the setter, etc.) in python. I'm a fan of the following way of doing properties, due to the fact that all of their methods are encapsulated in the same indented block of code (it's easier to see where the functions dealing with one property stop and the functions dealing with the next begin):
#apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
However, if I want to inherit from a class that defines properties in this manner, and then, say, override the foo setter function, it seems tricky. I've done some searching and most of the answers I've found have been to define separate functions in the base class (e.g. getFoo and setFoo), explicitly create a property definition from them (e.g. foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())), and then override getFoo, setFoo, and delFoo as needed.
I dislike this solution because it means I have to define lambas for every single property, and then write out each function call (when before I could have just done property(**locals())). I also don't get the encapsulation that I had originally.
Ideally, what I would like to be able to do would be something like this:
class A(object):
def __init__(self):
self.foo = 8
#apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
class ATimesTwo(A):
#some_decorator
def foo():
def fset(self, val):
self._foo = val * 2
return something
And then the output would look something like:
>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16
Basically, ATimesTwo inherits the getter function from A but overrides the setter function. Does anybody know of a way to do this (in a manner that looks similar to the example above)? What function would the some_decorator look like, and what should the foo function return?

The Python docs on the property decorator suggest the following idiom:
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
And then subclasses can override a single setter/getter like this:
class C2(C):
#C.x.getter
def x(self):
return self._x * -1
This is a little warty because overriding multiple methods seems to require you to do something like:
class C3(C):
#C.x.getter
def x(self):
return self._x * -1
# C3 now has an x property with a modified getter
# so modify its setter rather than C.x's setter.
#x.setter
def x(self, value):
self._x = value * 2
Of course at the point that you're overriding getter, setter, and deleter you can probably just redefine the property for C3.

I'm sure you've heard this before, but apply has been deprecated for eight years, since Python 2.3. Don't use it. Your use of locals() is also contrary to the Zen of Python -- explicit is better than implicit. If you really like the increased indentation, there is no need to create a throwaway object, just do
if True:
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, val):
self._foo = val
Which doesn't abuse locals, use apply, require creation of an extra object, or need a line afterwards with foo = foo() making it harder to see the end of the block. It works just as well for your old-fashioned way of using property -- just do foo = property(fget, fset) as normal.
If you want to override a property in an arbitrary subclass, you can use a recipe like this.
If the subclass knows where the property was defined, just do:
class ATimesTwo(A):
#A.foo.setter
def foo(self, val):
self._foo = val * 2

The answer of stderr satisfies most use cases.
I'd like to add a solution for the case where you want to extend a getter, setter and/or deleter. Two ways to do this are:
1. Subclass property
First way to do this is by subclassing the builtin property and adding decorators that are versions of getter, setter and/or deleter that extend the current get, set and delete callbacks
Example for a property that supports appending methods to the set-functions:
class ExtendableProperty(property):
def append_setter(self, fset):
# Create a wrapper around the new fset that also calls the current fset
_old_fset = self.fset
def _appended_setter(obj, value):
_old_fset(obj, value)
fset(obj, value)
# Use that wrapper as setter instead of only the new fset
return self.setter(_appended_setter)
Usage is the same as for normal properties, only now it is possible to add methods to the property setters:
class A(object):
#ExtendableProperty
def prop(self):
return self._prop
#prop.setter
def prop(self, v):
self._prop = v
class B(A):
#A.prop.append_setter
def prop(self, v):
print('Set', v)
>>> a = A()
>>> a.prop = 1
>>> a.prop
1
>>> b = B()
>>> b.prop = 1
Set 1
>>> b.prop
1
2. Overwrite getter, setter and/or deleter
Use a normal property, overwrite the getter, setter or deleter and then add calls to the fget, fset or fdel in the property of the parent class.
Example for the type of property as in example 1:
class A(object):
#property
def prop(self):
return self._prop
#prop.setter
def prop(self, v):
self._prop = v
class B(A):
#A.prop.setter
def prop(self, v):
A.prop.fset(self, v) # This is the call to the original set method
print('Set {}'.format(v))
I think the first option looks nicer because the call to the super property's fset is not necessary

Related

Why do we need propertie's getter if property already does it's job in python?

Imagine two similar classes. First has property and getter and second one has only property. Property is doing the getter job already if we do not write getter explicitly.
So why do we need to define getter?
class Test:
def __init__(self):
self._prop = 0
#property
def prop(self):
print("property accessed")
return self._prop
#prop.getter
def prop(self):
print("getter accessed")
return self._prop
class Test2:
def __init__(self):
self._prop = 0
#property
def prop(self):
print("property accessed")
return self._prop
if __name__ == "__main__":
t = Test()
print(t.prop)
et = Test2()
print(et.prop)
# output
# getter accessed
# 0
# property accessed
# 0
The property class provides two ways to configure the getter, setter, and deleter methods.
Passing functions as arguments when you create the property.
Using the property's getter, setter, and deleter methods, each of which returns a new property with the corresponding fget, fset, or fdel overriden.
For example, given three functions
def get_value(self):
...
def set_value(self, value):
...
def delete_value(self):
...
you write either
p1 = property(get_value, set_value, delete_value)
or
# All arguments are optional
p2 = property()
p2 = p2.getter(get_value)
p2 = p2.setter(set_value)
p2 = p2.deleter(set_value)
property, property.getter, property.setter, and property.deleter are all designed to allow them to be used as decorators.
p2 = property()
#p2.getter
def p2(self): # get_value
...
#p2.setter
def p2(self, value): # set_value
...
#p2.deleter
def p2(self): # del_value
...
In ordinary usage, a hybrid approach is taken: the property is created and initialized with a getter in one step (the first argument is the getter to support this most common use-case of a read-only property), an optional setter and deleter provided when necessary.
#property
def p2(self): # get_value
...
So, the explicit use of property.getter is rare, but available. While all three methods could be called at any time to alter the behavior of an existing property, I don't think I've even seen them used other than for the intial configuration of a property.

How does python builtin #property receives the overloaded setter function ?

When defining a builtin python property using the #property, how does the property object differentiates the setter from the getter method, provided that they are overloaded (have the same name)?
class A:
def __init__(self):
self._x = 12
#property
def x(self) -> int:
return self._x
#notifiable # strangely this stacks on both setter and getter
#x.setter
def x(self, val: int):
self._x = val
If I define a custom property decorator, say:
class my_property:
def __init__(self, getter):
print("__init__ getter %s:" % getter)
def setter(self, setter: FunctionType):
print("setter: %s" % setter)
class B:
def __init__(self):
self._val = 44
#my_property
def x(self):
return self._val
#x.setter
def x(self, val):
self._val = val
Executing the code results in the following output
__init__ getter <function B.x at 0x7ff80c5e1620>:
setter: <function B.x at 0x7ff80c5e1620>
Both the getter and the setter funtions passed to the decorator are the same funtion, but they should be different functions.
If I use the annotation like this:
class C:
def __init__(self):
self._val = 44
#my_property
def x(self):
return self._val
#x.setter
def set_x(self, val):
self._val = val
A different function is printed, as expected.
__init__ getter <function C.x at 0x7f529132c6a8>:
setter: <function C.set_x at 0x7f529132c6a8>
How does python solves this issue with the builtin #property? Is the decorator treated differently from user decorators ?
The reason you're seeing what you're seeing here is because you don't keep a reference to the getter anywhere. This means that once the __init__ method ends, there's no more reference to the first B.x, (i.e. the refcount is zero), so the function is released. Since the original getter function has been released, Python is free to reuse the exact same memory address for another object/function, which is what happens in this case.
If you modify my_property to keep a reference to the original getter method as such:
class my_property:
def __init__(self, getter):
print("__init__ getter %s:" % getter)
self.getter = getter
def setter(self, setter: FunctionType):
print("setter: %s" % setter)
you'll see that the function name (B.x) is still the same (which is ok, as python doesn't use the function name to uniquely identify a function), however the memory address of the two functions are different:
__init__ getter <function B.x at 0x7f9870d60048>
setter: <function B.x at 0x7f9870d600d0>
Is the decorator treated differently from user decorators ?
No, property just a regular decorator. However, if you want to reimplement the property decorator, you'd probably be interested in the descriptor protocol (there's a pure python reimplementation of #property in that page).

Why is Class property.setter not a case of method overloading?

Method overloading is not possible in Python!
Can you please explain why Properties.setter in Python is not a case of method overloading?
class newOne():
def __init__(self):
self.__x = 0
#property
def val(self):
return self.__x
#val.setter
def val(self,value):
self.__x = value
In the above code, I have two methods with the same name 'val' (but different set of args) and both behave differently.
It's not method overloading because there aren't two methods with different signatures that can be called. If it was method overloading, you could do something like this:
obj = newOne()
print(obj.val())
obj.val(5)
But that doesn't work, because val is a property and not an overloaded method.
So what's going on there? Why are we defining two methods with the same name, and what happens to them, if not overloading?
The magic happens in the decorators. As a prerequisite, you have to know that
#deco
def func(...):
...
is equivalent to
def func(...):
...
func = deco(func)
So, the first thing that happens is that the #property decorator turns your getter function into a property with that function as its getter function:
class newOne:
#property
def val(self):
return self.__x
print(newOne.val)
print(newOne.val.fget)
# output:
# <property object at 0x002B1F00>
# <function newOne.val at 0x0052EAE0>
After this, the #val.setter decorator creates a new property with a getter and setter function:
class newOne:
#property
def val(self):
return self.__x
#val.setter
def val(self, value):
self.__x = value
print(newOne.val)
print(newOne.val.fget)
print(newOne.val.fset)
# output:
# <property object at 0x0221B7B0>
# <function newOne.val at 0x0226EB70>
# <function newOne.val at 0x0226EAE0>
(The getter and setter functions have the same name because they were both defined as def val(...), but they're still different functions. That's why they have different ids.)
So in the end you have a val property with a getter and a setter function. You do not have an overloaded method.
For details about how properties work and how the getter and setter functions are called, see the descriptor documentation.
First, the #property decorator creates a descriptor named val and sets it aside to add to the class once it is defined. Then, the #val.setter decorated takes its function and simplyeffectively adds a reference to it to the val descriptor.
Your code is roughly equivalent to
d = {}
def __init__(self):
self.__x = 0
d['__init__'] = __init__
def val(self):
return self.__x
d['val'] = property(val)
def val(self, value):
self.__x = value
# Not d['val'].__set__ = val, as previously stated
d['val'] = property(fget=d['val'], fset=val)
newOne = type('newOne', (object,), d)
# These are all "local" to, or part of the implementation of,
# the class statement, so they don't stick around in the current
# namespace.
del __init__, val, d # These are all "local" to or part of the imple

Overriding properties in python

So, I'm trying to figure out the best (most elegant with the least amount of code) way to allow overriding specific functions of a property (e.g., just the getter, just the setter, etc.) in python. I'm a fan of the following way of doing properties, due to the fact that all of their methods are encapsulated in the same indented block of code (it's easier to see where the functions dealing with one property stop and the functions dealing with the next begin):
#apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
However, if I want to inherit from a class that defines properties in this manner, and then, say, override the foo setter function, it seems tricky. I've done some searching and most of the answers I've found have been to define separate functions in the base class (e.g. getFoo and setFoo), explicitly create a property definition from them (e.g. foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())), and then override getFoo, setFoo, and delFoo as needed.
I dislike this solution because it means I have to define lambas for every single property, and then write out each function call (when before I could have just done property(**locals())). I also don't get the encapsulation that I had originally.
Ideally, what I would like to be able to do would be something like this:
class A(object):
def __init__(self):
self.foo = 8
#apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
class ATimesTwo(A):
#some_decorator
def foo():
def fset(self, val):
self._foo = val * 2
return something
And then the output would look something like:
>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16
Basically, ATimesTwo inherits the getter function from A but overrides the setter function. Does anybody know of a way to do this (in a manner that looks similar to the example above)? What function would the some_decorator look like, and what should the foo function return?
The Python docs on the property decorator suggest the following idiom:
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
And then subclasses can override a single setter/getter like this:
class C2(C):
#C.x.getter
def x(self):
return self._x * -1
This is a little warty because overriding multiple methods seems to require you to do something like:
class C3(C):
#C.x.getter
def x(self):
return self._x * -1
# C3 now has an x property with a modified getter
# so modify its setter rather than C.x's setter.
#x.setter
def x(self, value):
self._x = value * 2
Of course at the point that you're overriding getter, setter, and deleter you can probably just redefine the property for C3.
I'm sure you've heard this before, but apply has been deprecated for eight years, since Python 2.3. Don't use it. Your use of locals() is also contrary to the Zen of Python -- explicit is better than implicit. If you really like the increased indentation, there is no need to create a throwaway object, just do
if True:
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, val):
self._foo = val
Which doesn't abuse locals, use apply, require creation of an extra object, or need a line afterwards with foo = foo() making it harder to see the end of the block. It works just as well for your old-fashioned way of using property -- just do foo = property(fget, fset) as normal.
If you want to override a property in an arbitrary subclass, you can use a recipe like this.
If the subclass knows where the property was defined, just do:
class ATimesTwo(A):
#A.foo.setter
def foo(self, val):
self._foo = val * 2
The answer of stderr satisfies most use cases.
I'd like to add a solution for the case where you want to extend a getter, setter and/or deleter. Two ways to do this are:
1. Subclass property
First way to do this is by subclassing the builtin property and adding decorators that are versions of getter, setter and/or deleter that extend the current get, set and delete callbacks
Example for a property that supports appending methods to the set-functions:
class ExtendableProperty(property):
def append_setter(self, fset):
# Create a wrapper around the new fset that also calls the current fset
_old_fset = self.fset
def _appended_setter(obj, value):
_old_fset(obj, value)
fset(obj, value)
# Use that wrapper as setter instead of only the new fset
return self.setter(_appended_setter)
Usage is the same as for normal properties, only now it is possible to add methods to the property setters:
class A(object):
#ExtendableProperty
def prop(self):
return self._prop
#prop.setter
def prop(self, v):
self._prop = v
class B(A):
#A.prop.append_setter
def prop(self, v):
print('Set', v)
>>> a = A()
>>> a.prop = 1
>>> a.prop
1
>>> b = B()
>>> b.prop = 1
Set 1
>>> b.prop
1
2. Overwrite getter, setter and/or deleter
Use a normal property, overwrite the getter, setter or deleter and then add calls to the fget, fset or fdel in the property of the parent class.
Example for the type of property as in example 1:
class A(object):
#property
def prop(self):
return self._prop
#prop.setter
def prop(self, v):
self._prop = v
class B(A):
#A.prop.setter
def prop(self, v):
A.prop.fset(self, v) # This is the call to the original set method
print('Set {}'.format(v))
I think the first option looks nicer because the call to the super property's fset is not necessary

How to call a property of the base class if this property is being overwritten in the derived class?

I'm changing some classes of mine from an extensive use of getters and setters to a more pythonic use of properties.
But now I'm stuck because some of my previous getters or setters would call the corresponding method of the base class, and then perform something else. But how can this be accomplished with properties? How to call the property getter or setter in the parent class?
Of course just calling the attribute itself gives infinite recursion.
class Foo(object):
#property
def bar(self):
return 5
#bar.setter
def bar(self, a):
print a
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return self.bar # --> recursion!
#bar.setter
def bar(self, c):
# perform the same action
# as in the base class
self.bar = c # --> recursion!
# then do something else
print 'something else'
fb = FooBar()
fb.bar = 7
You might think you could call the base class function which is called by property:
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return Foo.bar(self)
Though this is the most obvious thing to try I think - it does not work because bar is a property, not a callable.
But a property is just an object, with a getter method to find the corresponding attribute:
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
super() should do the trick:
return super().bar
In Python 2.x you need to use the more verbose syntax:
return super(FooBar, self).bar
There is an alternative using super that does not require to explicitly reference the base class name.
Base class A:
class A(object):
def __init__(self):
self._prop = None
#property
def prop(self):
return self._prop
#prop.setter
def prop(self, value):
self._prop = value
class B(A):
# we want to extend prop here
pass
In B, accessing the property getter of the parent class A:
As others have already answered, it's:
super(B, self).prop
Or in Python 3:
super().prop
This returns the value returned by the getter of the property, not the getter itself but it's sufficient to extend the getter.
In B, accessing the property setter of the parent class A:
The best recommendation I've seen so far is the following:
A.prop.fset(self, value)
I believe this one is better:
super(B, self.__class__).prop.fset(self, value)
In this example both options are equivalent but using super has the advantage of being independent from the base classes of B. If B were to inherit from a C class also extending the property, you would not have to update B's code.
Full code of B extending A's property:
class B(A):
#property
def prop(self):
value = super(B, self).prop
# do something with / modify value here
return value
#prop.setter
def prop(self, value):
# do something with / modify value here
super(B, self.__class__).prop.fset(self, value)
One caveat:
Unless your property doesn't have a setter, you have to define both the setter and the getter in B even if you only change the behaviour of one of them.
try
#property
def bar:
return super(FooBar, self).bar
Although I'm not sure if python supports calling the base class property. A property is actually a callable object which is set up with the function specified and then replaces that name in the class. This could easily mean that there is no super function available.
You could always switch your syntax to use the property() function though:
class Foo(object):
def _getbar(self):
return 5
def _setbar(self, a):
print a
bar = property(_getbar, _setbar)
class FooBar(Foo):
def _getbar(self):
# return the same value
# as in the base class
return super(FooBar, self)._getbar()
def bar(self, c):
super(FooBar, self)._setbar(c)
print "Something else"
bar = property(_getbar, _setbar)
fb = FooBar()
fb.bar = 7
Some small improvements to Maxime's answer:
Using __class__ to avoid writing B. Note that self.__class__ is the runtime type of self, but __class__ without self is the name of the enclosing class definition. super() is a shorthand for super(__class__, self).
Using __set__ instead of fset. The latter is specific to propertys, but the former applies to all property-like objects (descriptors).
class B(A):
#property
def prop(self):
value = super().prop
# do something with / modify value here
return value
#prop.setter
def prop(self, value):
# do something with / modify value here
super(__class__, self.__class__).prop.__set__(self, value)
You can use the following template:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
#property
def prop1(self):
return self.__prop1
#setter
#prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
#prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
#property
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
#prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
#prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
Note! All of the property methods must be redefined together. If do not want to redefine all methods, use the following template instead:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
#property
def prop1(self):
return self.__prop1
#setter
#prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
#prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
#Parent.prop1.getter
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
#Parent.prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
#Parent.prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
class Base(object):
def method(self):
print "Base method was called"
class Derived(Base):
def method(self):
super(Derived,self).method()
print "Derived method was called"
d = Derived()
d.method()
(that is unless I am missing something from your explanation)

Categories

Resources