Use a class variable across inheritance in Python - python

In Python, I want to define a top level class that can depend on a class variable. Then I want to be able to change that variable at the class level, for children of the class, but still inherit the functionality that uses that variable.
In general, my Parent class has some functions that depend on configuration variables. All my child classes use those same functions, but with different parameters. I would like to be able to change the parameters at the class level.
As the simplest example, here are two classes where the Parent defines functions in terms of my_global, then the Child attempts to change that variable (but fails)
class Parent():
my_global = "parent"
def _init_(self):
pass
def printmg(self):
print(Parent.my_global)
class Child(Parent):
my_global = "child"
my_parent = Parent()
my_parent.printmg()
my_child = Child()
my_child.printmg()
This outputs
parent
parent
While I would like it to output
parent
child
I don't wan't to keep the variables at the object level (i.e. self.my_global = "child"), or to rewrite the function for the child.

Change the line print(Parent.my_global) to print(self.my_global).
The self operater represents the current class. So printing like this will work.

If you don't need an instance method define printmg as classmethod:
#classmethod
def printmg(cls):
print(cls.my_global)

Related

How to tell a non-class method which child class a child object is?

I'm not well versed with inheritance in python. I'm currently writing a set of child classes that inherit from a single parent class, e.g.,
class child1(parent):
# define some stuff
class child2(parent):
# define some stuff
There's a separate non-class function that takes in one of the child objects and depending on which child object it is, it does certain things, e.g.,
def function(child_object):
if child_object is child1:
# do something
elif child_object is child2:
# do something else
I am wondering if there's a native way in the polymorphism properties in Python to allow function to tell which child class child_object is? Currently, I have a string in the parent class that gets set to the name of the child class and that's how the distinction is made.

Create child class instance from parent class instance

