Recommended way of subclassing a class while maintaining backwards compatibility - python

Here is the scenario. I have a class Foo that looks like this:
class Foo:
def __init__(self, prop):
self.prop = prop
def method(self):
if self.prop == PROP_VAL_1:
do_something()
elif self.prop == PROP_VAL_2:
do_something_2()
else:
raise ValueError(f"Invalid value {self.prop}")
I want to do a refactor in which I end up with two subclasses of Foo like this:
class FooA(Foo):
def method():
do_something()
class FooB(Foo):
def method():
do_something_2()
However, the issue is that I can't change how Foo is used because there is code currently in use that uses Foo. Is it possible to somehow have the superclass Foo instantiate objects of type FooA and FooB depending on the value of prop? Another thing I want to avoid is having Foo import it's subclasses since this would lead to circular dependencies. Is there some sort of "best practice" for this as well?

Rename the Foo class, then replace it with a factory function.
class NewFoo:
def __init__(self, prop):
self.prop = prop
class FooA(NewFoo):
def method():
do_something()
class FooB(NewFoo):
def method():
do_something_2()
def Foo(prop):
if prop == PROP_VAL_1:
return FooA(prop)
elif prop == PROP_VAL_2:
return FooB(prop)

By definition, you want the class Foo to return an instance that is not Foo, but a subclass of Foo.
I would caution against this, as it may be surprising to the users of your class.
You have a few options:
You can use a factory function, as #Barmar suggests but use a name such as foo_factory and keep the method Foo.method intact with some aggressive logging. This allows you to incrementally change all call sites from Foo to foo_factory and have the logging as peace of mind that no one is directly instantiating Foo anymore.
Another option is to modify Foo.__new__ to return an instance of a subclass:
I modified your code a bit to an MRE.
PROP_VAL_1 = 1
PROP_VAL_2 = 2
class Foo:
def __new__(cls, prop):
# if the class was instantiated directly
if cls is Foo:
if prop == PROP_VAL_1:
return FooA(prop)
elif prop == PROP_VAL_2:
return FooB(prop)
else:
raise ValueError(f"Invalid value {prop}")
# this is only reached if a subclass is instantiated
return super().__new__(cls)
def __init__(self, prop):
self.prop = prop
class FooA(Foo):
def method(self):
return 'do_something'
class FooB(Foo):
def method(self):
return 'do_something_2'
foo = Foo(prop=2)
print(isinstance(foo, Foo)) # True
print(type(foo)) # <class '__main__.FooB'>
print(foo.method()) # do_something_2

Related

How to implement __getattr__() for class members (not instance members) [duplicate]

I have a class like:
class MyClass:
Foo = 1
Bar = 2
Whenever MyClass.Foo or MyClass.Bar is invoked, I need a custom method to be invoked before the value is returned. Is it possible in Python? I know it is possible if I create an instance of the class and I can define my own __getattr__ method. But my scnenario involves using this class as such without creating any instance of it.
Also I need a custom __str__ method to be invoked when str(MyClass.Foo) is invoked. Does Python provide such an option?
__getattr__() and __str__() for an object are found on its class, so if you want to customize those things for a class, you need the class-of-a-class. A metaclass.
class FooType(type):
def _foo_func(cls):
return 'foo!'
def _bar_func(cls):
return 'bar!'
def __getattr__(cls, key):
if key == 'Foo':
return cls._foo_func()
elif key == 'Bar':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return 'custom str for %s' % (cls.__name__,)
class MyClass:
__metaclass__ = FooType
# # in python 3:
# class MyClass(metaclass=FooType):
# pass
print(MyClass.Foo)
print(MyClass.Bar)
print(str(MyClass))
printing:
foo!
bar!
custom str for MyClass
And no, an object can't intercept a request for a stringifying one of its attributes. The object returned for the attribute must define its own __str__() behavior.
(I know this is an old question, but since all the other answers use a metaclass...)
You can use the following simple classproperty descriptor:
class classproperty(object):
""" #classmethod+#property """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Use it like:
class MyClass(object):
#classproperty
def Foo(cls):
do_something()
return 1
#classproperty
def Bar(cls):
do_something_else()
return 2
For the first, you'll need to create a metaclass, and define __getattr__() on that.
class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
For the second, no. Calling str(MyClass.Foo) invokes MyClass.Foo.__str__(), so you'll need to return an appropriate type for MyClass.Foo.
Surprised no one pointed this one out:
class FooType(type):
#property
def Foo(cls):
return "foo!"
#property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Works:
>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'
(for Python 2.x, change definition of MyClass to:
class MyClass(object):
__metaclass__ = FooType
)
What the other answers say about str holds true for this solution: It must be implemented on the type actually returned.
Depending on the case I use this pattern
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Then you import and use it.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
This avoid use of metaclass, and handle some use cases.

