Passing class names to dictionary (and parsing order) - python

I have some code that looks like the following:
class Action(object):
...
class SpecificAction1(Action):
...
class SpecificAction2(Action):
...
They are all specified in the same file. Before their specification I want to put a dictionary that looks like this:
ACTIONS = {
"SpecificAction1": SpecificAction1,
"SpecificAction2": SpecificAction2
}
The idea is that I can simply import the ACTIONS dictionary from other modules and have this one dictionary be the one canonical string representation of the actions (they are sent over the network and other places where I need some identifier).
Is it possible to do "class pointers" like this in the same way you do function pointers? And my editor complains that names are undefined before the dictionary is declared before the class definitions - is this true?
Also, if the above is possible can I do this to instantiate a class: ACTIONS['SpecificAction2']()?

Classes are first-class citizens in Python, i.e. you can treat them like any other object. From that point of view your construction is perfectly fine, except that you have to define ACTIONS dictionary at the end of the file (because unlike some other languages the order is important in Python: it will throw a ReferenceError otherwise).
There's even more. You could use some metaprogramming to simplify this. Consider this (Python2.x, the syntax is a bit different in 3.x):
ACTIONS = {}
class MyMeta(type):
def __init__(cls, name, bases, nmspc):
super(MyMeta, cls).__init__(name, bases, nmspc)
if name != "Action": # <--- skip base class
ACTIONS[name] = cls
class Action(object):
__metaclass__ = MyMeta
...
class SpecificAction1(Action):
...
class SpecificAction2(Action):
...
It will automatically populate ACTIONS dictionary with any class which inherits from Action class (because Action class has MyMeta as a __metaclass__). Read more about metaprogramming here:
https://python-3-patterns-idioms-test.readthedocs.org/en/latest/Metaprogramming.html
As for ACTIONS['SpecificAction2'](): yes, it will create a new instance of the class, it's perfectly valid code.

Yes you can do that. Python is a dynamic language and allows you to just that. However, from your ACTIONS dict, the values are definitions of classes. If you want to provide them over network, pass them as strings and use getattr
ACTIONS = {
"SpecificAction1": 'SpecificAction1',
"SpecificAction2": 'SpecificAction2'
}
And them import the files containing the definitions:
module = __import__('my_actions_module')
class_ = getattr(module,ACTIONS.get('SpecificAction1'))
instance = class_()
and instance is what you need.

Related

Python 3.9 if a string matches a class name, instantiate the class (instantiate a specific class dynamically)