TL;DR: Python; I have Parent, Child classes. I have an instance of Parent class, parent. Can I make a Child class instance whose super() is parent?
Somewhat specific use case (workaround available) is as follows: I'd like to make an instance of Logger class (from Python logging module), with _log method overloaded. Methods like logger.info or logger.error call this method with a level specified as either INFO or ERROR etc., I'd like to replace this one method, touch nothing else, and make it all work seamlessly.
Here's some things that don't work (well):
I can't just inherit from logging.Logger instance and overload this one method and constructor, because Logger instances tend to be created via a factory method, logging.getLogger(name). So I can't just overload the constructor of the wrapper like:
class WrappedLogger(logging.Logger):
def __init__(self, ...):
super().__init__(...)
def _log(self, ...):
and expect it to all work OK.
I could make a wrapper class, which provides the methods I'd like to call on the resulting instance, like .info or .error - but then I have to manually come up with all the cases. It also doesn't work well when the _log method is buried a few calls down the stack - there is basically no way to guarantee that any use of the wrapped class will call my desired _log method
I can make a little kludge like so:
class WrappedLogger(logging.Logger):
def __init__(self, parent):
self._parent = parent
def _log(...): # overload
def __getattr__(self, method_name):
return getattr(self._parent, method_name)
now whenever I have an instance of this class, and call, say, wrapped.info(...), it will retrieve the parent method of info, call it, which will then call self._log which in turn points to my wrapped instance. But this feels very ugly.
Similarly, I could take a regular instance of Logger and manually swap out the method; this is maybe a bit less "clever", and less ugly than the above, but similarly underwhelming.
This question has been asked a few times, but in slightly different contexts, where other solutions were proposed. Rather than looking for a workaround, I'm interested in whether there is a native way of constructing a child class instance with the parent instance specified.
Related questions:
Create child class instances from parent class instance, and call parent methods from child class instance - here effectively a workaround is suggested
Python construct child class from parent - here the parent can be created in the child's constructor
If your goal is to supply a custom logger class that is used by getLogger, you can "register" the custom class with the logging manager.
So, let's define a custom logger class
from logging import Logger
class MyLogger(Logger):
def _log(self, level, msg, *args, **kwargs) -> None:
print("my logger wants to log:", msg)
super()._log(level, msg, *args, **kwargs)
Then we tell the global logging manager to use this class instead.
from logging import setLoggerClass
setLoggerClass(MyLogger)
Thank you #Daniil Fajnberg, for pointing out, that setLoggerClass exists.
Now getLogger will instantiate your custom class.
from logging import getLogger
logger = getLogger(__file__)
logger.error("Dummy Error")
This will log the error as normal and also print "my logger wants to log: ...".
Note: The _log method you are overloading is undocumented. Maybe there is a better way to achieve what you want.
If i am understanding correctly, what #Bennet wants is - he has some custom logger classes derived from Logger(Logger acts as interface) like Logger1, Logger2 etc(which implementation gets chosen will vary at runtime). On top of each of this he wants to add some functionality which modifies only the _log function of each of these implementations.
IMO there shouldn't be any direct way to do it, since what you are attempting is trying to modify(not extend) the behaviour of an existing class which is not recommended for OOP paradigm.
The hacky way is clever (found it cool).
def __getattr__(self, method_name):
return getattr(self._parent, method_name)
(I don't think you can do the same in Java)
P.S. Wanted to comment this but i am poor in SO it seems :)
From the way you keep re-phrasing your more general question, it seems you misunderstand how object creation works. You are asking for a
way of constructing a child class instance with the parent instance specified.
There is no such concept as a "parent instance". Inheritance refers to classes, not objects. This means you need to define yourself, what that term is supposed to mean. How would you define a "parent instance"? What should it be and when and how should it be created?
Just to demonstrate that there is no mechanism for creating "parent instances", when a child class instance is created, consider this:
class Foo:
instances = []
def __new__(cls):
print(f"{cls.__name__}.__new__()")
instance = super().__new__(cls)
Foo.instances.append(instance)
return instance
class Bar(Foo):
pass
bar = Bar()
assert len(Foo.instances) == 1
assert Foo.instances[0] is bar
assert type(bar) is Bar
assert isinstance(bar, Foo)
The output is Bar.__new__() and obviously the assertions are passed. This goes to show that when we create an instance of Bar it delegates construction further up the MRO chain (because it doesn't implement its own __new__ method), which results in a call to Foo.__new__. It creates the object (by calling object.__new__) and puts it into its instances list. Foo does not also create another instance of class Foo.
You also seem to misunderstand, what calling the super class does, so I suggest checking out the documentation. In short, it is just an elegant tool to access a related class (again: not an instance).
So, again, your question is ill defined.
If you mean (as #Barmar suggested) that you want a way to copy all the attributes of an instance of Foo over to an instance of Bar, that is another story. In that case, you still need to be careful to define, what exactly you mean by "all attributes".
Typically this would refer to the instances __dict__. But do you also want its __slots__ copied? What about methods? Do you want them copied, too? And do you want to just replace everything on the Bar instance or only update those attributes set on the Foo instance?
I hope you see, what I am getting at. I guess the simplest way is just update the instances __dict__ with values from the other one:
...
class Bar(Foo):
def update_from(self, obj):
self.__dict__.update(obj.__dict__)
foo = Foo()
foo.n = 1
foo.text = "Hi"
bar = Bar()
bar.update_from(foo)
print(bar.n, bar.text) # output: `1 Hi`
And you could of course do that in the __init__ method of Bar, if you wanted. If the initialization of Foo is deterministic and instances keep the initial arguments laying around somewhere, you could instead just call the inherited super().__init__ from Bar.__init__ and pass those initial arguments to it from the instance. Something like this:
class Foo:
def __init__(self, x, y):
self.x = x
self.y = y
self.z = x + y
class Bar(Foo):
def __init__(self, foo_obj):
super().__init__(foo_obj.x, foo_obj.y)
foo = Foo(2, 3)
bar = Bar(foo)
print(bar.z) # output: `5`
I hope this makes things clearer for you.

Is there a better way than overriding every method of my parent Python class?

I have a Python parent class with dozens of methods. These parent methods return a parent object.
Each of these methods is similar to a math operation on two objects (e.g. add(self,other), multiply(self,other), which returns the result of the operation as a new object of the same class.
I also have a child class, and its objects use all the parent methods. However, I need them to return the result as a new object of the child class not the parent class.
The child class has additional member variables and it has additional methods.
My first thought is to override each parent method with a child method that a) calls the eponymous parent method (child's add calls super's add) , b) converts the returned parent object into a new child object to set the additional child member variable, and c) returns the new child object.
Apart from the additional property that the child has over the parent, the conversion also allows me to perform type assertions to ensure I have submitted a child object as a function parameter, where required.
Maybe this is all par for the course. But it seems tedious, and cluttery, as I will have to write many such small overriding methods that all do the same thing (call the parent's method verbatim, convert the result).
What I also do not like about this approach is that if the parent is from a library used elsewhere, I'd have to write the overrides for each parent method. To future proof I'd even have to do this for methods I presently don't intend to use.
What are my alternatives? Or is there a better way to set up the classes in the first place, to avoid this?
It has crossed my mind to switch parent and child, but then this new child (formerly parent) will carry around a member variable that means nothing to it, and will have access to methods that make no sense to it.
I assume you have something like
class Parent:
def __add__(self, other):
return Parent(...)
when you probably want
class Parent:
def __add__(self, other):
return type(self)(...)
This allows the method to return a value whose type depends on its arguments (specifically, its first argument) rather than which class defined it.
Define the parent class' methods to take the class of self into consideration:
>>> class Parent:
... def __init__(self): pass
... def method(self): return self.__class__() # or type(self)()
...
>>> class Child(Parent): pass
...
>>> Child().method()
<__main__.Child object at 0x00000151EB7E0AF0>
>>> Parent().method()
<__main__.Parent object at 0x00000151EB977640>

Avoid using child method in parent class in python

Suppose I have two classes in Python as below:
class Parent(object):
def __init__(self):
self.num = 0
def fun1(self):
print 'p.fun1'
def fun2(self):
self.fun1()
print 'p.fun2'
and
from parent import Parent
class Child(Parent):
def __init__(self):
super(Child,self).__init__()
def fun1(self):
print 'c.fun1'
def fun2(self):
super(Child, self).fun2()
print 'c.fun2'
and if I call fun2 of Child
from child import Child
test = Child()
test.fun2()
I get output:
c.fun1
p.fun2
c.fun2
Which means the call of Child.fun2() leads to Parent.fun2(). But inside the Parent.fun2(), I use self.fun1() which in this case interpreted as Child.fun1() in my test.
But I really want the class Parent to be individual and the call of Parent.fun2() always uses Parent.fun1() inside it.
How can I avoid this?
I only know that I can make Parent.fun1() private into Parent.__fun1(). But I also have some instances of Parent where I need to use Parent.fun1() outside this class. That means I really need to override the fun1().
That's how inheritance is supposed to work. For the behavior you need, you might want to reconsider Parent & Child class's relationship, or change the method names or at least make the Parent methods classmethods or staticmethods.
This should work for your need, but I don't really like it.
class Parent(object):
def __init__(self):
self.num=0
def fun1(self):
print 'p.fun1'
def fun2(self):
Parent.fun1(self)
print 'p.fun2'
Child class can remain the same.
In all classes accessed in the inheritance chain, self will always point to the instance of class actually instantiated and not the current class accessed in super call (or in order to find method/attribute for that matter). So self.fun2 will always point to the method of Child class.
There is a mechanism called Name Mangling:
Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.
Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls
Python Classes Documentation
This should work:
class Parent(object):
def __init__(self):
self.num = 0
def fun1(self):
print 'p.fun1'
def fun2(self):
self.__fun1()
print 'p.fun2'
__fun1 = fun1

Init child with Parent instance

I have a function which return instances of the class Parent:
def generateParent():
do_stuff
return Parent(some_parameters)
Now I want to init a subclass of Parent with the results of a call to generateParent():
class Child(Parent):
def __new__():
return generateParent(some_other_parameters)
The problem is, when I override some methods from Parent in Child and then call them in instances of Child in my program, the original Parent method gets called instead of the new one from Child. Am I doing something wrong here? Am I using the correct design here for my task?
EDIT: I don't have access neither to Parent nor generateParent()
Solution(thanks to #Paul McGuire's answer):
class Child(object):
def __init__(self):
self.obj = generateParent()
def __getattr__(self, attr):
return getattr(self.obj, attr)
Since generateParent is not your code, then instead of inheritance, you might want to use containment and delegation. That is, instead of defining a subclass, define a wrapper class that contains the generated object, forwards method calls to it when needed, but can add new behavior or modified behavior in the wrapper.
In this question, the OP had a similar situation, having a class generated in a libary, but wanting to extend the class and/or modify some behavior of the class. Look at how I added a wrapper class in that question, and you might consider doing something similar here.
Here's one way to do it:
def generateChild(params):
p = generateParent(params)
p.__class__ = Child
return p
class Child(Parent):
# put method overrides etc here
childinstance = generateChild(some_params)
Perhaps you want generateParent to be able to make instances of other classes:
def generateParent(cls=Parent):
do_stuff
return cls(some_parameters)
Now this will make a Child object:
child = generateParent(Child)
Or perhaps you want Parent and all of its derived classes to use common initialization code?
class Parent(object):
def __init__(self):
do_stuff
# init from some_parameters
class Child(Parent):
# blah..
Make your Child object able to copy information from a created Parent object:
class Child(Parent):
def __init__(self):
model_parent = generateParent()
self.a = model_parent.a
self.b = model_parent.b
# etc.

Categories