Overriding properties in python
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
Override an attribute with a property in python class
instead of calling your method _initProperty
call it __getattr__
so that it will be called every time the attribute is not found in the normal places it should be stored (the attribute dictionary, class dictionary etc.) then the first time the attribute is tried to be accessed it gets initialized.
Be sure to not set them in the Parent initialization, maybe only set them if they are not None:
class Parent:
def __init__(self,a1=None,a2=None):
if a1 is not None:
self.a1 = a1
if a2 is not None:
self.a2 = a2
As well to stay consistent with errors you will want to raise an AttributeError
if the attribute doesn't exist instead of letting the KeyError
go through, and maybe add a reference to the value in the regular attribute dict so that it doesn't need to run the __getattr__
every time:
_propertiesCache = {'a1':None,'a2':None}
def __getattr__(self, propertyName):
if propertyName not in self._propertiesCache:
raise AttributeError(propertyName)
value = self._propertiesCache[propertyName]
if value is None:
value = self._propertiesCache[propertyName]=expensiveFunction(self.b, propertyName)
setattr(self,propertyName,value)
return value
Any way you implement this you need to make sure:
The attribute is not set until the first time they are used (at which point __getattr__
gets used)
Override a property with super()
Try return super().velocity
@property
def velocity(self, flag=True):
if flag:
return super().velocity
Subclassing: Is it possible to override a property with a conventional attribute?
A property is a data descriptor which takes precedence over an instance attribute with the same name. You could define a non-data descriptor with a unique __get__()
method: an instance attribute takes precedence over the non-data descriptor with the same name, see the docs. The problem here is that the non_data_property
defined below is for computation purpose only (you can't define a setter or a deleter) but it seems to be the case in your example.
import math
class non_data_property:
def __init__(self, fget):
self.__doc__ = fget.__doc__
self.fget = fget
def __get__(self, obj, cls):
if obj is None:
return self
return self.fget(obj)
class Math_Set_Base:
@non_data_property
def size(self, *elements):
return len(self.elements)
class Concrete_Math_Set(Math_Set_Base):
def __init__(self, *elements):
self.elements = elements
class Square_Integers_Below(Math_Set_Base):
def __init__(self, cap):
self.size = int(math.sqrt(cap))
print(Concrete_Math_Set(1, 2, 3).size) # 3
print(Square_Integers_Below(1).size) # 1
print(Square_Integers_Below(4).size) # 2
print(Square_Integers_Below(9).size) # 3
However this assumes that you have access to the base class in order to make this changes.
Overriding an inherited property setter
You can refer to the existing property directly through the Node
class, and use the property's setter
method to create a new property from it:
class Theorem(Node):
@Node.importance.setter
def importance(self, new_importance):
# You can change the order of these two lines:
assert new_importance >= 3
Node.importance.fset(self, new_importance)
This will create a new property into Theorem
class that uses the getter method from Node.importance
but replaces the setter method with a different one.
That's how properties in general work: calling a property's setter
returns a new property with a custom setter, which usually just replaces the old property.
You can learn more about how properties work by reading this answer (and the question too).
overriding a property that has no getter or setter in python
As mentionned in the comments, the code for your setter is incorrect.
Here is a working solution :
class Parent(object):
def __init__(self, val):
self.val = val
@property
def value(self):
return self.val
class Child(Parent):
@Parent.value.setter
def value(self, val):
self.val = val + " overridden"
parent = Parent("initial")
print(parent.value)
child = Child("initial")
print(child.value)
child.value = "new value"
print(child.value)
which produces
initial
initial
new value overridden
Python overriding of property does not work
You have to overload the property in B
, too:
class A():
def get_val(self):
print(self)
raise NotImplementedError
val = property(get_val)
class B(A):
def __init__(self):
self.b_val = "foo\n"
def get_val(self):
print(self)
return self.b_val
val = property(get_val)
or since get_val
is not really needed, use decorator syntax:
#!/usr/bin/env python
class A():
@property
def val(self):
print(self)
raise NotImplementedError
class B(A):
def __init__(self):
self.b_val = "foo\n"
@property
def val(self):
print(self)
return self.b_val
b = B()
print(b.val)
How to override the an inherent property when using its related method?
Your implementation is non-logical by default.
The function nums
in class bar
overrides the function nums
in class foo
- therefore when you call the self.nums
in list-comprehension of gt
function - the actual nums
invoked is the overrided one in bar
.
not sure what are your constraints, but just not override will do the work:
class foo:
@property
def nums(self):
return [1, 2, 3, 4, 5]
def gt(self, x):
return [num for num in self.nums if num > x]
class bar(foo):
@property
def nums2(self):
return super().gt(3)
f = foo()
b = bar()
print(f.nums)
print(b.nums2)
other option is to make the nums
a member and return it from nums
, like here:
class foo:
nums_member = [1, 2, 3, 4, 5]
@property
def nums(self):
return self.nums_member
def gt(self, x):
return [num for num in self.nums_member if num > x]
class bar(foo):
@property
def nums(self):
return super().gt(3)
f = foo()
b = bar()
print(f.nums)
print(b.nums)
Class method override with any type of property
You've got the variance backwards.
class App:
@abstractmethod
def main(self, params: object):
pass
This is a promise. It says "any class which implements App
has a main
which works on all objects. Not some of them. All of them.
Then you come along and write
class MyClass(App):
def main(self, params: Tuple[str, int, int]):
# Do something
So MyClass
has a main
which works on a specific type of parameter, not all of them. Your type checker correctly reports that this is not what you promised earlier. You could do the opposite thing: If App
requires Tuple[str, int, int]
, then MyClass
could implement a version for object
, since that includes Tuple[str, int, int]
and then some.
But for your case, you probably want generics.
from typing import TypeVar, Generic
_T_contra = TypeVar("_T_contra", contravariant=True)
class App(Generic[_T_contra]):
@abstractmethod
def main(self, params: _T_contra) -> None:
pass
class MyClass(App[Tuple[str, int, int]]):
def main(self, params: Tuple[str, int, int]):
# Do something
Now MyClass
doesn't claim to work for all objects. It claims to work for this specific type, so it derives from a specific App
.
For a discussion of why it's _T_contra
(a contravariant type parameter), you can read my other answer where I go into more detail on variance annotations in Python.
Related Topics
Run Powershell Function from Python Script
Sorting a Dictionary with Lists as Values, According to an Element from the List
Is There a Difference Between Using a Dict Literal and a Dict Constructor
Why Does CSVwriter.Writerow() Put a Comma After Each Character
Importerror: No Module Named Tensorflow
Checking Odd/Even Numbers and Changing Outputs on Number Size
Loop Over a List Containing Path to Sound Files
How to Get the Domain Name of My Site Within a Django Template
Search by Objectid in Mongodb with Pymongo
Populate a Pandas Sparsedataframe from a Scipy Sparse Matrix
Python - When to Use File VS Open
Python Parse Comma-Separated Number into Int
What Is the Meaning of "Failed Building Wheel for X" in Pip Install
How to Return a Subset of a List That Matches a Condition
Gunicorn Autoreload on Source Change