Redefining methods through class inheritance in Python - python

I'm playing around with class inheritance and I'm wondering if its possible to change aspects of an inherited method in the child class without having to rewrite the whole thing?
For example:
class Parent:
def _init_(self, x):
self.x = x
def foo(self):
a = self.x
if a > 0:
forward = True
elif a < 0:
forward = False
return forward
class Child1(Parent):
def foo(self, y=None, bool=False):
if bool:
a = y
else:
a = self.x
super().foo()
class Child2(Parent):
pass
What I'm looking for is if I called Child1.foo it could reassign the variable a before running through
the method, as defined in the parent class. Where a is dependent on the y and bool arguments passed through the rewritten method in Child1:
print(Child1(2).foo(-2, True))
# => False
print(Child1(2).foo())
# => True
print(Child2(2).foo())
# => True
Is this possible, or would I just have to rewrite a new method for each class?

I think I understand your problem and I have some suggestions for how you can resolve this:
Using "private" methods
For example:
class Parent:
def __init__(self, x):
self.x = x
def _foo(self, a=None):
a = a if a else self.x * 2
if a > 10:
over = True
else:
over = False
return over
def foo(self):
return self._foo()
class Child1(Parent):
def foo(self, y=None, condition=False):
if condition:
a = y*2
else:
a = self.x*2
return self._foo(a)
class Child2(Parent):
pass
In this example, all child classes will inherit the _foo "private" function, where they may or may not receive a value of a.
Using abstract classes
There is another solution to this problem with abstract classes (here is an example of how to do this), where you forces the child class to implement the function foo:
Important
Remembering that in the case of abstract classes, if you do not define the function decorated with #abstractmethod, you will receive an error similar to this TypeError: Can't instantiate abstract class Child2 with abstract methods foo
Example:
Python 2.x
from abc import ABCMeta, abstractmethod
class Parent:
__metaclass__ = ABCMeta
def __init__(self, x):
self.x = x
def _foo(self, a=None):
a = a if a else self.x * 2
if a > 10:
over = True
else:
over = False
return over
#abc.abstractmethod
def foo(self):
pass
class Child1(Parent):
def foo(self, y=None, condition=False):
if condition:
a = y*2
else:
a = self.x*2
return self._foo(a)
class Child2(Parent):
def foo(self):
return self._foo()
Python 3.x
class Parent(metaclass=ABCMeta):
def __init__(self, x):
self.x = x
def _foo(self, a=None):
a = a if a else self.x * 2
if a > 10:
over = True
else:
over = False
return over
#abc.abstractmethod
def foo(self):
pass
class Child1(Parent):
def foo(self, y=None, condition=False):
if condition:
a = y*2
else:
a = self.x*2
return self._foo(a)
class Child2(Parent):
def foo(self):
return self._foo()
In both examples you will have the same result by running this:
print(Child1(2).foo(10, True)) // True
print(Child1(2).foo()) // False
print(Child2(2).foo()) // False

Related

Inheriting variables from multiple class functions with super()

class base():
def __init__(self):
self.var = 10
def add(self, num):
res = self.var+num
return res
class inherit(base):
def __init__(self, num=10):
x = super().add(num)
a = inherit()
print(a)
Hello,
I'm learning about inheritance and super(). When running this, the error AttributeError: 'inherit' object has no attribute 'var' is returned. How can I inherit the init variables too?
You first need to call super constructor because you did not define var in your base class constructor.
Working version of your code (though you should probably add var in base __init__)
class Base:
def __init__(self):
self.var = 10
def add(self, num):
res = self.var + num
return res
class Inherit(Base):
def __init__(self, num=10):
super().__init__()
x = super().add(num)
a = Inherit()
print(a)
one possible solution
class Base:
def __init__(self, var=10):
self.var = var
def add(self, num):
res = self.var + num
return res
class Inherit(Base):
pass
a = Inherit()
a.add(0) # replace 0 with any integer

Can you make a class attribute a specific instance of that class within the class definition?

