Get mangled attribute value of a parent class outside of a class - python

Imagine a parent class which has a mangled attribute, and a child class:
class Foo:
def __init__(self):
self.__is_init = False
async def init(self):
# Some custom logic here, not important
self.__is_init = True
class Bar(Foo):
...
# Create class instance.
bar = Bar()
# How access `__is_init` of the parent class from the child instance?
How can I get a __is_init value from a parent (Foo) class?
Obviously, I can bar._Foo__is_init in this example, but the problem is that class name is dynamic and I need a general purpose solution that will work with any passed class name.

The solution I see now is iterating over parent classes, and building a mangled attribute name dynamically:
from contextlib import suppress
class MangledAttributeError(Exception):
...
def getattr_mangled(object_: object, name: str) -> str:
for cls_ in getattr(object_, "__mro__", None) or object_.__class__.__mro__:
with suppress(AttributeError):
return getattr(object_, f"_{cls_.__name__}{name}")
raise MangledAttributeError(f"{type(object_).__name__} object has no attribute '{name}'")
Checking that this works:
class Foo:
def __init__(self):
self.__is_init = False
async def init(self):
self.__is_init = True
class Bar(Foo):
def __init__(self):
super().__init__()
bar = Bar()
is_init = getattr_mangled(bar, "__is_init")
print(f"is_init: {is_init}") # Will print `False` which is a correct value in this example

class Foo:
def __init__(self):
self.__is_init = False
async def init(self):
self.__is_init = True
class Bar(Foo):
def getattr_mangled(self, attr:str):
for i in self.__dict__.keys():
if attr in i:
return getattr(self,i)
# return self.__dict__[i] #or like this
bar = Bar()
print(bar.getattr_mangled('__is_init')) #False
if there is a need in __init__ in Bar we should of course initiate Foo's init too by: super().__init__()
When Foo's init is run, self namespace already has attribute name we need in the form we need it (like_PARENT_CLASS_NAME__attrname).
And we can just get it from self namespace without even knowing what parent class name is.

Related

Recommended way of subclassing a class while maintaining backwards compatibility

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

Determine if subclass has a base class's method implemented in Python

