0

I have many similar classes, they all contain their own class attributes:

class MyClass1(): 
    Context = ClassId1+"CONTEXTSTR"
    SubContext = ClassId1+"SUBCONTEXTSTR"
    UpVal = ClassID+"UPVAL"
    DoenVal = ClassID1+"DOWNVAL"
class MyClass2(): 
    Context = ClassId2+"CONTEXTSTR"
    SubContext = ClassId2+"SUBCONTEXTSTR"
    UpVal = ClassID2+"UPVAL"
    DoenVal = ClassID2+"DOWNVAL"
...

but all this soon becomes annoying and requires a lot of code repetition (error-prone).

I would like to be able to manipulate a sort of class_variable and to do something like:

class MyClass1():
    self_cls.MakeParameters(ClassId1)

even better if I could use inheritance and pass parameters to classes to do things like:

ClassID1 = "test01"    

class MyClass1(BaseClass,<ClassID1>):
    pass

print MyClass1.CONTEXT

obtaining as output "test01CONTEXTSTR" the code. How to do set the arguments of classes according to a given "template" that takes a parameter?

gecco
  • 17,969
  • 11
  • 51
  • 68
jimifiki
  • 5,377
  • 2
  • 34
  • 60
  • Can you explain why you need these all in class variables? These seem like they could be returned by methods which need only know the `ClassID`... – Noah Jan 10 '12 at 15:53
  • I have to add some more classes to a code I didn't write. – jimifiki Jan 10 '12 at 15:58
  • The other users of this code want to keep writing "NameClass.Context" for every new class. So I want a way to assign the "static" variables to all the classes I need (for instance by using inheritance). – jimifiki Jan 10 '12 at 16:18
  • "The other users of this code want to keep writing "NameClass.Context" for every new class". Good. Why do you want to stop them from doing the right thing? – S.Lott Jan 10 '12 at 16:39
  • Hi Lott, I want them to continue do this. They asked me to code some new classes. In order to avoid similar code repetition I need something of the kind: "def mypar(cls,par) : cls.thec = par ; class B(parameter): mypar(self,parameter)" where self should refer to the class, not to a specific object... (is not the real self, is what I need, is the part of syntax I think I'm missing) – jimifiki Jan 10 '12 at 17:04

2 Answers2

2
>>> class MyClass:
    def __init__(self, classid):
        self.context = "%s%s" % (classid, "CONTEXTSTR")


>>> somevar = MyClass("someid")
>>> print somevar.context
someidCONTEXTSTR
>>> 

Now if you want to inherit a class, it's a bit different. Continuning from above:

>>> class NewClass(MyClass):
    def __init__(self, classid, secondid):
        MyClass.__init__(self, classid)
        self.secondcontext = "%s_%s" % (secondid, "SECONDCONTEXT")


>>> secondvar = NewClass("someid", "someotherid")
>>> secondvar.context
'someidCONTEXTSTR'
>>> secondvar.secondcontext
'someotherid_SECONDCONTEXT'
>>> 

To auto-set the context (Which I think you're asking?), use keyword arguments:

>>> class NewClass(MyClass):
    def __init__(self, newclassid="newclassid", myclassid="myclassid"):
        MyClass.__init__(self, myclassid)
        self.newcontext = "%s%s" % (newclassid, " new context")


>>> NewClass().context
'myclassidCONTEXTSTR'
>>> NewClass().newcontext
'newclassid new context'
>>> NewClass(newclassid="only set this ones id").context
'myclassidCONTEXTSTR'
>>> NewClass(newclassid="only set this ones id").newcontext
'only set this ones id new context'
>>> 

Here I didn't assign the class, I just called it (Hence the brackets) and the keywords filled it in for me.

I think this is what you mean?

You can also set it as a keyword for MyClass, and it'll auto-assign if the NewClass doesn't set the variable. I don't think you'd need a code example for that though, but ask if you do.

I think this is what you're after, if not you'll need to clarify a bit more sorry.

TyrantWave
  • 4,583
  • 2
  • 22
  • 25
  • The other users of this code want to keep writing "NameClass.Context" for every class even when there is no object belonging to the class. So I want a way to assign the "static" variables to all the classes I write (for instance by using inheritance) and not to the objects. – jimifiki Jan 10 '12 at 16:29
  • I think I've explained what you're after in the new edit, but I'm still not sure. Do you want classes you write to have pre-set variables, but when other people write classes (In the same format, just not including your base class), it'll work the same as before? – TyrantWave Jan 10 '12 at 18:26
2

Of course, you could add the attributes manually to the class like this:

def addToClass(cls, ClassId):
    cls.Context = ClassId1+"CONTEXTSTR"
    cls.SubContext = ClassId1+"SUBCONTEXTSTR"
    cls.UpVal = ClassID+"UPVAL"
    cls.DoenVal = ClassID1+"DOWNVAL"

class NewClass(MyClass):
    ...

Usage:

addToClass(NewClass, "someid")

But if you think that this is still too 'manual' and you would expect that a great language like Python should provide more and better, then you are right: metaclasses

Use metaclasses

You can achieve the wanted behavior using metaclasses (If you don't know what I am talking about I recommend reading the great answer posted in this issue: What is a metaclass in Python?)

Write a Metaclass factory method:

def getMetaClass(classId):
    class MyMetaClass(type):
        def __new__(cls, name, bases, dct):
            dct["Context"] =  "%sCONTEXTSTR" % classId
            dct["SubContext"] = "%sSUBCONTEXTSTR" % classId
            dct["UpVal"] = "%sUPVAL" % classId
            dct["DownVal"] = "%sDOWNVAL" % classId
            return super(MyMetaClass, cls).__new__(cls, name, bases, dct)
    return MyMetaClass

Define a class based on your Metaclass:

class MyClass1():
    __metaclass__ = getMetaClass("test01")

Use your class:

>>> A.Context
'test01CONTEXTSTR'

Update: If you don't like having this __metaclass__ in every of your classes, you could hide it in a superclass like this (proposed in comment by Anders):

Metaclass:

class MyMetaClass(type):
    def __new__(cls, name, bases, dct):
        classId = dct.get("CLASSID", "noClassId")
        dct["Context"] =  "%sCONTEXTSTR" % classId
        dct["SubContext"] = "%sSUBCONTEXTSTR" % classId
        dct["UpVal"] = "%sUPVAL" % classId
        dct["DownVal"] = "%sDOWNVAL" % classId
        return super(MyMetaClass, cls).__new__(cls, name, bases, dct)

Class hiding Meta logic:

class AutoContext:
    __metaclass__ = getMetaClass()

Usage:

class MyClass1(AutoContext):
    CLASSID = "test01"
Community
  • 1
  • 1
gecco
  • 17,969
  • 11
  • 51
  • 68
  • This is exactly how I've implemented it. It's ok, it does the work. But I was looking for something slightly more object-oriented (I want the user of the class to know that addToClass() is going to run on it just by reading the class-body). I wonder why I cannot do the same I usually do with the creation of objects: I would like to do the analogous of what I do when I implement the __init__() specifying how to initialize the members of all the objects of the class (or I would like to know if and why that's not possible). – jimifiki Jan 10 '12 at 22:06
  • 1
    @jimifiki I think metaclasses is what you need: See my updated post. – gecco Jan 11 '12 at 07:28
  • 2
    It might be cleaner to hide the metaclass in a superclass. So the definition becomes: class MyClass1(AutoContext): CLASSID="test01" – Anders Waldenborg Jan 11 '12 at 07:36