Reputation: 11
My setup: I am coding in python 3.7, with spyder4, on a windows10 machine.
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
, C
have 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.
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.
Upvotes: 1
Views: 165
Reputation: 11
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:
Using a factory function that builds your class - see Reblochon Masque's answer for that.
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
Upvotes: 0
Reputation: 36732
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()
from class A
from class A
from class B
from class B
from class C
from class C
Upvotes: 1
Reputation: 21
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
.
Upvotes: 1