Inherit from a class given as an argument - python

My setup: I am coding in python 3.7, with spyder4, on a windows10 machine.
Context
I am writing classes for a package. It is some kind of handler for classes defined in another package that I am importing.
Here is a simplified version of the situation:
# This I cannot modify, as they are in the package
class Letter_Class():
def __init__(self, name):
self.name = name
class A(Letter_Class):
def source(self):
print("from class A")
def funcA(self):
print("from class A")
class B(Letter_Class):
def source(self):
print("from class B")
def funcB(self):
print("from class B")
class C(Letter_Class):
def source(self):
print("from class C")
def funcC(self):
print("from class C")
# This I can modify
class Dynamic(Letter_Class):
def __new__(self, name, this_class): # -------- issue lies here? --------
obj = this_class(name)
return obj
def new_func(self):
print("function defined in dynamic class")
The classes Letter_Class, A, B, Chave already been defined by someone else (in that 'other package' I am handling) and I cannot change them. I want to make a class, here called "Dynamic", who will inherit from a class given as an argument. It must still have the (many, many) methods that I wish to add such as new_func(). I cannot know in advance which Letter_Class child is used.
Classes A, B, C have both polymorphic and specific methods, such as source() and funcA(). Because of that, I cannot make Dynamic inherit from these classes. It would have been too easy.
Problem
If I execute the previous and the following code:
# user executes in main:
instance = Dynamic("test name", B)
print(instance.name)
instance.source()
instance.funcB()
instance.new_func()
I get:
test name
from class B
from class B
AttributeError: 'B' object has no attribute 'new_func'
The instance is created, source() and funcB() are executed without any problem.
Then, an attribute error is raised, because a Device object was not truly created.
Could anyone explain to me what to put in the __new__() constructor function to actually return an instance of the Device class, with the proper inheritance from this_class? I would be very grateful.
This discusion seems to address the issue, but in a different context and I cannot apply the suggested solution.

The problem is that you're setting instance to equal the return value of the Letter_Class; instance is a B object, and has no relationship with Dynamic.

You could use a factory function that builds your class instances using the given class to inherit from:
class Letter_Class:
def __init__(self, name):
self.name = name
class A(Letter_Class):
def source(self):
print("from class A")
def funcA(self):
print("from class A")
class B(Letter_Class):
def source(self):
print("from class B")
def funcB(self):
print("from class B")
class C(Letter_Class):
def source(self):
print("from class C")
def funcC(self):
print("from class C")
def make_dynamic_letterclass_factory(cls):
"""makes an returns a Letter_Class subclass instance that inherits
from the class passed as argument
"""
class Dynamic(cls):
def __init__(self):
pass
def new_func(self):
print("function defined in dynamic class")
return Dynamic()
a = make_dynamic_letterclass_factory(A)
a.source(), a.funcA()
b = make_dynamic_letterclass_factory(B)
b.source(), b.funcB()
c = make_dynamic_letterclass_factory(C)
c.source(), c.funcC()
output:
from class A
from class A
from class B
from class B
from class C
from class C

