An elegant way of overloading Python class properties - python

Consider a base class:
class A(object):
def __init__(self, x):
self._x = x
def get_x(self):
#...
return self._x
def set_x(self, x):
#...
self._x = x
x = property(get_x, set_x)
and a derived class:
class B(A):
def set_x(self, x):
#...
self._x = x**2
x = property(A.get_x, set_x)
Is there an elegant way of overloading set_x() in class B, without re-declaring it and the property x? Thank you.

Add an extra layer of indirection (i.e. use a hook):
class A(object):
def __init__(self, x):
self._x = x
# Using a _get_hook is not strictly necessary for your problem...
def _get_hook(self):
return self._x
def get_x(self):
return self._get_hook()
# By delegating `set_x` behavior to `_set_hook`, you make it possible
# to override `_set_hook` behavior in subclass B.
def _set_hook(self, x):
self._x=x
def set_x(self, x):
self._set_hook(x)
x = property(get_x, set_x)
class B(A):
def _set_hook(self, x):
print('got here!')
self._x = x**2
b=B(5)
b.x=10
# got here!
print(b.x)
# 100
For modern versions of Python, you can also use the #property decorator:
class A(object):
#property
def x(self):
return self._get_hook()
#x.setter
def x(self, x):
self._set_hook(x)

Try this one:
class A(object):
def __init__(self):
self._x = 0
def get_x(self):
#...
return self._x
def set_x(self, x):
#...
self._x = x
x = property(get_x, lambda self,x : self.set_x(x))
class B(A):
def set_x(self, x):
#...
self._x = x**2
The extra indirection given by the lambda will make the set_x function virtually.

Related

Simple python inheritance with singleton class failing with no stack trace

class B:
#property
def x(self):
print('x getter')
return self.x
#x.setter
def x(self, x) -> None:
print('x setter')
self.x = x + 1
class A(B):
__instance = None
def __new__(self):
''' Virtually private constructor '''
if not A.__instance:
A.__instance = object.__new__(self)
A.__instance.__setup()
return A.__instance
def __setup(self):
self.x = 10
def minus(self):
self.x -= 3
a1 = A()
Class A is a singleton class.
I'm not sure what is causing the program to fail as there is no stack trace and it just fails.
You are not using #property and setter correctly. You need a different name for the actual underlying property:
class B:
#property
def x(self):
print('x getter')
return self._x
#x.setter
def x(self, x) -> None:
print('x setter')
self._x = x + 1

Overriding a descriptor with a regular attribute

Is it possible to override a property in a base class by a regular attribute in a derived class, something like this:
class A(object):
#property
def x(self):
return self._x
#x.setter
def x(self, y):
self._x = y
class B(A):
def __init__(self, y):
self.x = y #the descriptor methods are not called and
#"x" is a regular attribute in the object dict.
The reason I am asking is because I have a complex base class in which one of the descriptor attributes typically performs a complicated calculation. However, in one of the derived classes, the returned value is trivial and it seems like a waste to have to override with another descriptor and not just a regular storage attribute.
You can simply redeclare x in B:
class A(object):
#property
def x(self):
print("calculating x...")
return self._x
#x.setter
def x(self, y):
print('setting x...')
self._x = 10*y
class B(A):
x = None
def __init__(self, y):
self.x = y #the descriptor methods are not called and
#"x" is a regular attribute in the object dict.
b = B(3)
print(b.x)
# 3

Can i divide my class(parent)addition answer in class(child)?

class a:
def __init__(self,x):
self.x=x
def __str__(self):
return 'addition is(%d)'%(self.x)
def __add__(self,other):
c=self.x+other.x
return a(self.x+other.x)
a1=a(2)`enter code here`
a2=a(5)
c=a1+a2
print(c)
class b(a):
can i divide my parent class addition answer(c)=7 in class b and how ?
I am still not really sure what you are looking for, but it sounds like you want a child class of a named b, which can do division. Here is what that would look like:
class a:
def __init__(self,x):
self.x=x
def __str__(self):
return string(self.x)
def __add__(self,other):
c=self.x+other.x
return a(self.x+other.x)
class b(a):
def __init__(self,x):
a.__init__(self, x)
def __div__(self, other):
return self.x/other.x
a1 = a(2)
a2 = a(5)
a3 = a1+a2
print(a3)
#7
a4 = b(21)
print(a4/a3)
#3
You need a call to initiate the super class to use it in a subclass, which is what
def __init__(self,x):
a.__init__(self, x)
does. Once you have that, you can access all the superclass functions and fields, along with functions and fields defined for the subclass.

