I'd like to make my python IDE give me an error or warning when I implement derived classes that break the base class contract and return the wrong type.
( By the way this is not a theoretical interest question, this kind of mistake actually happened and bit me at run time )
class Hair(ABC):
#abstractmethod
def show(self):
pass
class DogHair(Hair):
def show(self):
print("|||||")
class Student: # a completely unrelated class used by mistake
def print_student_details(self):
print("Some student info...")
class Animal(ABC):
#abstractmethod
def get_hair(self) -> Hair:
pass
class Dog(Animal):
def get_hair(self) -> Student: # OOPS! does not conform to the base class contract!
return Student()
dog:Dog = Dog()
dog.get_hair().print_student_details() # logical mistake, but works
is there any way to get an error or at least a warning for this?
I am using python 3.8, VS Code and pycharm
I have checked lots of websites and almost all questions related to the same on Stackoverflow. I cant find the solution to this problem. Its very important for a project. Please help. I want to use the variable self.email in Class A in the function email(self) in Class B. I've tried several things, but its not working. Inheritance wont work because its a kivy-python code and its already inheriting classes like GridLayout().
Class A:
def __init__(self):
---some code---
def email_id(self):
self.email = x
Class B:
def __init__(self):
print(A().email)
I think what you might be looking for are property decorators
Class A:
def __init__(self):
---some code---
#property
def email(self):
return (some code to show the email)
The field email of class A is created when email_id() is called.
Therefore, by the time print(A().email) is executed, this field is still not set.
You can set it first
Class B:
def __init__(self):
a = A()
a.email_id("email")
print(a.email)
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
I'm trying to inherit attributes from a super class but they are not being initialized correctly:
class Thing(object):
def __init__(self):
self.attribute1 = "attribute1"
class OtherThing(Thing):
def __init__(self):
super(Thing, self).__init__()
print self.attribute1
This throws an error since attribute1 is not an attribute of OtherThing, even though Thing.attribute1 exists. I thought this was the correct way to inherit and extend a super class. Am I doing something wrong? I don't want to create an instance of Thing and use its attributes, I need it to inherit this for simplicity.
You have to give, as argument, the class name (where it is being called) to super():
super(OtherThing, self).__init__()
According to Python docs:
... super can be used to refer to parent classes without naming them
explicitly, thus making the code more maintainable.
so you are not supposed to give the parent class.
See this example from Python docs too:
class C(B):
def method(self, arg):
super(C, self).method(arg)
Python3 makes this easy:
#!/usr/local/cpython-3.3/bin/python
class Thing(object):
def __init__(self):
self.attribute1 = "attribute1"
class OtherThing(Thing):
def __init__(self):
#super(Thing, self).__init__()
super().__init__()
print(self.attribute1)
def main():
otherthing = OtherThing()
main()
I have the following kind of superclass / subclass setup:
class SuperClass(object):
def __init__(self):
self.do_something() # requires the do_something method always be called
def do_something(self):
raise NotImplementedError
class SubClass(SuperClass):
def __init__(self):
super(SuperClass, self).__init__() # this should do_something
def do_something(self):
print "hello"
I would like the SuperClass init to always call a not-yet-implemented do_something method. I'm using python 2.7. Perhaps ABC can do this, but it is there another way?
Thanks.
Your code is mostly correct, except for the use of super. You need to put the current class name in the super call, so it would be:
super(SubClass, self).__init__()
Since you put in the wrong class name, SuperClass.__init__ wasn't called, and as a result do_something wasn't called either.