Class inheritance, how to improve? - python

i have a little question that is discouraging me i have this portion of code:
class A(object):
def __init__(self):
self.variable = "Hello World!!!"
class B(A):
def __init__(self):
self.A = A()
self.inherited = self.A.variable
the thing is that i have a bunch of variables on class A that i don't want to instantiate and declare on class B one by one
is there a way to improve the code?

You are using a mix of composition and inheritance. It seems like you want to exclusively use composition:
class A(Object):
def __init__(self):
self.c = C()
self.e = E()
class B(Object):
def __init__(self):
self.c = C()
self.d = D()
Where C, D, and E are components that group variables and methods together. Now A and B only share the component of C. You should look for more complete composition tutorials
Edit: Actually just double checking it looks like your just confused about instantiating super class variables. Other answers correctly addressed this with super

You are misunderstanding two concepts.
For example, you are trying to do both composition and inheritance.
class A(Object):
def __init__(self):
self.variable = "Hello World!!!"
class B(A):
def __init__(self):
self.A = A()
self.inherited = self.A.variable
When you do this, you are saying, "Make B also an A object." In other words, the following works:
class A(object): # can omit this (object) in python3
def __init__(self):
self.value_from_a = 'Im from A!'
class B(A):
def __init__(self):
super().__init__()
self.value_from_b = 'im a bbbbb!'
b = B()
print(b.value_from_a)
print(b.value_from_b)
So, in your case the way to do this is to not try to make an A both the base as well as part of B.
Note if you are using Python2 the above syntax will be slightly different.

Related

Not being able to inherit the logger [duplicate]

Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())

Only declare variable if doesn't exist yet in the initialization of a class