Why does PyCharm raise a warning when using #property here?

In tutorials I have seen two types of instance attribute naming for the purpose of using #property. Here is code showing examples of both. They also seem to work differently.
class A:
def __init__(self, x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x > 1000:
self.__x = 1000
else:
self.__x = x # Instance attribute __x defined outside __init__
class B:
def __init__(self, x):
self._x = x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
if x > 1000:
self._x = 1000
else:
self._x = x
a = A(9999)
print(a.x) # -> 1000
b = B(9999) # -> 9999
print(b.x)
b.x = 9999
print(b.x) # -> 1000
I like the behaviour of class A better as it seems that the #x.setter is used immediately in __init__, however that piece of code gives me a warning in PyCharm (I have it as a comment). Why would there be a warning if that is the proper use of a Python's property setter? There are no warnings in class B. Could I somehow call #x.setter in __init__ the same way as in class A without a warning?
It seems to be a bug in PyCharm: https://youtrack.jetbrains.com/issue/PY-25263.
A temporary solution I found was to add self._x = None in the __init__. So the code would be:
class A:
def __init__(self, x):
self._x = None
self.x = x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
if x > 1000:
self._x = 1000
else:
self._x = x
a = A(9999)
print(a.x) # -> 1000

Inheritance and base class method call python

I would like a method in a base class to call another method in the same class instead of the overriding method in an inherited class.
I would like the following code to print out
Class B: 6
Class A: 9
Can this be done?
# Base class definition
class ClassA(object):
def __init__(self):
print("Initializing A")
# hoping that this function is called by this class's printFnX
def fnX(self, x):
return x**2
def printFnX(self, x):
print("ClassA:",self.fnX(x))
# Inherits from ClassA above
class ClassB(ClassA):
def __init__(self):
print("initizlizing B")
def fnX(self, x):
return 2*x
def printFnX(self, x):
print("ClassB:", self.fnX(x))
ClassA.printFnX(self,x)
bx = ClassB()
bx.printFnX(3)
Congratulations, you've discovered the motivating use case for Python's double-underscore name mangling :-)
For the details and a worked-out example see: http://docs.python.org/tutorial/classes.html#private-variables and at http://docs.python.org/reference/expressions.html#atom-identifiers .
Here's how to use it for your example:
# Base class definition
class ClassA(object):
def __init__(self):
print("Initializing A")
# hoping that this function is called by this class's printFnX
def fnX(self, x):
return x**2
__fnX = fnX
def printFnX(self, x):
print("ClassA:",self.__fnX(x))
# Inherits from ClassA above
class ClassB(ClassA):
def __init__(self):
print("initizlizing B")
def fnX(self, x):
return 2*x
def printFnX(self, x):
print("ClassB:", self.fnX(x))
ClassA.printFnX(self,x)
bx = ClassB()
bx.printFnX(3)
The use case is described as a way of implementing the Open-Closed Principle in "The Art of Subclassing" found at http://www.youtube.com/watch?v=yrboy25WKGo&noredirect=1 .
The same can be achieved by making fnX and printFnX both classmethods.
class ClassA(object):
def __init__(self):
print("Initializing A")
# hoping that this function is called by this class's printFnX
#classmethod
def fnX(self, x):
return x ** 2
#classmethod
def printFnX(self, x):
print("ClassA:",self.fnX(x))
class ClassB(ClassA):
def __init__(self):
print("initizlizing B")
def fnX(self, x):
return 2*x
def printFnX(self, x):
print("ClassB:", self.fnX(x))
ClassA.printFnX(x)
bx = ClassB()<br>
bx.printFnX(3)

Categories