How does this python inheritance hierarchy work? (specific example) - python

I am working on a code base that uses oop and I am relavtively new to it. My question specifically is, why NewMenuItem not inherit from File?
code bunk to play with code: https://codebunk.com/b/350127244/
"""Build class hierarchy and get values from decendants"""
import inspect
def selftest():
class Menu(object):
def __init__(self):
super(Menu, self).__init__()
self.value = "Menu"
class MenuBar(Menu):
#having object in there makes it a new style object, which allows us to use super
def __init__(self):
super(MenuBar, self).__init__()
self.value = "MenuBar"
class File(MenuBar):
def __init__(self):
Menu.__init__()
super(File, self).__init__()
self.value = "File"
self.FileValue = "File here!"
class New(Menu):
def __init__(self):
Menu.__init__()
pass
class NewMenuItem(Menu):
def __init__(self):
"""
Q 1- Why do I need self here?
Menu.__init__(self)
"""
Menu.__init__(self)
pass
def show_vals(self):
print(self.value)
"""
Q 2 -why wont this work?
def show_vals2(self):
print(self.FileValue)
"""
example = File.New.NewMenuItem()
example.show_vals()
"""
Q 3 - Why do I get this error with this line?
inspect.getmro(example)
AttributeError: 'ManageProduct' object has no attribute '__bases__'
"""
I'm trying to understand what is happening line by line, but what I don't get is why NewMenuItem doesn't inherit from File.
I tried hard-coding the instantiation of File,like so:
File.init()
but then I get an error unless I pass the File object:
File.__init__(File())
I guess what I am struggling with is:
-inheritance trees
-super classes
-why we need to hard-code instantiations in this case
Keep in mind that this is the code I have come across. I am not sure why this is the way it is.

Inheritance and scope are two completely different things. NewMenuItem is defined inside the scope of the class New, inside of the scope of the class File, but it inherits from Menu, which inherits from object. So while NewMenuItem will only be accessible through the class File and again through New, it will inherit its methods from Menu, and super will refer to Menu.

Related

Using attributes from the derived class in the base class

In the project I'm working on, we have a hierarchy of classes with each class defining a get_text() method.
class Base:
def get_text(self):
raise NotImplementedError
class Derived1(Base):
def get_text(self):
return "Text from Derived1"
class Derived2(Base):
def get_text(self):
return "Text from Derived2"
obj1 = Derived1()
print(obj1.get_text())
==> 'Text from Derived1'
obj2 = Derived2()
print(obj2.get_text())
==> 'Text from Derived2'
This way, the programmer can call obj.get_text() and get the text from the class obj is pointing to.
Now I want to refactor the method to be just an attribute (called TEXT). I want to keep the original methods for backward compatibility, though. Is there a way to do it in the base class only?
class Base:
def get_text(self):
"""
Keep backward compatibility.
"""
return TEXT # What should be here?
class Derived1(Base):
TEXT = "Text from Derived1"
class Derived2(Base):
TEXT = "Text from Derived2"
obj1 = Derived1()
print(obj1.TEXT)
# Non-refactored code
obj2 = Derived2()
print(obj2.get_text())
==> NameError: name 'TEXT' is not defined
Coming from C++, I'm used to having a pointer to the base class invoke a method from the derived class using C++ virtual method dispatching. Is something similar possible in Python?
To answer my own question (thanks to the commenters!) both following ways work:
return self.__class__.TEXT
Here, self.__class__ points either to Derived1 or to Derived2 class objects, which have access to TEXT.
return self.TEXT
makes this one step shorter, as the attribute resolution algorithm accesses class attributes automatically.

Python inner container class

from itertools import count
class myobject(object):
id=count(0)
def __init__(self):
self.ID=next(self.id)
self.dat=[]
class bucket(object):
def __init__(self):
self.container_object=self.Container_object
class Container_object(object):
self.container={}
self.contained_objects=self.container.keys()
def create_object(myobject):
self.container[myobject.ID]=object_data
I am looking to create a container object within class bucket such that I can create different instances of myobject within class bucket.
Such that when create_object creates new object that can be accessed and contained within class bucket.Container_object and bucket.Container_object.ID would be an instance of myobject
PS I am new to classes and still try to understand how I can use them, so most likely the question shows this understanding. Please feel free to point to those misunderstandings. thanks
I think this is what you are looking for.
A class Bucket which contains multiple instances of class MyObject. Since you don't specify any particular peculiarities of your Container object, I will use a simple dictionary.
from itertools import count
class MyObject(object):
id=count(0)
def __init__(self):
self.ID=next(self.id)
self.dat=[]
class Bucket(object):
def __init__(self):
self.contained_objects={}
def add_object(myobject):
self.contained_objects[myobject.ID]=myobject.dat
If you want to wrap the collection inside some other object for some reasons, you simply define the WrapperContainer class outside the Bucket class and simply instantiate an instance of WrapperContainer inside it.
from itertools import count
class MyObject(object):
id=count(0)
def __init__(self):
self.ID=next(self.id)
self.dat=[]
class WrapperContainer(object):
def __init__(self):
self.collection = {}
def add_object(my_object):
self.collection[myobject.ID]=myobject.dat
def do_whatever_you_want():
pass
class Bucket(object):
def __init__(self):
self.container_object= WrapperContainer() #a specific instance
def add_object(myobject):
self.container_object.add_object(my_object)
Finally, you can think at your container class as a subclass of an already existent collection (list or dictionary or anything else) to inherit its features.

