2

I want to automatically run a class method defined in a base class on any derived class during the creation of the class. For instance:

class Base(object):
  @classmethod
  def runme():
    print "I am being run"

  def __metclass__(cls,parents,attributes):
    clsObj = type(cls,parents,attributes)
    clsObj.runme()
    return clsObj


class Derived(Base):
  pass:

What happens here is that when Base is created, ''runme()'' will fire. But nothing happens when Derived is created.

The question is: How can I make ''runme()'' also fire when creating Derived.

This is what I have thought so far: If I explicitly set Derived's metaclass to Base's, it will work. But I don't want that to happen. I basically want Derived to use the Base's metaclass without me having to explicitly set it so.

Barry Steyn
  • 1,563
  • 3
  • 19
  • 25
  • Why are you defining `__metaclass__` inside the class like that? It's supposed to be used to set the metaclass to an externally-defined class. – BrenBarn Jun 21 '12 at 03:05
  • @BrenBarn: But why can't it be used like I have used it as well. As far as I can see, whether the __metaclass__ attribute points to a class object or a function object should not matter. But it does matter, and I think you are right. I want to know why? If you could, pls can you provide some simple code examples. – Barry Steyn Jun 21 '12 at 03:18

4 Answers4

2

See this answer. Basically, when calling type(cls,parents,attributes), you are creating a class without passing in the information about what that class's metaclass is. Thus, the class that is returned doesn't have the metaclass you want it to; instead it has metaclass type. (Ironically, by defining __metaclass__ to do as it does, you are explicitly causing your class to not have that metaclass.) Instead of directly calling type, you need to call type.__new__(meta, cls, parents, attrs), where meta is the metaclass.

However, you can't achieve this when you define __metaclass__ inline. From inside your __metaclass__ method, you have no way to refer to that method, because it's a method of a class that hasn't been defined yet. You want to do something like

def __metaclass__(cls, bases, attrs):
    type.__new__(metaclassGoesHere, cls, bases, attrs)

. . . but there's nothing you can put in for metaclassGoesHere to make it do the right thing, because what you're trying to refer to is the method inside which you're trying to refer to it.

So just define your metaclass externally.

Community
  • 1
  • 1
BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • Thanks @BrenBarn. You have clarified this matter for me very much. Basically, one needs to control the actual creation of the metaclass object, using __new__, we can control this and set the meta class of that object. Thank you. – Barry Steyn Jun 21 '12 at 15:51
1

If you want to do something when the class is created you have to do it in the __init__ method of __metaclass__.

class foo(object):
    @classmethod
    def method(cls):
        print 'I am being run by', cls.__name__

    class __metaclass__(type):
        def __init__(self, *args, **kwargs):
            type.__init__(self, *args, **kwargs)
            self.method()

class bar(foo): pass

Which prints:

I am being run by foo
I am being run by bar
John Damen
  • 399
  • 4
  • 8
0

rename your runme() to __init__(self) and DO NOT override the __init__() method, and it will get called every time you make an instance of Derived

class Base(object):
    def __init__(self):
        print "I am being run"

class Derived(Base):
    pass

dummy_instance = Derived()

Copy and paste that, it will print I am being run.

notbad.jpeg
  • 3,308
  • 1
  • 32
  • 37
  • Yes, I know this will happen for derived instances. But I am not talking about derived instances, I am talking about derived classes. The __init__ function intialises instance attributes, but I want initialisation to happen on the class level. It is for an API that I am designing, so I really need some metaclass magic, but I can't understand why it is not being inherited... – Barry Steyn Jun 21 '12 at 03:16
  • Could you provide the use-case for this pls @BarrySteyn – Jon Clements Jun 21 '12 at 03:18
  • @JonClements I am using SQLalchemy to make an ORM. One pattern for doing things is to use what is known as a declarative reflection pattern, but that necessitates one calling a class method of a base class. I would like this method called automatically once the class is loaded. – Barry Steyn Jun 21 '12 at 03:21
  • To clarify - once a class is instantiated? – Jon Clements Jun 21 '12 at 03:28
  • 1
    I'm just more: "What do you want to achieve" - than "how do we do this specifically in this way" – Jon Clements Jun 21 '12 at 03:30
  • When you start getting into the "black magic" of Python - it's just advisable to take one step and look at what you're trying to achieve again is all I'm saying – Jon Clements Jun 21 '12 at 03:36
  • @Barry Steyn I feel like you might know more python than I do. But still, i feel like if you have to say you want 'magic' to happen, you're trying to go along with bad coding practice. – notbad.jpeg Jun 21 '12 at 03:37
  • I've noticed trying to read through django's ORM code that magic is BAD. Explicit rather than implicit(don't hide what you're doing). – notbad.jpeg Jun 21 '12 at 03:38
  • 1
    @notbad.jpeg but Django does use some corner spots of the language :) – Jon Clements Jun 21 '12 at 03:39
  • @BarrySteyn I can think of a way to give you want you're asking for, but I'm damn sure it's not what you need... clarify your question, and we discuss upon the clarification – Jon Clements Jun 21 '12 at 03:41
0

Try This,

class MyMetaclass(type): 

    def __new__(cls, name, bases, dct):

        runme()

        return super(MyMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
Vivek S
  • 5,384
  • 8
  • 51
  • 72