is there an equivalent for__getattr__ to intercept accesses to a python class's static attributes? [duplicate]

I have a class like:
class MyClass:
Foo = 1
Bar = 2
Whenever MyClass.Foo or MyClass.Bar is invoked, I need a custom method to be invoked before the value is returned. Is it possible in Python? I know it is possible if I create an instance of the class and I can define my own __getattr__ method. But my scnenario involves using this class as such without creating any instance of it.
Also I need a custom __str__ method to be invoked when str(MyClass.Foo) is invoked. Does Python provide such an option?
__getattr__() and __str__() for an object are found on its class, so if you want to customize those things for a class, you need the class-of-a-class. A metaclass.
class FooType(type):
def _foo_func(cls):
return 'foo!'
def _bar_func(cls):
return 'bar!'
def __getattr__(cls, key):
if key == 'Foo':
return cls._foo_func()
elif key == 'Bar':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return 'custom str for %s' % (cls.__name__,)
class MyClass(metaclass=FooType):
pass
# # in python 2:
# class MyClass:
# __metaclass__ = FooType
print(MyClass.Foo)
print(MyClass.Bar)
print(str(MyClass))
printing:
foo!
bar!
custom str for MyClass
And no, an object can't intercept a request for a stringifying one of its attributes. The object returned for the attribute must define its own __str__() behavior.
Updated 2023-02-20 for Python 3.x default implementation (python 2 as a comment).
(I know this is an old question, but since all the other answers use a metaclass...)
You can use the following simple classproperty descriptor:
class classproperty(object):
""" #classmethod+#property """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Use it like:
class MyClass(object):
#classproperty
def Foo(cls):
do_something()
return 1
#classproperty
def Bar(cls):
do_something_else()
return 2
For the first, you'll need to create a metaclass, and define __getattr__() on that.
class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
For the second, no. Calling str(MyClass.Foo) invokes MyClass.Foo.__str__(), so you'll need to return an appropriate type for MyClass.Foo.
Surprised no one pointed this one out:
class FooType(type):
#property
def Foo(cls):
return "foo!"
#property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Works:
>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'
(for Python 2.x, change definition of MyClass to:
class MyClass(object):
__metaclass__ = FooType
)
What the other answers say about str holds true for this solution: It must be implemented on the type actually returned.
Depending on the case I use this pattern
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Then you import and use it.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
This avoid use of metaclass, and handle some use cases.

__getattr__ on Exception subclass [duplicate]

