Understand the meaning of this piece of code? - python-3.x

What does this code mean?
class Singleton(object):
_instances = {}
def __new__(class_, *args, **kwargs):
if class_ not in class_._instances:
class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs) # noqa E501
return class_._instances[class_]

This is a parent class for creating Singleton classes. The Singleton pattern means that there is only one instance of a class. (For example, None is the only instance of the NoneType class).
This works by creating a map of classes to instances, _instances. It has overridden the default __new__ method so that whenever someone tries to create a new instance, it either uses the existing instance from the map or stores the new instance in the map.

Related

Using parent class method and child instance, call the most concrete method (child's method)

Consider this code:
class Interface():
def my_method(self, a):
print(f"Interface: {a=}")
class ChildA(Interface):
def my_method(self, a):
print(f"ChildA test: {a=}")
method = Interface.my_method
my_instance = ChildA()
method(my_instance, a=4)
This prints Interface: a=42. How can I make it print ChildA: a=42?
Obviously I do not want to use ChildA.my_method, I want a solution that works for any new child classes that are implemented in the future.
The best you can do is just write a helper function:
def methodcaller(name):
def inner(obj, *args, **kwargs):
return getattr(obj, name)(*args, **kwargs)
return inner
method = methodcaller('my_method')
Knowing the method name and the fact that the object will be an instance of Interface doesn't let you perform any part of the method resolution in advance, unfortunately. You would need to know the concrete class in advance. (Also, operator.methodcaller doesn't work, because it requires all method arguments to be provided at methodcaller creation time.)

MetaClass in Python

I am trying to create a Meta-Class for my Class.
I have tried to print information about my class in meta-class
Now I have created two objects of my class
But Second object gets created without referencing my Meta-Class
Does Meta Class gets called only once per Class??
Any help will be appreciated
Thanks
class Singleton(type):
def __new__(cls,name,bases,attr):
print (f"name {name}")
print (f"bases {bases}")
print (f"attr {attr}")
print ("Space Please")
return super(Singleton,cls).__new__(cls,name,bases,attr)
class Multiply(metaclass = Singleton):
pass
objA = Multiply()
objB = Multiply()
print (objA)
print (objB)
Yes - the metaclass's __new__ and __init__ methods are called only when the class is created. After that, in your example, the class will be bound to theMultiply name. In many aspects, it is just an object like any other in Python. When you do objA = Multiply() you are not creating a new instance of type(Multiply), which is the metaclass - you are creating a new instance of Multiply itself: Multiply.__new__ and Multiply.__init__ are called.
Now, there is this: the mechanism in Python which make __new__ and __init__ be called when creating an instance is the code in the metaclass __call__ method. That is, just as when you create any class with a __call__ method and use an instance of it with the calling syntax obj() will invoke type(obj).__call__(obj), when you do Multiply() what is called (in this case) is Singleton.__call__(Multiply).
Since it is not implemented, Singleton's superclass, which is type __call__ method is called instead - and it is in there that Multiply.__new__ and __init__ are called.
That said, there is nothing in the code above that would make your classes behave as "singletons". And more importantly you don't need a metaclass to have a singleton in Python. I don't know who invented this thing, but it keeps circulating around.
First, if you really need a singleton, all you need to do is to write a plain class, no special anything, create your single instance, and document that the instance should be used. Just as people use None - no one keeps getting a reference to Nonetype and keep calling it to get a None reference:
class _Multiply:
...
# document that the code should use this instance:
Multiply = _Multiply()
second Alternatively, if your code have a need, whatsoever, for instantiating the class that should be a singleton where it will be used, you can use the class' __new__ method itself to control instantiation, no need for a metaclass:
class Multiply:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
# insert any code that would go in `__init__` here:
...
...
return cls._instance
Third just for demonstration purposes, please don't use this, the metaclass mechanism to have singletons can be built in the __call__ method:
class Singleton(type):
registry = {}
def __new__(mcls,name,bases,attr):
print(f"name {name}")
print(f"bases {bases}")
print(f"attr {attr}")
print("Class created")
print ("Space Please")
return super(Singleton,mcls).__new__(cls,name,bases,attr)
def __call__(cls, *args, **kw):
registry = type(cls).registry
if cls not in registry:
print(f"{cls.__name__} being instantiated for the first time")
registry[cls] = super().__call__(*args, **kw)
else:
print(f"Attempting to create a new instance of {cls.__name__}. Returning single instance instead")
return registry[cls]
class Multiply(metaclass = Singleton):
pass

