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.
Related
I'm trying to write a tracker class where the instances of the tracker class track the sub-classes of another class that are in the scope of the tracker instance.
More concretely, the following is an example of what I am trying to achieve:
class Foo(object): pass
class FooTracker(object):
def __init__(self):
# use Foo.__subclasses__() or a metaclass to track subclasses
# - but how do I filter this to only get the ones in scope?
self.inscope = <something magic goes here>
ft1 = FooTracker()
assert ft1.inscope == []
class Bar(Foo): pass
ft2 = FooTracker()
assert ft2.inscope == [<class '__main__.Bar'>]
def afunction():
class Baz(Foo): pass # the global definition of Bar is now hidden
class Bar(Foo): pass
ft3 = FooTracker()
assert (set(ft3.inscope) == set([<class '__main__.afunction.<locals>.Baz'>,
<class '__main__.afunction.<locals>.Bar'>])
ft4 = FooTracker() # afunction.Baz and afunction.Bar are no longer in scope
assert ft4.inscope == [<class '__main__.Bar'>]
So I want the instances of FooTracker to track the sub-classes of Foo that were in scope at the time the FooTracker object was created.
I've tried a few different things, such as parsing the qualified names of the Foo sub-classes and using exec() to do the name resolution but the fundamental problem is that it always works out the sub-classes relative to the scope within FooTracker.__init__() and not where it was called.
My only other thought was to try something with inspect.currentframe() but even if this were possible it would probably be too much of a hack and would make the code too brittle (e.g., there is a comment in the docs that not all Python implementations will have frame support in the interpreter").
There's no easy way to do exactly what you're asking for. But you might be able to use some Python features to get something with a roughly similar API, without as much hassle.
One option would be to require each subclass to be decorated with a method of your Tracker class. This would make it really easy to keep track of them, since you'd just append each caller of the method to a list:
class Tracker:
def __init__(self):
self.subclasses = []
def register(self, cls):
self.subclasses.append(cls)
return cls
class Foo(): pass
foo_tracker = Tracker()
#foo_tracker.register
class FooSubclass1(Foo): pass
#foo_tracker.register
class FooSubclass2(Foo): pass
print(foo_tracker.subclasses)
This doesn't actually require that the classes being tracked are subclasses of Foo, all classes (and even non-class objects) can be tracked if you pass them to the register method. Decorator syntax makes it a little nicer than just appending each class to a list after you define it, but not by a whole lot (you still repeat yourself a fair amount, which may be annoying unless you make the tracker and method names very short).
A slightly trickier version might get passed the base class, so that it would detect subclasses automatically (via Foo.__subclasses__). To limit the subclasses it detects (rather than getting all subclasses of the base that have ever existed), you could make it behave as a context manager, and only track new subclasses defined within a with block:
class Tracker:
def __init__(self, base):
self.base = base
self._exclude = set()
self.subclasses = set()
def __enter__(self):
self._exclude = set(self.base.__subclasses__())
return self
def __exit__(self, *args):
self.subclasses = set(self.base.__subclasses__()) - self._exclude
return False
class Foo(): pass
class UntrackedSubclass1(Foo): pass
with Tracker(Foo) as foo_tracker:
class TrackedSubclass1(Foo): pass
class TrackedSubclass2(Foo): pass
class UntrackedSubclass2(Foo): pass
print(foo_tracker.subclasses)
If you're using Python 3.6 or later, you can do the tracking a different way by injecting an __init_subclass__ class method into the tracked base class, rather than relying upon __subclasses__. If you don't need to support class hierarchies that are already using __init_subclass__ for their own purposes (and you don't need to support nested trackers), it can be quite elegant:
class Tracker:
def __init__(self, base):
self.base = base
self.subclasses = []
def __enter__(self):
#classmethod
def __init_subclass__(cls, **kwargs):
self.subclasses.append(cls)
self.base.__init_subclass__ = __init_subclass__
return self
def __exit__(self, *args):
del self.base.__init_subclass__
return False
class Foo(): pass
class UntrackedSubclass1(Foo): pass
with Tracker(Foo) as foo_tracker:
class TrackedSubclass1(Foo): pass
class TrackedSubclass2(Foo): pass
class UntrackedSubclass2(Foo): pass
print(foo_tracker.subclasses)
One nice feature of this version is that it automatically tracks deeper inheritance hierarchies. If a subclass of a subclass is created within the with block, that "grandchild" class will still be tracked. We could make the previous __subclasses__ based version work this way too, if you wanted, by adding another function to recursively expand out the subclasses of each class we find.
If you do want to play nice with existing __init_subclass__ methods, or want to be able to nest trackers, you need to make the code a bit more complicated. Injecting a well behaved classmethod in a reversible way is tricky since you need handle both the case where the base class has its own method, and the case where it's inheriting a version from its parents.
class Tracker:
def __init__(self, base):
self.base = base
self.subclasses = []
def __enter__(self):
if '__init_subclass__' in self.base.__dict__:
self.old_init_subclass = self.base.__dict__['__init_subclass__']
else:
self.old_init_subclass = None
#classmethod
def __init_subclass__(cls, **kwargs):
if self.old_init_subclass is not None:
self.old_init_subclass.__get__(None, cls)(**kwargs)
else:
super(self.base, cls).__init_subclass__(**kwargs)
self.subclasses.append(cls)
self.base.__init_subclass__ = __init_subclass__
return self
def __exit__(self, *args):
if self.old_init_subclass is not None:
self.base.__init_subclass__ = self.old_init_subclass
else:
del self.base.__init_subclass__
return False
class Foo:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
print("Foo!")
class Bar(Foo): pass # every class definition from here on prints "Foo!" when it runs
with Tracker(Bar) as tracker1:
class Baz(Bar): pass
with Tracker(Foo) as tracker2:
class Quux(Foo): pass
with Tracker(Bar) as tracker3:
class Plop(Bar): pass
# four Foo! lines will have be printed by now by Foo.__init_subclass__
print(tracker1.subclasses) # will describe Baz and Plop, but not Quux
print(tracker2.subclasses) # will describe Quux and Plop
print(tracker3.subclasses) # will describe only Plop
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.
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.
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
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.