Parent class has a property called 'deserialize' that is static and abstract with one argument. Each Child class implemented that method. Now I have a situation that Child class needs more than one argument. When I add options=None to Parent class, children classes complain that they have a different signature(warning). I have to add options=None to each class. That is a refactoring. I want to know if I can omit the warning and continue, or there is a better solution? Or do I have to refactor?
class Serializable:
__metaclass__ = ABCMeta
#staticmethod
#abstractmethod
def deserialize(json_obj, options=None):
pass
class ChildWithNoExtraArguments(Serializable):
# warning is here...
#staticmethod
def deserialize(json_obj):
# some implementation
class ChildWithExtraArgumnets(Serializable):
#staticmethod
def deserialize(json_obj, options):
# some implementation, I need options
You need to decorate your child classes deserialize implementation with #staticmethod too. The exception you're seeing is because python is automatically adding self to each of the method calls. Decorating then with #staticmethod stops this behavior.
Additionally, you're second implementation needs to define options as a keyword argument. Keyword arguments have default values, for instance: options=None.
class Serializable:
__metaclass__ = ABCMeta
#staticmethod
#abstractmethod
def deserialize(json_obj, options=None):
pass
class ChildWithNoExtraArguments(Serializable):
# warning is here...
#staticmethod
def deserialize(json_obj, options=None):
# some implementation
class ChildWithExtraArgumnets(Serializable):
#staticmethod
def deserialize(json_obj, options=None):
# some implementation, I need options
Related
I'm using __init_subclass__ in order to register subclass in a specific registry. The registry itself is another class that contain a dictionary to store those subclass. I wanted to create a decorator to link those two classes but I'm not sure it is possible as __init_subclass__ is called before the decorator itself.
Here is a simple example that describe what I would like but that is not working for obvious reason:
class Registry:
#classmethod
def link(cls, other_class):
other_class._regsitry = cls()
return other_class
class Foo:
_registry = None
def __init_subclass__(cls):
if cls._registry = None:
raise ...
# Add the class to the registry
....
#Registry.link
class Bar(Foo):
pass
The thing here is that Bar will raise the exception as it does not have _registry. I understand why but I don't know if there is any possibility to do that with decorator ?
I am familiar with OOP, and understand we can inherit from a base class and extend user_call_api in a child class adding more definitions to it. But I'm wondering is there a way that in parent class, we could find out
which methods are overridden (by child classes)
the name of (child) classes that have overridden the method
class Parent:
def call_api(self):
print("API is called")
def use_call_api(self):
# if it's overridden, find out in parent,
# do something and then
self.call_api()
# if it's not overridden
self.call_api()
class Child(Parent):
def call_api(self):
print("call_api")
class Child2(Parent):
def call_api(self):
print("call_api2")
class Child3(Parent):
def call_api(self):
print("call_ap3")
def use_call_api(self):
print("custom call_api")
You can have a metaclass that will override the __new__ dunder-method and hold the necessary information (method name and class names that overrides it) into the singleton property of it.
import re
class Inspect(type):
implementations = {}
def __new__(mcs, cls, args, kwargs):
for attr in kwargs.keys():
if not re.match(r"^__\w+__$", attr):
mcs.implementations[attr] = (*mcs.implementations.get(attr, ()), cls)
return type(cls, args, kwargs)
The classes (primarily the child classes inherited from Parent) should use Inspect metaclass.
The Inspect.implementations will be in their final state after the application starts and all classes and functions are declared in dynamic memory to be ready to execute the script. So you can get declare an additional method in the Parent to get the list of classes that override the current method or even was the method overridden or not.
import inspect
class Parent:
#staticmethod
def overridden() -> tuple:
return Inspect.implementations.get(inspect.stack()[1].function, ())
def call_api(self):
print("API is called")
def use_call_api(self):
# if it's overridden, find out in parent,
if self.overridden():
print("use_call_api has been overridden in", self.overridden())
# do something and then
self.call_api()
# if it's not overridden
self.call_api()
class Child(Parent, metaclass=Inspect):
def call_api(self):
print("call_api")
def use_call_api(self):
pass
if __name__ == "__main__":
p = Parent()
p.use_call_api()
If you run the above code, then you will see that when Child overrides use_call_api method, then the overridden method called by the same method of the Parent will contain the Child, indicating that it overrides the method. If we do not implement use_call_api for Child, the overridden would return an empty tuple, and if self.overridden() condition would not work and will pass the case.
I have an abstract base class Base that provides an abstract method _run() that needs to be implemented by derived classes, as well as a method run() that will call _run() and do some extra work that is common to all derived classes.
In all derived classes, I am setting the function docstring for the _run() method. As this function is not part of the public API, I want the same docstring (and function signature) to instead show up for the run() method.
Consider the following example:
import inspect
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def _run(self):
return
def run(self, *args, **kwargs):
"""old_doc"""
return self._run(*args, **kwargs)
class Derived(Base):
def _run(self):
"""new_doc"""
return
My initial idea was to manipulate the docstring in Base.__init__ or Base.__new__. This works to some extent, but presents a number of problems:
I want to be able to override these two methods (at the very least __init__) in derived classes.
This requires the class to be instantiated before the docstring is available.
By setting the docstring for Base.run when instantiating the derived class, it would in fact set the docstring for all derived classes.
class Base(ABC):
def __init__(self):
type(self).run.__doc__ = type(self)._run.__doc__
type(self).run.__signature__ = inspect.signature(type(self)._run)
...
What I am hoping for:
>>> Derived.run.__doc__
'new_doc'
What I get so far:
>>> Derived.run.__doc__
'old_doc'
>>> Derived().run.__doc__
'new_doc'
Are there any solutions to this?
Don't modify the docstring of Base.run; instead, document what it does: it invokes a subclass-defined method.
class Base(ABC):
#abstractmethod
def _run(self):
"Must be replaced with actual code"
return
def run(self, *args, **kwargs):
"""Does some setup and runs self._run"""
return self._run(*args, **kwargs)
class Derived(Base):
def _run(self):
"""Does some work"""
return
There is no need to generate a new docstring for Derived.run, because Derived.run and Base.run evaluate to the exact same object: the run method defined by Base. Inheritance doesn't change what Base.run does just because it is invoked from an instance of Derived rather than an instance of Base.
The best workaround I have come up with is to create a decorator instead:
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def run(self, *args, **kwargs):
"""old_doc"""
return self._run(*args, **kwargs)
def extra_work(func):
# Do some extra work and modify func.__doc__
...
return func
class Derived(Base):
#extra_work
def run(self):
"""new_doc"""
return
This way the extra work can still be defined outside the derived class to avoid duplicating it in every class derived from Base, and I am able to automatically update the docstring to reflect the added functionality.
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
class Email():
def __init__(self, store_number):
self.store_number = store_number
def amethod(self):
pass
What is the correct way to pass variables from a sub-class to a parent-class?
should I do:
class MoreSpecificEmail():
def __init__(self, store_number):
Email.__init__(self, store_number=store_number)
def another_method(self):
pass
or:
class MoreSpecificEmail():
def __init__(self, store_number):
self.store_number = store_number
Email.__init__(self, store_number=self.store_number)
I have just been using different abbreviations of store_number in each sub-class to help clarify what's going on in my head. I am sure that is the wrong way, though.
What you currently have isn't inheritance; neither of your classes actually inherits from anything! Firstly, Email should be a "new-style class", inheriting from object:
class Email(object):
# ^ note inheritance from object
def __init__(self, store_number):
self.store_number = store_number
def amethod(self):
pass
Then MoreSpecificEmail should inherit from Email - as it doesn't have any additional instantiation parameters, it can just use the inherited __init__ and doesn't need to define its own:
class MoreSpecificEmail(Email):
# ^ note inheritance from Email
# note no need to define __init__
def another_method(self):
pass
For an example where there are additional __init__ parameters, note that you should use super and rely on the superclass's __init__ to assign the parameters it takes - you only need to assign the attributes that don't get handled by the superclass:
class MoreSpecificEmail(Email):
def __init__(self, store_number, something_else):
super(MoreSpecificEmail, self).__init__(store_number)
# ^ pass it straight on
self.something_else = something_else
def another_method(self):
pass
For more information, see the Python class tutorial.