Singleton only containing a dict, statically accesible

I want to provide a class, that contains a dictionary, that should be accessible all over my package. This class should be initialized by another class, which is a database connector.
From the database I retrieve the mapping but I want to do this only once on initialization of the database connector. Furthermore this mapping than should be availabe to all other modules in my package without getting the instance of the database connector passed through all function calls.
I thought about using a Singleton pattern and tried some stuff from this SO post. But I can't find a working solution.
I tried it this way with metaclass:
The mapping class:
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]
class CFMapping(metaclass=Singleton):
def __init__(self, cf_mapping: dict):
self._cf_mapping = cf_mapping
#classmethod
def get_cf_by_name(cls, name: str) -> str:
return cls._cf_mapping.get(name)
The database connector
class DBConnector:
def __init__(....):
# some init logic, connection to db etc...
self.cf_mapping = self.get_cf_mapping() # just returning a dict from a rest call
Now i expect the mapping to be accesible via the DBConnector instance.
But at other scripts where I don't have this instance I would like to access the mapping just over a static/class method like this:
CFMapping.get_cf_by_name("someName")
# leads to AttributeError as CFMapping has no attribute _cf_mapping
Is there a way to get this construct to work the way I want it to or is there some better approach for some problem like this?

Can python metaclasses inherit?

classes can inherit..
class Base:
def __init__(self,name):
self.name = name
class Derived1(Base):
def __init__(self,name):
super().__init__(name)
class Derived2(Base):
def __init__(self,name):
super().__init__(name)
Can a similar thing done for meta classes also?
I have a requirement where some of my classes will have to be both abstract base classes and also my own meta classes (say singleton types..)
Is it possible to do
class Singleton(type):
'''
implementation goes here..
'''
class AbstractSingleton(Singleton,ABCMeta):
'''
What code should go here??
'''
If its possible how to implement the AbstractSingleton class?
Yes, it is possible.
But first things first:
You should not be using metaclasses for creating singletons in Python.
Singletons are a simple concept, and just a custom __new__ method is enough - no need for a metaclass for that.
This simple 4 line normal class code can be used as a mixin, and will turn any derived classes into "singleton" classes - afer the first instance is created, no further instances are created, and the first instance is always returned:
class SingletonBase:
def __new__(cls, *args, **kw):
if not "instance" in cls.__dict__:
cls.instance = super().__new__(cls, *args, **kw)
return cls.instance
Now, if you'd have a real case for another metaclass and needed to combine that with ABCMeta or other metaclass, all you'd have to do is to create a third metaclass that inherits from both metaclasses - if both of them use super in a well behaved way, it would just work.
class SingletonMeta(type):
def __call__(cls, *args, **kw):
# You know - you _really_ should not be using metaclasses for singletons.
if not "instance" in cls.__dict__:
cls.instance = super().__call__(*args, **kw)
return cls.instance
class SingletonAbstractMeta(SingletonMeta, abc.ABCMeta):
pass
class SingleAbstractBase(metaclass=SingleAbstractMeta):
...
For sheer coincidence, earlier this week I used exactly this use case as an example of what can be achieved with a "meta meta class" in Python. By having a special "meta meta class" to the metaclass one wants to combine to another (I even use ABCMeta on the example), it can create the derived combined metaclass just by using the operator " + ", like in
class SingletonMeta(type, metaclass=MM):
...
class AbstractSingletonBase(metaclass=SingletonMeta + abc.ABCMeta):
# code here.
Check the answer here.

Change class parent dynamically from attributes python 3