I would like to instantiate a single class if a string var matches the name of the class. In similar questions, there seems to be a dependency that the name of the class is known at the start. In my case, I only know that the input string will match the name of a class. I want to use the string to identify which class (out of many choices) I need to instantiate. I.E. load the class with the same name as the string;
If string-var == a class (in a module file) in a directory, instantiate that class.
I thought the new getattr would help, but that seems to be for methods/functions as opposed to a class itself. I also considered issubclass, but the parameter needs to be a class, not a string. Any suggestions are greatly appreciated!
You should explicitly define an interface that maps a publicly known string to a reference to the class:
class Foo:
...
class Bar:
...
classes = {"foo": Foo, "bar": Bar}
Then you can lookup the appropriate class in the dict using your string (which, as you'll note, is not required to be the name of the class):
cls_name = "foo"
obj = classes[cls_name]()

Set class variable value, the value returned by a class method

I'm trying to create a class which maps to a mongoDB collection.
My code looks like this:
class Collection:
_collection = get_collection() # This seems not working
#classmethod
def get_collection(cls):
collection_name = cls.Meta.collection_name if cls.Meta.collection_name \
else cls.__name__.lower()
collection = get_collection_by_name(collection_name) # Pseudo code, please ignore
return collection
class Meta:
collection_name = 'my_collection'
I came across a situation where I need to assign the class variable _collection with the return value of get_collection.
I also tried _collection = Collection.get_collection() which also seems not to be working
As a work-around, I subclassed Collection and set value of _collection in the child class.
Would like to know any simple solution for this.
Thanks in advance
As DeepSpace mentions, here:
class Collection:
_collection = get_collection() # This seems not working
#classmethod
def get_collection(cls):
# code that depends on `cls`
the get_collection method is not yet defined when you call it. But moving this line after the method definition won't work either, since the method depends on the Collection class (passed as cls to the method), which itself won't be defined before the end of the class Collection: statement's body.
The solution here is to wait until the class is defined to set this attribute. Since it looks like a base class meant to be subclassed, the better solution would be to use a metaclass:
class CollectionType(type):
def __init__(cls, name, bases, attrs):
super(CollectionType, cls).__init__(name, bases, attrs)
cls._collection = cls.get_collection()
# py3
class Collection(metaclass=CollectionType):
# your code here
# py2.7
class Collection(object):
__metaclass__ = CollectionType
# your code here
Note however that if Collection actually inherit from a another class already having a custom metaclass (ie Django Model class or equivalent) you will need to make CollectionType a subclass of this metaclass instead of a subclass of type.
There are some design/syntax errors in your code.
When the line _collection = get_collection() executes, get_collection is not yet defined. As a matter of fact, the whole Collection class is not yet defined.
get_collection_by_name is not defined anywhere.
EDIT OP updated the question so the below points may not be relevant anymore
collection = get_collection(collection_name) should be collection = cls.get_collection(collection_name)
Sometimes you are passing a parameter to get_collection and sometimes you don't, however get_collection's signature never accepts a parameter.
Calling get_collection will lead to an infinite recursion.
You have to take a step back and reconsider the design of your class.

Static member initialization in python from static member value of a derived class

Is it possible to initialize a static member of a base class from a static member of a derived class in python (2.7)?
Namely, suppose I have a bunch of classes that map the entities stored in a simple database:
class EntityA(EntityBase):
entityName = 'EntA' # the name of the entity in the DB
...
class EntityB(EntityBase):
entityName = 'EntB' # the name of the entity in the DB
...
and suppose that the DB is built following the rule that all entities have an id field that is named 'id_name-of-the-entity'. So, 'id_EntA' and 'id_EntB' are the names of the id fields in the DB for EntityA and EntityB respectively.
Now I'd like to generate these two names from the (abstract) base-class (EntityBase) only once, but I can't find a way to do it...
I'd like to write something like:
class EntityBase:
idFieldName = 'id_' + *derived-class*.entityName
...
I know I could write a simple function that returns the concatenated string, but I'd like it not to be evaluated every time I call that function. That should be possible, since all the information needed to build the idFieldName value are stored in static variables.
What you can use is a metaclass. A metaclass is the class to which a certain class belongs.
Then you can use:
class MetaEntityBase(type):
def __new__(meta, name, bases, dct):
if 'entityName' in dct:
dct['idFieldName'] = 'id_'+dct['entityName']
return super(MetaEntityBase,meta).__new__(meta,name,bases,dct)
Then you can write:
class EntityBase:
__metaclass__ = MetaEntityBase
and now if we query for EntityA.idFieldName, we get:
>>> EntityA.idFieldName
'id_EntA'
Here we used an if statement to inspect the dct first. The dct is a dictionary that contains the classes members before initialization: so it contains all methods, class fields, etc.
We thus check if 'entityName' is one of the keys (this thus means that at the class level, it is defined somewhere). If that is the case, we add a new element to the dct: 'idFieldName' that prepends the entityName with id_. Of course you can define an else case for what to do if there is no such attribute entityName
The __new__ of a metaclass is executed at the construction of the class, not the construction of an object. So unless you dynamically create classes, it will be called only once.

How to build a hierarchical view of inherited classes in Python?

This is a question I tried to avoid several times, but I finally couldn't escape the subject on a recent project. I tried various solutions and decided to use one of them and would like to share it with you. Many solutions on internet simply don't work and I think it could help people not very fluent with classes and metaclasses.
I have hierarchy of classes, each with some class variables which I need to read when I instantiate objects. However, either these variables will be overwritten, or their name would be mangled if it has the form __variable. I can perfectly deal with the mangled variables, but I don't know, with an absolute certainty, which attribute I should look in the namespace of my object. Here are my definitions, including the class variables.
class BasicObject(object):
__attrs = 'size, quality'
...
class BasicDBObject(BasicObject):
__attrs = 'db, cursor'
...
class DbObject(BasicDBObject):
__attrs = 'base'
...
class Splits(DbObject):
__attrs = 'table'
...
I'd like to collect all values stored in __attrs of each class when Instantiate the Splits class. The method __init__() is only defined in the class BasicObject and nowhere else. Though, I need to scan self.__dict__ for mangled __attrs attributes. Since other attributes have the pattern attrs in these objects, I can't filter out the dictionary for everything with the pattern __attrs in it ! Therefore, I need to collect the class hierarchy for my object, and search for the mangled attributes for all these classes.
Hence, I will use a metaclass to catch each class which calls __new__() method which is being executed when a class definition is encountered when loading a module. By defining my own __new__() method in the base class, I'll be able to catch classes when each class is instantiated (instantiation of the class, not an object instantiation).
Here is the code :
import collections
class BasicObject(object) :
class __metaclass__(type) :
__parents__ = collections.defaultdict(list)
def __new__(cls, name, bases, dct) :
klass = type.__new__(cls, name, bases, dct)
mro = klass.mro()
for base in mro[1:-1] :
cls.__parents__[name] = mro[1]
return klass
def __init__(self, *args, **kargs) :
"""
Super class initializer.
"""
this_name = self.__class__.__name__
parents = self.__metaclass__.__parents__
hierarchy = [self.__class__]
while this_name in parents :
try :
father = parents[this_name]
this_name = father.__name__
hierarchy.append(father)
except :
break
print(hierarchy)
...
I could have access attributes using the class definition, but all these classes are defined in three different modules and the main one (init.py) doesn't know anything about the other modules.
This code works well in Python 2.7 and should also work in Python 3.. However, Python 3. have some new features which may help write a simpler code for this kind of introspection, but I haven't had the time to investigate it in Python 3.0.
I hope this short explanation and example will save some of your (precious) time :-)
Yes, the question is the answer; simply because I couldn't find anything other than the "Ask Question" button on the site. Did I miss something ?