I have a class that extends a base class. Upon instantiation, I want to check if the subclass has one of the classes implemented from its base, but I'm not sure the best way. hasattr(self, '[method]') returns the method from super if not implemented by the child, so I'm trying to tell the difference.
Here is an example:
class Base :
def __init__ ( self,) :
pass
def fail (self,) :
pass
# Now create the subclass w/o .fail
class Task ( Base ) :
def __init__ ( self, ):
print( hasattr( self, 'fail' ) ) # < returns True
When Task() is instantiated it prints True because Task inherits .fail from Base. But in this case, I want to know that Task Does Not implement .fail, so I want a False returned somehow. It seems like I'm looking for something like isimplemented( self, 'fail' ). What am I missing?
I'm not sure I understand correctly, but it sounds like you might be looking for Abstract Base Classes. (Documentation here, tutorial here.) If you specify an abstractmethod in a base class that inherits from abc.ABC, then attempting to instantiate a subclass will fail unless that subclass overrides the abstractmethod.
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def fail(self):
pass
class Task(Base):
pass
class Task2(Base):
def fail(self):
pass
# this raises an exception
# `fail` method has not been overridden in the subclass.
t1 = Task()
# this succeeds
# `fail` method has been overridden in the subclass.
t2 = Task2()
If you want a check to happen at class definition time rather than instance instantiation time, another option is to write an __init_subclass__ method in your base class, which is called every time you subclass your base class or you subclass a class inheriting from your base class. (You don't have to raise an exception in __init_subclass__ — you could just add a fail_overriden boolean attribute to the class, or do anything you like really.)
class Base:
def fail(self):
pass
def __init_subclass__(cls, **kwargs):
if cls.fail == Base.fail:
raise TypeError(
'Subclasses of `Base` must override the `fail` method'
)
super().__init_subclass__(**kwargs)
# this class definition raises an exception
# because `fail` has not been overridden
class Task(Base):
pass
# this class definition works fine.
class Task2(Base):
def fail(self):
pass
And if you just want each instance to tell you whether fail was overridden in their subclass, you can do this:
class Base:
def __init__(self):
print(type(self).fail != Base.fail)
def fail(self):
pass
class Task(Base):
def __init__(self):
super().__init__()
class Task2(Base):
def __init__(self):
super().__init__()
def fail(self):
pass
t1 = Task() # prints "True"
t2 = Task2() # prints "False"
IIUC, you can check super().fail == self.fail
class Base:
def __init__(self):
pass
def fail(self):
pass
class Task(Base):
def __init__(self):
print(super().fail == self.fail)
class Task2(Base):
def __init__(self):
print(super().fail == self.fail)
def fail(self):
# Override
pass
Output:
t1 = Task()
# True
t2 = Task2()
# False
Not sure if I understand correctly but you could check if fail method is in the vars of the classes, but not inherited to the main class.
So you could try:
class Base:
def __init__(self):
print(self.__dir__())
def fail(self):
pass
class Task(Base):
def __init__(self):
print('fail' not in vars(Task))
class Task2(Base):
def __init__(self):
print('fail' not in vars(Task2))
def fail(self):
# Override
pass
t1 = Task()
t2 = Task2()
Output:
True
False
Or use __dict__:
...
class Task(Base):
def __init__(self):
print('fail' not in Task.__dict__)
class Task2(Base):
def __init__(self):
print('fail' not in Task2.__dict__)
def fail(self):
# Override
pass
...

Can a class object know its "parent"? [duplicate]

In Python, is it possible to get the object, say Foo, that contains another object, Bar, from within Bar itself? Here is an example of what I mean
class Foo(object):
def __init__(self):
self.bar = Bar()
self.text = "Hello World"
class Bar(object):
def __init__(self):
self.newText = foo.text #This is what I want to do,
#access the properties of the container object
foo = Foo()
Is this possible? Thanks!
Pass a reference to the Bar object, like so:
class Foo(object):
def __init__(self):
self.text = "Hello World" # has to be created first, so Bar.__init__ can reference it
self.bar = Bar(self)
class Bar(object):
def __init__(self, parent):
self.parent = parent
self.newText = parent.text
foo = Foo()
Edit: as pointed out by #thomleo, this can cause problems with garbage collection. The suggested solution is laid out at http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/ and looks like
import weakref
class Foo(object):
def __init__(self):
self.text = "Hello World"
self.bar = Bar(self)
class Bar(object):
def __init__(self, parent):
self.parent = weakref.ref(parent) # <= garbage-collector safe!
self.newText = parent.text
foo = Foo()
is it possible to get the object, say Foo, that contains another object, Bar, from within Bar itself?
Not "automatically", because the language isn't built like that, and in particular, the language is built such that there is no way to guarantee that Foo exists.
That said, you can always do it explicitly. Attributes, like every other identifier in Python, are just names, not storage space for data; so nothing prevents you from letting the Bar instance have a manually assigned foo attribute that is a Foo instance, and vice-versa at the same time.
Yes, it's possible. Even without passing the container reference on object creation, i.e. if your object is a class attribute.
Your object needs to implement the descriptor protocol (have a __get__()):
class ChildName(SimpleNamespace):
def __get__(self, instance, owner):
# instance is our parent
return f'I am {self.name}, my parent is {instance.name}.'
class ChildDiff(SimpleNamespace):
#property
def diff(self):
return self.born - self.parent.born
def age_diff(self):
return f'I am {self.diff} years older than {self.parent.name}.'
def __get__(self, instance, owner):
self.parent = instance # XXX: weakref?
return self # expose object to be able call age_diff() etc.
class Parent(SimpleNamespace):
child_name = ChildName(name='Bar')
child_diff = ChildDiff(born=42)
parent = Parent(name='Foo', born=23)
print(parent.child_name) # ... I am Bar, my parent is Foo.
print(parent.child_diff.age_diff()) # ... I am 19 years older than Foo.
This worked for me:
the parent:
import child
obj_1 = 25 # an object that both parent and child can access
def startup(): # any startup function
global obj_1
child.ref( obj_1 ) # send the shared object to the child
...
the child:
obj_1 = 0 # initual value will be overwritten
def ref(shared_obj): # receive a reference to the shared object
global obj_1 # shared object is global in the child, optional
obj_1 = shared_obj # set obj_1 in the child to be obj_1 in the parent
What about using inheritance:
class Bar(object):
def __init__(self):
self.newText = self.text
class Foo(Bar):
def __init__(self):
self.txt = 'Hello World'
Bar.__init__(self)
foo = Foo()
print foo.newText

Pass a parent class as an argument?

Is it possible to leave a parent class unspecified until an instance is created?
e.g. something like this:
class SomeParentClass:
# something
class Child(unspecifiedParentClass):
# something
instance = Child(SomeParentClass)
This obviously does not work. But is it possible to do this somehow?
You can change the class of an instance in the class' __init__() method:
class Child(object):
def __init__(self, baseclass):
self.__class__ = type(self.__class__.__name__,
(baseclass, object),
dict(self.__class__.__dict__))
super(self.__class__, self).__init__()
print 'initializing Child instance'
# continue with Child class' initialization...
class SomeParentClass(object):
def __init__(self):
print 'initializing SomeParentClass instance'
def hello(self):
print 'in SomeParentClass.hello()'
c = Child(SomeParentClass)
c.hello()
Output:
initializing SomeParentClass instance
initializing Child instance
in SomeParentClass.hello()
Have you tried something like this?
class SomeParentClass(object):
# ...
pass
def Child(parent):
class Child(parent):
# ...
pass
return Child()
instance = Child(SomeParentClass)
In Python 2.x, also be sure to include object as the parent class's superclass, to use new-style classes.
You can dynamically change base classes at runtime. Such as:
class SomeParentClass:
# something
class Child():
# something
def change_base_clase(base_class):
return type('Child', (base_class, object), dict(Child.__dict__))()
instance = change_base_clase(SomeParentClass)
For example:
class Base_1:
def hello(self):
print('hello_1')
class Base_2:
def hello(self):
print('hello_2')
class Child:pass
def add_base(base):
return type('Child', (base, object), dict(Child.__dict__))()
# if you want change the Child class, just:
def change_base(base):
global Child
Child = type('Child', (base, object), dict(Child.__dict__))
def main():
c1 = add_base(Base_1)
c2 = add_base(Base_2)
c1.hello()
c2.hello()
main()
Result:
hello_1
hello_2
Works well in both python 2 and 3.
For more information, see the related question How to dynamically change base class of instances at runtime?

access calling classes atrributes

I have 2 classes defined as such
class class1():
self.stuff = 1
def blah(self):
foo = class2()
foo.start()
class class2(threading.Thread):
def run(self):
#access class1.stuff
How would I access class1.stuff from class2
It could look like this:
class class1(object):
stuff = 1
def blah(self):
foo = class2()
foo.start()
class class2(threading.Thread):
def run(self):
print(class1.stuff)
There is no special syntax to "access calling classes atrributes". If you want access to a object you must give it a visible name, for example by passing it to __init__ or by using the class object like this.
You would have to pass it into the function.
class class1():
self.stuff = 1
def blah(self):
foo = class2()
foo.start(self)
class class2(threading.Thread):
def run(self, obj):
obj.stuff;
There is no way to access another object's properties without having a reference to the object. The easiest way to obtain a reference to an object of class1 is to ask for it as an argument.

Categories