I want the new class to dynamically inherit from different parents depending on an attribute given when creating an instance. So far I've tried something like this:
class Meta(type):
chooser = None
def __call__(cls, *args, **kwars):
if kwargs['thingy'] == 'option':
Meta.choose = option
return super().__call__(*args, **kwargs)
def __new__(cls, name, parents, attrs):
if Meta.choose == option:
bases = (parent1)
return super().__new__(cls, name, parents, attrs)
It doesn't work, is there a way that, depending on one of the parameters of the instance, I can dynamically choose a parent for the class?
First, lets fix a trivial mistake in the code, and then dig into the "real problem": the bases parameter needs to be a tuple. when you do bases=(option) the right hand side is not a tuple - it is merely a parenthesized expression that will be resolved and passed on as the non-tuple option.
Change that to bases=(option,) whenever you need to create a tuple for the bases.
The second mistake is more conceptual, and is probably why you didn't get it to work across varios attempts: the __call__ method of the metaclass is not something one usually fiddles with. To keep a long history short, the __call__ method of a __class__ is what is called to coordinate the calling of the __new__ and __init__ methods of instances of that class - that is made by Python automatically, and it is the __call__ from type that has this mechanism. When you transpose that to your metaclass you might realise that the __call__ method of your metaclass is not used when the __new__ and __init__ methods of your metaclass itself are about to be called (when a class is defined). In other words - the __call__ that is used is on the "meta-meta" class (which is again, type).
The __call__ method you wrote will instead be used by the instances of your custom classes when they are created (and this is what you intended), and will have no effect on class creation as it won't invoke the metaclass' __new__ - just the class __new__ itself. (and this is not what you intended).
So, what you need is, from inside __call__ not to call super().__call__ with the same arguments you received: that will pass cls to type's call, and the bases for cls are baked in when the metaclass __new__ was run - it just happens when the class body itself is declared.
You must have, in this __call__ dynamically create a new class, or use one of a pre-filled in table, and them pass that dynamically created class to the type.__call__.
But - in the end of the day, one can perceive that all this can be done with a factory function, so there is no need to create this super-complicated meta-class mechanism for that - and other Python tools such as linters and static analysers (as embbeded in an IDE you or your colleagues may be using) might work better with that.
Solution using factory function:
def factory(cls, *args, options=None, **kwargs):
if options == 'thingy':
cls = type(cls.__name__, (option1, ), cls.__dict__)
elif options = 'other':
...
return cls(*args, **kwargs)
If you don't want to create a new class on every call, but want to share a couple of pre-existing classes with common bases, just create a cache-dictionary, and use the dict's setdefault method:
class_cache = {}
def factory(cls, *args, options=None, **kwargs):
if options == 'thingy':
cls = class_cache.setdefault((cls.__name__, options),
type(cls.__name__, (option1, ), cls.__dict__))
elif options = 'other':
...
return cls(*args, **kwargs)
(The setdefault method will store the second argument on the dict if the key (name, options) does not exist yet).
using a metaclass:
updated
After breakfast :-) I came up with this:
make your metaclass __new__ inject a __new__ function on the created class itself that will either create a new class, with the desired bases dynamically or used a cached one for the same options. But unlike the other example, use a metaclass to anotate the original parameters to the class creation to create the derived class:
parameter_buffer = {}
derived_classes = {}
class Meta:
def __new__(metacls, name, bases, namespace):
cls = super().__new__(metacls, name, bases, namespace)
parameter_buffer[cls] = (name, bases, namespace)
def __new__(cls, *args, option=None, **kwargs):
if option is None:
return original_new(cls, *args, **kwargs)
name, bases, namespace = parameter_buffer[cls]
if option == 'thingy':
bases = (option1,)
elif option== 'thingy2':
...
if not (cls, bases) in derived_classes:
derived_classes[cls, bases] = type(name, bases, namespace)
return derived_classes[cls, bases](*args, **kwargs)
cls.__new__ = __new__
return cls
To keep the example short, this simply overwrites any explict __new__ method on the class that uses this metaclass. Also, the derived classes created this way are not themselves bearer of the same capability since they are created calling type and the metaclass is discarded in the process. Both things could be taken care off by writing more careful code but it would become complicated as an example here.

Resources