Is there a method like '__getattribute__' for class (not instance) variables?

I have a class sysprops in which I'd like to have a number of constants. However, I'd like to pull the values for those constants from the database, so I'd like some sort of hook any time one of these class constants are accessed (something like the getattribute method for instance variables).
class sysprops(object):
SOME_CONSTANT = 'SOME_VALUE'
sysprops.SOME_CONSTANT # this statement would not return 'SOME_VALUE' but instead a dynamic value pulled from the database.
Although I think it is a very bad idea to do this, it is possible:
class GetAttributeMetaClass(type):
def __getattribute__(self, key):
print 'Getting attribute', key
class sysprops(object):
__metaclass__ = GetAttributeMetaClass
While the other two answers have a valid method. I like to take the route of 'least-magic'.
You can do something similar to the metaclass approach without actually using them. Simply by using a decorator.
def instancer(cls):
return cls()
#instancer
class SysProps(object):
def __getattribute__(self, key):
return key # dummy
This will create an instance of SysProps and then assign it back to the SysProps name. Effectively shadowing the actual class definition and allowing a constant instance.
Since decorators are more common in Python I find this way easier to grasp for other people that have to read your code.
sysprops.SOME_CONSTANT can be the return value of a function if SOME_CONSTANT were a property defined on type(sysprops).
In other words, what you are talking about is commonly done if sysprops were an instance instead of a class.
But here is the kicker -- classes are instances of metaclasses. So everything you know about controlling the behavior of instances through the use of classes applies equally well to controlling the behavior of classes through the use of metaclasses.
Usually the metaclass is type, but you are free to define other metaclasses by subclassing type. If you place a property SOME_CONSTANT in the metaclass, then the instance of that metaclass, e.g. sysprops will have the desired behavior when Python evaluates sysprops.SOME_CONSTANT.
class MetaSysProps(type):
#property
def SOME_CONSTANT(cls):
return 'SOME_VALUE'
class SysProps(object):
__metaclass__ = MetaSysProps
print(SysProps.SOME_CONSTANT)
yields
SOME_VALUE

Categories