I'm working for the first time with OOP in a python project. I have 3 classes: PRA, GDB and XLS.
PRA = Main Class
GDB = Responsible for control databases
XLS = Responsible for control xls(x)
UserInput = Responsible for validate user input
How i'm doing:
PRA:
class PRA(GDB, XLS, UserInput):
__init__():
self.gdb_file, self.xls_file = self.ask_questions() # <--- ask_questions is inside the UserInput class.
self.do_something()
XLS:
class XLS:
do_something():
print(self.xls_file)
__init__(self, xls_file):
self.xls_file = xls_file
GDB:
class GDB:
__init__(self, gdb_file):
self.gdb_file = gdb_file
I would like to know if is a bad practice to initialize the gdb_file and xls_file inside the PRA.init, and if not, how can i run the initialization of self.xls_file and self.xls_gdb only in the PRA?
This is not really an answer, but more of an example of code reuse. I'm not sure if this is super Pythonic, though. Anyhow, take a look at this example:
import inspect
class A:
def __init__(self):
self.a = 1
class B:
def __init__(self):
self.b = 2
class C(A, B):
def __init__(self):
for super_class in inspect.getmro(type(self))[1:]:
super_class.__init__(self)
c = C()
print(c.a, c.b)
A bit OT but you may want to think twice about your class names... They are really terrible.
Now wrt/ your question... Technically, the way to handle proper initialisation of a parent class is to call it's initialiser from the child class, ie:
class Parent(object):
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
super(Child, self).__init__(name)
self.age = age
You can also explicitly name the parent class, ie:
class Child(Parent):
def __init__(self, name, age):
Parent.__init__(self, name)
self.age = age
but it has a couple drawbacks, like harcoding the parent class in the call (so you have two or more places to edit if you change the parent class), and, more important, not taking care of proper resolution of the "diamond" problem in multiple inheritance (ie D child of B & C, B child of A, C child of A).
Actually, the only reason to not use super() would be when parent classes constructor are not compatible, but this is a huge design smell - if you have two or more parent classes that are not compatible, then you're certainly not using inheritance the right way. And that's actually the case in your example: your "main" class "is" not a "Database controller", it uses a Database controller instead, so you want to use composition, not inheritance
class ServiceA(object):
# code here
class ServiceB(object):
# code here
class Main(object):
def __init__(self, arg1, arg2, arg3):
self.arg1 = arg1
self.service_a = ServiceA(arg2)
self.service_b = ServiceB(arg3)
def run(self):
something = self.service_a.ask(question)
self.service_b.do_something_with(something)
Also you may want to avoid user interactions and costly resource acquisitions in constructors... Better use an entry point method for the first case (like app = MyApp(params); app.run()`) and lazy initialization for the second case (wait until you need the resource to acquire it, use a private attribute as cache an a public property that will take care of acquisition on first access).
[edit]
Someone suggested a dirty hack based on introspection, ie:
import inspect
class A:
def __init__(self):
self.a = 1
class B:
def __init__(self):
self.b = 2
class C(A, B):
def __init__(self):
for super_class in inspect.getmro(type(self))[1:]:
super_class.__init__(self)
c = C()
print(c.a, c.b)
This is definitly not something to do.
If all your parent classes have compatible constructors and properly use super calls, then all you have to do is using super instead:
class A(object):
def __init__(self):
super(A, self).__init__()
self.a = "a"
class B(object):
def __init__(self):
super(B, self).__init__()
self.b = "b"
class C(A, B):
def __init__(self):
super(C, self).__init__()
self.c = "c"
c = C()
print c.a, c.b, c.c
This also takes care of the "diamond inheritance" problem by calling parent classes in the right order:
class D(C, A):
def __init__(self):
super(D, self).__init__()
self.d = "d"
d = D()
print "d : ", d.a, d.b, d.c, d.d
Note that unless only one of the parent classes is a "proper" class (with state) and all other are mixin classes (stateless classes that only add functionalities), multiple inheritance is more often than not a design smell (as well as a maintainance hell). Since Python doesn't use static typing, inheritance is mostly used for implementation inheritance, and for a lot of cases composition/delegation (or just plain composition as in the OP case) is a better solution than implementation inheritance.

A Python Puzzler

I was wandering if you have any suggestions on how I should perform the following task in python:
Suppose I have the following classes:
class A(object):
self._classes = []
def magic(self):
c.foo() for c in self._classes
class B(object):
def foo():'''some cool stuff here'''
class C(B):
def foo():'''very cool stuff'''
class D(B):
def foo():'''very cool stuff'''
What I want to do is when class A is instantiated all classes of type B - (C and D) will be insantiated in self._classes, meaning _classes is [C(),D()].
The general motivation for this, is that I want the user to easily add classes without the need to know about class that uses them. Any help will be appricated.
Voila (thanks to this answer for all_subclasses()):
# recursively get all subclasses of a given class
def all_subclasses(cls):
return cls.__subclasses__() + [g for s in cls.__subclasses__()
for g in all_subclasses(s)]
class B(object):
def foo(self): print '''some cool stuff here in B'''
class C(B):
def foo(self): print '''very cool stuff in C'''
class D(B):
def foo(self): print '''very cool stuff in D'''
class E(D):
def foo(self): print '''very cool stuff in E'''
class A(object):
def __init__(self):
self._classes = [cls() for cls in all_subclasses(B)]
def magic(self):
for c in self._classes: c.foo()
# usage:
A().magic()
Output:
very cool stuff in C
very cool stuff in D
very cool stuff in E
If you know the module in question for example modulex, you can use dir(modulex) to list all the names in the module and then for each name x you can use modulex.__dict__.get(x) to get the actual object.
Then just check if it is of type of B.
In python you can store objects like other methods in list , so first note that you need to define other class then store them in a list , also you need to using self as your foo functions argument! if you haven't subclasses you can use this :
class B(object):
def foo(self):
print 'B'
class C(B):
def foo(self):
print 'C'
class D(B):
def foo(self):
print 'D'
class A(object):
def __init__(self):
self._classes = [B(),C(),D()]
def magic(self):
for c in self._classes:
c.foo()
A().magic()
resoult:
B
C
D

Python multiple inheritance constructor not called when using super()

Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())

How can i have 2 classes reference the same instance of a class

I have class A class B and class C.
class A and B can affect class C. so they need to refer to the same instance of the class.
#a.py
from C import C
Cinstance = C()
Cinstance.add()
#b.py
class b(object)
#i need to refer to 'cinstance' here to control the same instance of the class
#C.py
class C(object)
def __init__(self):
self.a=1
def add(self):
self.a += 1
print a
How do i need to import and instanciate the classes for it to work this way? I am new to programming and still learning so things that are obvious are still a little difficult for me right now.
class A:
def __init__(self,cInst):
self.c = cInst
class B:
def __init__(self,cInst):
self.c = cInst
cInst = C()
a = A(cInst)
b = B(cInst)
something like that maybe
based on what you have there I think the easiest thing would be to import Cinstance from module a.
from a import Cinstance
You can pass an instance of A and B to your C.__init__ method and save them as attributes of C.
I'm on my phone, so the code below isn't tested
class C(object):
def __init__(self, a, b):
self.a = a
self.b = b
>>> c = C(A(), B())

Categories