Was wondering if there was a way to set a class attribute to a specific instance from within the class definition. For example,
class Value:
def __init__(self, x):
self.x = x
# Something like
# half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
I'm also aware I can easily set it outside the class that seems a bit more bulky and error prone, like this
class Value:
def __init__(self, x):
self.x = x
Value.half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
No. At the time the body of the class is being evaluated, the class doesn't yet exist. A class statement is a declarative syntax for calling a metaclass:
class Value:
def __init__(self, x):
self.x = x
is roughly equivalent to
def init(self, x):
self.x = x
Value = type('Value', (object,), {'__init__': init})
Your class attribute would have to be a member of the dict passed as the third argument, which has to be fully defined before type is called.
not quite, but you can make a class method that return a new instance of your class in whatever way you want with the classmethod decorator
>>> class Value:
def __init__(self, x):
self.x=x
def __repr__(self):
return f"{type(self).__name__}({self.x})"
#classmethod
def half(cls):
return cls(0.5)
>>> Value(10)
Value(10)
>>> Value.half()
Value(0.5)
>>>
look like in py3.9 you can combine it with the property decorator to accomplish just that, see linked documentation above (but I don't have it at the moment)
Simply, you can't because the class hasn't yet existed. But you can use either metaclass or class decorator to achieve the same goal as the following shows:
#Metaclass
class Meta(type):
def __init__(cls, clsname, clsbases, clsdict):
cls.half = cls(0.5)
class Value(metaclass=Meta):
def __init__(self, x):
self.x = x
#Decorator
def decorator(cls):
cls.half = cls(0.5)
return cls
#decorator
class Value2:
def __init__(self, x):
self.x = x
print(Value.half.half.x)
print(Value.half.x)
print(Value2.half.half.x)
print(Value2.half.x)

python object building using multi-inheritance

I want to build an object dynamically which allow use to mix the class properties in whichever way they like base on multiple inheritance. This is the expected behaviour. These classes are dataclasses so there won't be many methods in them, mostly data properties.
class Foo():
def bar(self, x):
return x
class FooA(Foo):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
class FooB(Foo):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
class FooC(FooA, FooB):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
f = FooC()
f.bar('S') # SFooBFooAFooC
However this code violate the DRY principle in broad daylight, hence I want to avoid the bar method completely, if there is no special operations in the current class.
Ideally I want something like
#bar_wrapper
class FooA(Foo):
pass
# OR
class FooA(Foo):
__metaclass__ = BarBase
Instead of this full implementation
class FooA(Foo):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
Essentially is there a way that I extract the middle layer class information in a multi-level inheritance class through a decorator or metaclass (the two options that I can think of)? Anyone has any idea on how to do this?
Write a class decorator that adds the bar method to the class:
def bar_wrapper(cls):
def bar(self, x):
p = super(cls, self).bar(x)
p += cls.__name__
return p
bar.__module__ = cls.__module__
bar.__qualname__ = '{}.{}'.format(cls.__qualname__, bar.__name__)
cls.bar = bar
return cls
class Foo():
def bar(self, x):
return x
#bar_wrapper
class FooA(Foo):
pass
#bar_wrapper
class FooB(Foo):
pass
#bar_wrapper
class FooC(FooA, FooB):
pass
f = FooC()
print(f.bar('S')) # SFooBFooAFooC

Python method using class variables, right approach

I have a question about python using OOP.
If I have two classes, and a method which takes variables from those classes, I pass the classes as parameters of the method as shown below. I am sure that this is not the right approach, and this is why I would like to know an alternative and more efficient way to do it. Specifically:
class Player(object):
x_player = 5
y_player = 5
class Food(object):
x_food = 10
y_food = 10
def method(Player, Food):
if Player.x_player > Food.x_food:
print('Food behind)
if Player.x_player < Food.x_food:
print('Food in front')
Is this correct? If I use the method def __init(self)__ then I cannot pass those variables in the function.
Thank you
If I use the method def init(self) then I cannot pass those variables in the function.
This understanding is wrong, as you can add parameters to __init__.
How about this? Or you still insist to use class method? If you choose class method, your solution already ok, just call with method(Player, Food) or even you did not need the parameters added in the function.
class Player(object):
def __init__(self, x, y):
self.x_player = x
self.y_player = y
class Food(object):
def __init__(self, x, y):
self.x_food = x
self.y_food = y
def method(player, food):
if player.x_player > food.x_food:
print('Food behind')
if player.x_player < food.x_food:
print('Food in front')
method(Player(5, 5), Food(10, 10))
if those are class variables, you can just refer to those variables as classname.var_name:
class A:
a = 10
if __name__ == '__main__':
print(A.a)
if the values are defined by instance, then you need to instantiate the class variables and then you can perform comparison on that
class A:
def __init__(self, a):
self.a = a
if __name__ == '__main__':
x = A(5)
y = A(10)
print(x.a > y.a)
>> False
Also this is python and in python you don't specify the data types of function arguments so you can send any object and be carefree of which class' instance it is. You code should handle the exceptions if a wrong class' object is sent.
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, a):
self.a = a
def fun(obj):
print(obj.a)
if __name__ == '__main__':
x = A(5)
y = B(10)
fun(x)
fun(y)
>> 5
>> 10
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, a):
self.a = a
def fun(obj):
if not isinstance(obj, B):
print(obj.a)
if __name__ == '__main__':
x = A(5)
y = B(10)
fun(x)
fun(y)
>> 5

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