how to solve attribute error when all attributes do exist?

I am writing a program for my A level course in python and i need to access an attribute from one class in to another using inheritance. here is an example of what I am trying to do.
class class1():
def __init__(self):
self.testValue = 'hello'
class class2(class1):
def __init__(self):
self.inheritedValue = class1.testValue
print(self.inheritedValue)
object = class2()
when running this code i get the following attribute error.
AttributeError: type object 'class1' has no attribute 'testValue'
anyone got a solution for this??
First a comment to code style: class names are written in CamelCase, so name them Class1 and Class2.
Secondly, your class Class1 doesn't have the said attribute, but each instance does.
So your class2 should look like
class Class2(Class1):
def __init__(self):
super().__init__() # now we have everything Class1 provides us with
self.inheritedValue = self.testValue
print(self.inheritedValue)
because each object of Class2 is also an object of Class1
The attribute does not exist within the scope of class2 the way you've implemented it. By passing it in the class definition, it is inherited but the attribute doesn't exist yet. That is, unless you've called the constructor. Two ways of doing this, by either using the super built-in function(not recommended in real life, see here, it's a nice read. Anyway, here are a few solutions:
class class1():
def __init__(self):
self.testValue = 'hello'
class class2(class1):
def __init__(self):
class1.__init__(self)
print(self.testValue)
obj = class2()
if you do not want to call the constructor of the class you are inheriting, you could do something like this:
class class1():
testValue = 'hello'
def __init__(self):
pass
class class2(class1):
def __init__(self):
self.inheritedValue = class1.testValue
print(self.inheritedValue)
obj = class2()
Side note, object is a built-in so you shouldn't use it.

Inheritance in python 2.7

I am attempting to build a framework for parsing a very specific text structure.
The structure that I am processing is rich and has a known schema, similar to xml.
I am attempting to build an framework to do the parsing. The text has various sections and I anticipate that more sections code be added in the future. To Compensate, I am attempting to build a series of derived classed that can be swapped in or out as needed.
I thought everything was going as planned, until I started coding up the first derived class.
The base class has some functionality inside of __init__ that I was expecting that I would get for free in all of the concrete derived classes. That however, doesn't seem to be the case at all.
Here is a simple example to illustrate my problem:
I would expect the output to be:
['memberA', 'memberB', 'memberC'],
['DerivedA', 'DerivedB', 'DerivedC']
class base(object):
def __init__(self):
members = [attr for attr in dir(self) if not callable(attr) and not attr.startswith("__")]
print members
class test(base):
def __init__(self):
self.memberA = None
self.memberB = None
self.memberC = None
class test2(test):
def __init__(self):
self.DerivedA = None
self.DerivedB = None
self.DerivedC = None
t = test()
t2 = test2()
Can someone please explain to my, why the print functionality is not working as I expect it?
EDIT:
in light of the answer below: I now have this question:
What if base.__init(self) instead looked like:
class base(object):
def __init__(self, text):
would I have to define the derived class as:
class test(base):
def __init__(self, text):
base.__init__(self, text)
I was hoping to at least get the parameter object referance for free
In Python, you must call the base class's __init__ explicitly inside test.__init__:
class test(base):
def __init__(self):
base.__init__(self)
Or, if you wish to support multiple inheritance, use super:
class test(base):
def __init__(self):
super(test, self).__init__()
If the base.__init__ looks like
class base(object):
def __init__(self, text):
then test.__init__ should indeed look like
class test(base):
def __init__(self, text):
base.__init__(self, text)
See Guido van Rossum's blog for why self is explicit in Python.
PS. PEP8 recommends using CapWords for class names.
you are overwriting init in test2
following code will complete overwrite init in test. so there is no longer a print int the init function.
def __init__(self):
self.DerivedA = None
self.DerivedB = None
self.DerivedC = None

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