I have a class like:
class MyClass:
Foo = 1
Bar = 2
Whenever MyClass.Foo or MyClass.Bar is invoked, I need a custom method to be invoked before the value is returned. Is it possible in Python? I know it is possible if I create an instance of the class and I can define my own __getattr__ method. But my scnenario involves using this class as such without creating any instance of it.
Also I need a custom __str__ method to be invoked when str(MyClass.Foo) is invoked. Does Python provide such an option?
__getattr__() and __str__() for an object are found on its class, so if you want to customize those things for a class, you need the class-of-a-class. A metaclass.
class FooType(type):
def _foo_func(cls):
return 'foo!'
def _bar_func(cls):
return 'bar!'
def __getattr__(cls, key):
if key == 'Foo':
return cls._foo_func()
elif key == 'Bar':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return 'custom str for %s' % (cls.__name__,)
class MyClass(metaclass=FooType):
pass
# # in python 2:
# class MyClass:
# __metaclass__ = FooType
print(MyClass.Foo)
print(MyClass.Bar)
print(str(MyClass))
printing:
foo!
bar!
custom str for MyClass
And no, an object can't intercept a request for a stringifying one of its attributes. The object returned for the attribute must define its own __str__() behavior.
Updated 2023-02-20 for Python 3.x default implementation (python 2 as a comment).
(I know this is an old question, but since all the other answers use a metaclass...)
You can use the following simple classproperty descriptor:
class classproperty(object):
""" #classmethod+#property """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Use it like:
class MyClass(object):
#classproperty
def Foo(cls):
do_something()
return 1
#classproperty
def Bar(cls):
do_something_else()
return 2
For the first, you'll need to create a metaclass, and define __getattr__() on that.
class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
For the second, no. Calling str(MyClass.Foo) invokes MyClass.Foo.__str__(), so you'll need to return an appropriate type for MyClass.Foo.
Surprised no one pointed this one out:
class FooType(type):
#property
def Foo(cls):
return "foo!"
#property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Works:
>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'
(for Python 2.x, change definition of MyClass to:
class MyClass(object):
__metaclass__ = FooType
)
What the other answers say about str holds true for this solution: It must be implemented on the type actually returned.
Depending on the case I use this pattern
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Then you import and use it.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
This avoid use of metaclass, and handle some use cases.

Is there a way to make code run when a class is derived from a particular parent class in Python?

For example, if I create the class Foo, then later derive the subclass Bar, I want the myCode() method of Foo to run.
class Foo(object):
x = 0
def __init__(self):
pass
def myCode(self):
if(self.x == 0):
raise Exception("nope")
class Bar(Foo): #This is where I want myCode() to execute
def baz(self):
pass
This should happen any time a class is derived from the base class Foo. Is it possible to do this in Python? I'm using Python 3 if it matters.
Note: In my real code, Foo is actually an abstract base class.
Edit: I also need access to derived class member data and methods in myCode().
Use a metaclass:
class MetaClass:
def __init__(cls, name, bases, dictionary):
if name is not 'Parent':
print('Subclass created with name: %s' % name)
super().__init__(name, bases, dictionary)
class Parent(metaclass=MetaClass):
pass
class Subclass(Parent):
pass
Output:
Subclass created with name: Subclass
Metaclasses control how classes themselves are created. Subclass inherits its metaclass from Parent, and thus that code gets run when it is defined.
Edit: As for your use case with an abstract base class, off the top of my head I think you'd just need to define your metaclass as a subclass of ABCMeta, but I didn't test that.
May this code can help you:
class Foo:
def myCode(self):
print('myCode within Foo')
def __init__(self):
if type(self) != Foo:
self.myCode()
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
def baz(self):
pass
Test:
>>>
>>> f = Foo()
>>> b = Bar()
myCode within Foo
>>>
This works:
class MyMeta(type):
def __new__(cls, name, parents, dct):
if name is not 'Foo':
if 'x' not in dct:
raise Exception("Nein!")
elif 'x' in dct and dct['x'] == 0:
raise Exception("Nope!")
return super(MyMeta, cls).__new__(cls, name, parents, dct)
Output:
class Bar(Foo):
x = 0
> Exception: Nope!
This is my specific use case if anyone wants to comment on whether or not this is appropriate:
class MagmaMeta(type):
def __new__(cls, name, parents, dct):
# Check that Magma instances are valid.
if name is not 'Magma':
if 'CAYLEY_TABLE' not in dct:
raise Exception("Cannot create Magma instance without CAYLEY_TABLE")
else:
# Check for square CAYLEY_TABLE
for row in CAYLEY_TABLE:
if not len(row) == len(dct['CAYLEY_TABLE']):
raise Exception("CAYLEY_TABLE must be a square array")
# Create SET and ORDER from CAYLEY_TABLE
dct['SET'] = set([])
for rows in CAYLEY_TABLE:
for x in rows:
dct['SET'].add(x)
dct['ORDER'] = len(dct['SET'])
return super(MyMeta, cls).__new__(cls, name, parents, dct)