Solutions
I have found a solution to MY problem (thanks to the SO community). To all coders searching for a solution to THEIR problem, the use case might make you want to choose one of these two solutions:
factory functions
Using a factory function that builds your class - see Reblochon Masque's answer for that.
using a proxy class
This was suggested by juanpa.arrivillaga in this question.
This is the solution I believe I will be using from now on:
# classes Letter_Class, A, B, and C are the same as in the "Context" section (above)
class Proxy():
""" attempt to make a transparent proxy
"""
def __init__(self, name, letterclass, *args, **kwargs):
self.letter = letterclass(name, *args, **kwargs)
def __getattr__(self, attribute):
return getattr(self.letter, attribute)
class Dynamic(Proxy):
""" My actual class and its methods
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# anything else to initiate the instance
def new_func(self):
print("calling a method defined in the Dynamic class")
I can now execute:
instance = Dynamic("foo bar", B)
print(f"instance is called: '{instance.name}'")
instance.source()
instance.funcB()
instance.new_func()
I will get:
instance is called: 'foo bar'
from class B
from class B
calling a method defined in the Dynamic class
Any calls to the attributes that are NOT defined in the Dynamic class will be redirected to self.letter, with the magic method: __getattr__(). Using __getattribute__() instead would redirect ALL calls to self.letter instead.
I chose this approach specifically because I expect my users to be building their own type of Dynamic class; I find it simpler to make an intermediate proxy class.
This resource was also helpful, and has clues to make a much more transparent proxy class: Object proxying. I did not attempt to implement this though.
So, thanks for the help folks, Take care.
XC

Related

Can you override a function from a class outside of a class in Python

Can you override a function from a class, like:
class A:
def func():
print("Out of A")
classA = A
# Is something like this possible
def classA.func():
print("Overrided!")
Wanted Output:
Overrided
I googled "python override function", "python override function from class" and so on but couldnt find anything that fits. I found just how to override the parent function.
You most likely shouldn't do this. If you want to change a single part of some class, make a new class that inherits from it and reimplement the parts you want changed:
class A:
#staticmethod
def func():
print("Out of A")
classA = A
class B(A):
#staticmethod
def func():
print("Overridden!")
A.func()
B.func()

Calling subsclass #classmethod from parent class

I'm trying to do the following:
class A:
#classmethod
def test_function(cls, message):
cls.__get_the_function()
class B(A):
#classmethod
def __get_the_function(cls):
return print("BBBB")
class C(A):
#classmethod
def __get_the_function(cls):
return print("CCCC")
however when I call:
B.test_function("Test")
I get the following:
AttributeError: type object 'B' has no attribute '_A__get_the_function'
I want class A to __get_the_function from the subclass (either class B or C depends on which one I use), but it looks like it is trying to look for it in itself.
NOTE: I'm using Python 3.8.2
__-prefixed names are handled specially during class creation. The name is replaced when the function is defined by a mangled name, as if you had defined the function as
#classmethod
def test_function(cls, message):
cls._A__get_the_function()
in the first place.
This is done to explicitly provide a way to hide a name from a subclass. Since you want to override the name, __get_the_function isn't an appropriate name; use an ordinary _-prefixed name if you want to mark it as private:
class A:
#classmethod
def test_function(cls, message):
cls._get_the_function()
# Define *something*, since test_function assumes it
# will exist. It doesn't have to *do* anything, though,
# until you override it.
#classmethod
def _get_the_function(cls):
pass

Proper way of defining init() methods with different number of parameters (to be used in multiple inheritance)

This is my current setup:
class Base():
def __init__(self):
pass
# ...other methods
class A(Base):
def __init__(self, dst, fname, alg, src=None):
super().__init__()
# I then use these instance variables throughout instance methods.
self.src = [] if src is None else src
self.dst = dst
self.fname = fname
self.alg = alg
# ...other methods
class B(Base):
def __init__(self, fname):
super().__init__()
# I then use these instance variables throughout instance methods.
self.fname = fname
# ...other methods
class C(A, B):
"""Here is my problem.
When I try to inherit A and B this way,
I keep getting those "missing required positional args..." errors
"""
def __init__(self, dst, src=None):
super().__init__()
# I then use these instance variables throughout instance methods.
self.fname = fname
# ...other methods
Here is what I am trying to do right now:
class Base():
def __init__(self, *args, **kwargs):
pass
class A(Base):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class B(Base):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class C(A, B):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
My main question:
What is the "best" (standard, most preferred, efficient, readable etc.) way to handle such situations?
P.S.
I read the following article, Python’s super() considered super!, but I could not derive the best answer for myself after reading it.
Also, I referenced this SO question but the accepted answer does not have different number of params like I do...
By the way, I am willing to hear that my design (class hierarchy) is bad overall. Technically, I could relocate methods that I am trying to inherit from A and B in C to the Base class...I hope (did not try yet)...
Based on your implementation, to remove the error, I think it would be best to explicitly use the base class' required parameters based on the method resolution order (in your case, the base class for the class C should be class A).
class C(A, B):
def __init__(self, dst, src=None):
fname = 'some value'
super().__init__(dst, fname, alg)
This will be the simplest solution that can be done. But if you are willing to make design changes, I would recommend using composition instead of inheritance for python. I assume that you have methods in class A and class B that is needed by class C. With composition, needed functions/operations can just be added to the classes that needs it. It would also prevent code smells like refused bequest
Python has an advantage that supports multiple inheritance, even the box office technology java doesn't supports this feature because it leads to overriding of methods when both extended class have a same method name.
But python overcomes this problem and supporting multiple inheritance. To use multiple inheritance in python, should know the arguments to define in proper way.
class C(A, B):
def __init__(self, dst, src=None):
super().__init__()
self.fname = fname
In the above obove code, Class C inherits Classes A and B. Here first of all super().__init__() navigate to Class A, search for .__init__(), if init() or some other method abc() not in Class A, then next it will look up into Class B,
These navigation go through based on sequential order that you inherits into Class C
Here Solution for you problem is, you defined super class __init__() with parameters in both Class A & B, and calling super class super().__init__() with out parameters in Class C.

Refer to a class outside its method?

I need to deliver something like this in my program
class the_class_name(Parent):
the_attribute = self.parent_class_method()
#the parent class method will return a value
#but I cannot use self here since there's no self
How can I carry this out? Is there any other alternative that can do the job for me?
I have tried using __init__ like this:
def __init__(self):
Parent.__init__(self)
self.attribute = self.the_method()
But then I have problem creating the object, it won't receive any parameters that the Parent class normally receives anymore
Sounds like you are looking for __init__:
class TheClassName(Parent):
def __init__(self):
# Set attribute to the result of the parent method
self.attribute = super(TheClassName, self).the_method()
EDIT
If your parent class has parameters in it's own __init__ function, include them in the child class:
class Parent(object):
def __init__(self, foo, bar):
...
#classmethod
def the_method(cls):
...
class TheClassName(Parent):
def __init__(self, foo, bar):
super(TheClassName, self).__init__(foo, bar)
self.attribute = super(TheClassName, self).the_method()
I don't quite understand why you don't just call the parent method on your child object when you need the value though.
There is no self at that point of the creation of the subclass, nor is there an instance of the Parent class. That means the only Parent class methods you could call would have to be either static or class methods.
To demonstrate:
class Parent(object):
#staticmethod
def static_method():
return 42
#classmethod
def class_method(cls):
return 43
class TheClassName(Parent):
the_attribute = Parent.static_method()
another_attribute = Parent.class_method()
print(TheClassName.the_attribute) # -> 42
print(TheClassName.another_attribute) # -> 43
You must use class methods, declared with the #classmethod decorator, or a #staticmethod. The #classmethod decorator is preferable so that inheritance is handled correctly, i.e. the method is invoked on the derived class (a bit of a technicality, if you are still learning this).
class Alpha(object):
#classmethod
def method1(cls):
return 'method1 has been called on {}'.format(cls)
class Beta(Alpha):
def __init__(self):
self.myattr = Beta.method1()
print(Beta().myattr)
method1 has been called on class <'__main__.Beta'>
Use
super(ClassName, self).methodname(arg)
inside a method
def child_method(self, arg):
super(ClassName, self).methodname(arg)
You cannot use self outside a method.

initialize base class with variable not coming from derived class

I'm trying to provide framework which allows people to write their own plugins. These plugins are basically derived classes. My base class needs some variables to initialize, how can I initialize my base class without having to let my derived class feed the variable in the base class initialization?
#!/bin/python
class BaseClass():
def __init__(self,config):
self.config=config
def showConfig(self):
print "I am using %s" % self.config
class UserPlugin(BaseClass):
def __init__(self,config):
BaseClass.__init__(self,config)
def doSomething(self):
print "Something"
fubar = UserPlugin('/tmp/config.cfg')
fubar.showConfig()
My goal is to avoid the need to define the config parameter in the UserPlugin class, since this is something I don't want the user who writes a plugin to be bothered with.
You can use argument lists to pass any remaining arguments to the base class:
class UserPlugin(BaseClass):
def __init__(self, *args, **kwargs):
BaseClass.__init__(self, *args, **kwargs)
Based on your Pastebin code, how about this? This avoids using a separate global, instead using a class attribute, which is accessible as a member to all derived classes and their instances.
#!/bin/python
class BaseClass():
config = '/tmp/config.cfg'
def __init__(self):
pass
def showConfig(self):
print "I am using %s" % self.config
class UserPlugin(BaseClass):
def __init__(self):
BaseClass.__init__(self)
def doSomething(self):
print "Something"
fubar = UserPlugin()
fubar.showConfig()
This was the other way to do it that I mentioned before. Keep in mind that if you want to change the value of BaseClass.config itself, you should access it directly (i.e. BaseClass.config = '/foo/path'; otherwise, you wind up creating a custom UPinstance.config value, leaving BaseClass.config unchanged.

Categories