How dynamically add base class to dynamic class in python - python

In case I would like dynamically add a base class to child class
but I don't know what classes will be
which option should I use?
# first_file.py
# Option 1
def add_base(clazz, new_parent_class):
bases = tuple(list(clazz.__bases__) + [new_parent_class])
clazz.__bases__ = bases
return clazz
# Option 2
def add_base(clazz, new_parent_class):
bases = tuple(list(clazz.__bases__) + [new_parent_class])
clazz = type(clazz.__name__, bases, dict(clazz.__dict__))
return clazz
# second_file.py
class OtherClass(object # or any other class #)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class MyClass(object # or any other class #)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# fourth_file.py
from first_file import add_base
from second_file import OtherClass
from third_file import MyClass
new_class = add_base(OtherClass, MyClass)
ins = new_class()

Related

Singleton that instantiates a class by its object and arguments

I'm trying to prepare a singleton class that would distinguish the instances not only by class types, but also by arguments with which the class was called.
Let's say I've a Singleton class like below:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
And now I'm trying to create two class instances with different parameters:
class MyClass(metaclass=Singleton):
def __init__(self, x, y):
print(f"Called constructor with x as {x} and y as {y}")
a = MyClass(1, 2)
b = MyClass(1, 2)
print(id(a) == id(b)) # Returns True, which is fine
c = MyClass(1, 3)
d = MyClass(1, 2)
print(id(c) == id(d)) # Returns True, which is not really fine to me :)
# Moreover y in that case is 3, not 2.
What in case I want to distinguish instances in singleton additionally by parameters with which the class was initialized?
Here is what worked for me:
from dataclasses import dataclass
#dataclass(frozen=True)
class ObjectKey:
obj_type: object
args: tuple
kwargs: frozenset
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
obj_key = ObjectKey(obj_type=cls, args=args, kwargs=frozenset(kwargs.items()))
if obj_key not in cls._instances:
cls._instances[obj_key] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[obj_key]

How to make a third party obj thread safe in python?

I want to use a third module in python like this :
import some_module
class MyService():
def __init__(self):
self.some_module_obj = some_module.some_obj()
def run(self,some_parameter):
self.some_module_obj.some_attritude(some_parameter)
I know that some_module.some_obj and its method some_attritude is not thread safe , My question is how to make MyService thread safe ?
update 1:
I see Artiom Kozyrev's code , Is the code below right ?
import some_module
import threading
GLOBAL_LOCK = threading.Lock()
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
with GLOBAL_LOCK:
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(
*args, **kwargs
)
return cls._instances[cls]
class MyService(metaclass=Singleton):
def __init__(self):
self.some_module_obj = some_module.some_obj()
self.rlock = threading.RLock()
def run(self,some_parameter):
result = None
with self.rlock:
result = self.some_module_obj.some_attritude(some_parameter)
return result

Adding hooks to functions in subclassed methods

Given the following simplified code:
from abc import ABC, abstractmethod
class Parent(ABC):
def __init__(self,*args,**kwargs):
self.parent_name = 'SuperClass'
# global hook to run before each subclass run()
def global_pre_run_hook(self):
pass
#abstractmethod
def run(self, *args, **kwargs):
raise NotImplementedError()
class Child(Parent):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.name = 'ChildClass'
def run(self):
print(f'my parent name is {self.parent_name}')
print(f'my name is {self.name}')
return 22
obj = Child()
result = obj.run()
Is there a way to add functionality so that when the child class run() method is called directly, it first triggers a hook function from the parent class? Assume there is a parent class and a lot of classes that subclass it - would I need to manually add a call global hook() at the beginning of each run() definition for each class that subclasses Parent()? Is there a pythonic way to accomplish this?
There might be a way to do this with a proper decorator, but for the time being I think the cleanest solution you might come up with would be to create your own 'decorator' and manually apply it in the course of Parent.__init__(), which would make sure it takes effect so long as the child class invokes the parent __init__():
from abc import ABC, abstractmethod
def create_hook(func, hook):
def wrapper(*args, **kwargs):
hook()
return func(*args, **kwargs)
return wrapper
class Parent(ABC):
def __init__(self, *args, **kwargs):
self.parent_name = 'SuperClass'
self.run = create_hook(self.run, self.global_pre_run_hook)
# global hook to run before each subclass run()
def global_pre_run_hook(self):
print("Hooked")
#abstractmethod
def run(self, *args, **kwargs):
raise NotImplementedError()
class Child(Parent):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.name = 'ChildClass'
def run(self):
print(f'my parent name is {self.parent_name}')
print(f'my name is {self.name}')
return 22
obj = Child()
result = obj.run()
# this prints:
# Hooked
# my parent name is SuperClass
# my name is ChildClass
The answer by Green Cloak Guy works, but cannot be pickled! To fix this, we need to move the hook creation into __new__. Also, it's a good idea to make use of functools.wraps in the hook creator.
import pickle
from abc import ABC, abstractmethod
from functools import wraps
def create_hook(func, hook):
#wraps(func)
def wrapper(*args, **kwargs):
hook(*args, **kwargs)
return func(*args, **kwargs)
return wrapper
class Parent(ABC):
def __new__(cls, *args, **kwargs):
cls.run = create_hook(cls.run, cls.global_pre_run_hook)
return super().__new__(cls, *args, **kwargs)
# global hook to run before each subclass run()
def global_pre_run_hook(self, *args, **kwargs):
print("Hooked")
#abstractmethod
def run(self, *args, **kwargs):
raise NotImplementedError()
class Child(Parent):
def run(self):
print(f"my parents are {self.__class__.__mro__}")
print(f"my name is {self.__class__.__name__}")
return 22
obj = Child()
result = obj.run()
pickle.dumps(obj)

Why is super().__init__(*args,**kwargs) being used when class doesn't specify a superclass?

I understand why super() is used when a class is a subclass of a superclass, but what is the superclass of a class that doesn't specify the superclass in the subclass parameters? Here's my code:
import random
class Sneaky:
sneaky = True
def __init__(self, sneaky=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sneaky = sneaky
def hide(self, light_level):
return self.sneaky and light_level < 10
class Agile:
agile = True
def __init__(self, agile=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.agile = agile
def evade(self):
return self.agile and random.randint(0, 1)
Suppose Sneaky is used as part of a multiple inheritance class structure such as:
class Sneaky:
sneaky = True
def __init__(self, sneaky=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sneaky = sneaky
def hide(self, light_level):
return self.sneaky and light_level < 10
class Person:
def __init__(self, human=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.human = human
class Thief(Sneaky, Person):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
t = Thief()
print(t.human)
# True
Since
In [62]: Thief.mro()
Out[62]: [__main__.Thief, __main__.Sneaky, __main__.Person, object]
Thief.__init__'s super().__init__(*args, **kwargs) calls Sneaky.__init__.
Sneaky.__init__'s super().__init__(*args, **kwargs) calls Person.__init__.
If the super().__init__ call were removed from Sneaky.__init__, then t.human would raise
AttributeError: 'Thief' object has no attribute 'human'
because Person.__init__ would not get called.

__repr__ decorator fails on subclasses

Background
I wrote a decorator function to modify the __repr__ of a given class, such that when an class instance is called all its attributes are printed to the user. When used in the on the Container class in the example below the decorator __repr__dec behaves as intended.
Input
def __repr__wrapper(self):
"""Show all attributes."""
return "Attributes: "+", ".join(list(self.__dict__.keys()))
def __repr__dec(func):
"""Replaces the __repr__ function of a class with __repr__wrapper"""
def call(*args, **kwargs):
func.__repr__ = __repr__wrapper
result = func(*args, **kwargs)
return result
return call
#__repr__dec
class Container(object):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
occ = Container(42, how="now")
occ
Output
Attributes: metadata, how
However when trying to subclass Container I receive a TypeError message:
Input
class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
han = Handle(42)
Output
TypeError Traceback (most recent call last)
<ipython-input-17-b4c252411c1f> in <module>()
----> 1 class Handle(Container):
2 def __init__(self, *args, **kwargs):
3 Container.__init__(self, *args, **kwargs)
4
5 han = Handle(42)
TypeError: function() argument 1 must be code, not str
Question
Why does sub-classing Conatainer fail when using the __repr__dec function? Is it possible to fix this?
The problem is that your decorator made Container a function and no longer a class. You can control it very simply:
>>> type(Container)
<class 'function'>
This is because your use of the decorator ends in the following:
declare a undecorated class
class Container:
...
use the decorator on it:
Container = __repr__dec(Container)
As __repr__dec returns a function you have indeed change Container into a function able to return objects having the expected __repr__ member, but it is no longer a class.
Your decorator must return a class if you want to be able to later subclass it:
def repr_dec(cls):
cls.__repr__ = __repr__wrapper
return cls
Then everything is fine:
>>> Container
<class '__main__.Container'>
>>> occ=Container(42, how="now")
>>> occ
Attributes: metadata, how
And you can successfully subclass it:
>>> class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
>>> han = Handle(42, foo="now")
>>> han
Attributes: metadata, foo
Handle class has inherited the __repr__ method from its parent.
def replace_str(cls):
class Wrapper:
def __init__(self, *args, **kargs):
self.wrapped = cls(*args, **kargs)
def __getattr__(self, attrname):
return getattr(self.wrapped, attrname)
def __str__(self):
return "Attributes: " + ", ".join(list(self.wrapped.__dict__.keys()))
return Wrapper
#replace_str
class Container(object):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
Using a proxy class could easily achieve this.
also, metaclass could do this:
class PrintKey(type):
def __new__(meta, classname, bases, namespace):
namespace['__str__'] = lambda self: "Attributes: " + ", ".join(list(self.__dict__.keys()))
return type.__new__(meta, classname, bases, namespace)
class Container(object, metaclass=PrintKey):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v

Categories

Resources