How to detect method overloading in subclasses in python?

I have a class that is a super-class to many other classes. I would like to know (in the __init__() of my super-class) if the subclass has overridden a specific method.
I tried to accomplish this with a class method, but the results were wrong:
class Super:
def __init__(self):
if self.method == Super.method:
print 'same'
else:
print 'different'
#classmethod
def method(cls):
pass
class Sub1(Super):
def method(self):
print 'hi'
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
>>> same
>>> different
>>> different
Is there any way for a super-class to know if a sub-class has overridden a method?
It seems simplest and sufficient to do this by comparing the common subset of the dictionaries of an instance and the base class itself, e.g.:
def detect_overridden(cls, obj):
common = cls.__dict__.keys() & obj.__class__.__dict__.keys()
diff = [m for m in common if cls.__dict__[m] != obj.__class__.__dict__[m]]
print(diff)
def f1(self):
pass
class Foo:
def __init__(self):
detect_overridden(Foo, self)
def method1(self):
print("Hello foo")
method2=f1
class Bar(Foo):
def method1(self):
print("Hello bar")
method2=f1 # This is pointless but not an override
# def method2(self):
# pass
b=Bar()
f=Foo()
Runs and gives:
['method1']
[]
If you want to check for an overridden instance method in Python 3, you can do this using the type of self:
class Base:
def __init__(self):
if type(self).method == Base.method:
print('same')
else:
print('different')
def method(self):
print('Hello from Base')
class Sub1(Base):
def method(self):
print('Hello from Sub1')
class Sub2(Base):
pass
Now Base() and Sub2() should both print "same" while Sub1() prints "different". The classmethod decorator causes the first parameter to be bound to the type of self, and since the type of a subclass is by definition different to its base class, the two class methods will compare as not equal. By making the method an instance method and using the type of self, you're comparing a plain function against another plain function, and assuming functions (or unbound methods in this case if you're using Python 2) compare equal to themselves (which they do in the C Python implementation), the desired behavior will be produced.
You can use your own decorator. But this is a trick and will only work on classes where you control the implementation.
def override(method):
method.is_overridden = True
return method
class Super:
def __init__(self):
if hasattr(self.method, 'is_overridden'):
print 'different'
else:
print 'same'
#classmethod
def method(cls):
pass
class Sub1(Super):
#override
def method(self):
print 'hi'
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
>>> same
>>> different
>>> same
In reply to answer https://stackoverflow.com/a/9437273/1258307, since I don't have enough credits yet to comment on it, it will not work under python 3 unless you replace im_func with __func__ and will also not work in python 3.4(and most likely onward) since functions no longer have the __func__ attribute, only bound methods.
EDIT: Here's the solution to the original question(which worked on 2.7 and 3.4, and I assume all other version in between):
class Super:
def __init__(self):
if self.method.__code__ is Super.method.__code__:
print('same')
else:
print('different')
#classmethod
def method(cls):
pass
class Sub1(Super):
def method(self):
print('hi')
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
And here's the output:
same
different
same
You can compare whatever is in the class's __dict__ with the function inside the method
you can retrieve from the object -
the "detect_overriden" functionbellow does that - the trick is to pass
the "parent class" for its name, just as one does in a call to "super" -
else it is not easy to retrieve attributes from the parentclass itself
instead of those of the subclass:
# -*- coding: utf-8 -*-
from types import FunctionType
def detect_overriden(cls, obj):
res = []
for key, value in cls.__dict__.items():
if isinstance(value, classmethod):
value = getattr(cls, key).im_func
if isinstance(value, (FunctionType, classmethod)):
meth = getattr(obj, key)
if not meth.im_func is value:
res.append(key)
return res
# Test and example
class A(object):
def __init__(self):
print detect_overriden(A, self)
def a(self): pass
#classmethod
def b(self): pass
def c(self): pass
class B(A):
def a(self): pass
##classmethod
def b(self): pass
edit changed code to work fine with classmethods as well:
if it detects a classmethod on the parent class, extracts the underlying function before proceeding.
--
Another way of doing this, without having to hard code the class name, would be to follow the instance's class ( self.__class__) method resolution order (given by the __mro__ attribute) and search for duplicates of the methods and attributes defined in each class along the inheritance chain.
I'm using the following method to determine if a given bound method is overridden or originates from the parent class
class A():
def bla(self):
print("Original")
class B(A):
def bla(self):
print("Overridden")
class C(A):
pass
def isOverriddenFunc(func):
obj = func.__self__
prntM = getattr(super(type(obj), obj), func.__name__)
return func.__func__ != prntM.__func__
b = B()
c = C()
b.bla()
c.bla()
print(isOverriddenFunc(b.bla))
print(isOverriddenFunc(c.bla))
Result:
Overridden
Original
True
False
Of course, for this to work, the method must be defined in the base class.
You can also check if something is overridden from its parents, without knowing any of the classes involved using super:
class A:
def fuzz(self):
pass
class B(A):
def fuzz(self):
super().fuzz()
class C(A):
pass
>>> b = B(); c = C()
>>> b.__class__.fuzz is super(b.__class__, b).fuzz.__func__
False
>>> c.__class__.fuzz is super(c.__class__, c).fuzz.__func__
True
See this question for some more nuggets of information.
A general function:
def overrides(instance, function_name):
return getattr(instance.__class__, function_name) is not getattr(super(instance.__class__, instance), function_name).__func__
>>> overrides(b, "fuzz")
True
>>> overrides(c, "fuzz")
False
You can check to see if the function has been overridden by seeing if the function handle points to the Super class function or not. The function handler in the subclass object points either to the Super class function or to an overridden function in the Subclass. For example:
class Test:
def myfunc1(self):
pass
def myfunc2(self):
pass
class TestSub(Test):
def myfunc1(self):
print('Hello World')
>>> test = TestSub()
>>> test.myfunc1.__func__ is Test.myfunc1
False
>>> test.myfunc2.__func__ is Test.myfunc2
True
If the function handle does not point to the function in the Super class, then it has been overridden.
Not sure if this is what you're looking for but it helped me when I was looking for a similar solution.
class A:
def fuzz(self):
pass
class B(A):
def fuzz(self):
super().fuzz()
assert 'super' in B.__dict__['fuzz'].__code__.co_names
The top-trending answer and several others use some form of Sub.method == Base.method. However, this comparison can return a false negative if Sub and Base do not share the same import syntax. For example, see discussion here explaining a scenario where issubclass(Sub, Base) -> False.
This subtlety is not apparent when running many of the minimal examples here, but can show up in a more complex code base. The more reliable approach is to compare the method defined in the Sub.__bases__ entry corresponding to Base because __bases__ is guaranteed to use the same import path as Sub
import inspect
def method_overridden(cls, base, method):
"""Determine if class overriddes the implementation of specific base class method
:param type cls: Subclass inheriting (and potentially overriding) the method
:param type base: Base class where the method is inherited from
:param str method: Name of the inherited method
:return bool: Whether ``cls.method != base.method`` regardless of import
syntax used to create the two classes
:raises NameError: If ``base`` is not in the MRO of ``cls``
:raises AttributeError: If ``base.method`` is undefined
"""
# Figure out which base class from the MRO to compare against
base_cls = None
for parent in inspect.getmro(cls):
if parent.__name__ == base.__name__:
base_cls = parent
break
if base_cls is None:
raise NameError(f'{base.__name__} is not in the MRO for {cls}')
# Compare the method implementations
return getattr(cls, method) != getattr(base_cls, method)

Categories