2

For example, if I create the class Foo, then later derive the subclass Bar, I want the myCode() method of Foo to run.

class Foo(object):
    x = 0
    def __init__(self):
        pass
    def myCode(self):
        if(self.x == 0):
            raise Exception("nope")


class Bar(Foo):    #This is where I want myCode() to execute
    def baz(self):
        pass

This should happen any time a class is derived from the base class Foo. Is it possible to do this in Python? I'm using Python 3 if it matters.

Note: In my real code, Foo is actually an abstract base class.

Edit: I also need access to derived class member data and methods in myCode().

Nathan BeDell
  • 2,263
  • 1
  • 14
  • 25
  • 3
    Look up metaclasses. This would require a bit of trickery to have Foo "tell" the metaclass that it wants to be called when subclassing. If you attempt a metaclass solution you will probably be in a better position to ask a more specific question. – BrenBarn Jun 09 '14 at 02:25
  • I'm actually using abstract base classes from the "abc" library for my Foo class, I looked at the documentation for that and it didn't seem like it would help me with what I want to do though. I'm not sure what the difference between an abstract and a meta class is, but I'll look into it. – Nathan BeDell Jun 09 '14 at 02:29
  • What exactly do you want `myCode` to do? – user2357112 Jun 09 '14 at 02:34
  • What "self" argument do you want to be passed to `myCode`? – DSM Jun 09 '14 at 02:39
  • In this use case `myCode()` would check the implementation of the derived class to ensure that is consistent with how derived instances of the parent class should be used. Tldr: I need to be able to access derived class member data in `myCode()`, e.a. `self.x`. I'm not sure how to do that with grayshirt's answer. – Nathan BeDell Jun 09 '14 at 02:44
  • It should be the "self" of the derived class. – Nathan BeDell Jun 09 '14 at 02:47
  • @Sintrastes: but at the time the class is defined, when the line you highlight is executed, there aren't any instances to refer to. So even though you say that you want code to run when a class is derived, do you really mean that you want code to run when an *instance* of a subclass is *constructed*? -- update, after reading your edit: or do you want *the subclass itself* to be passed to `myCode`? – DSM Jun 09 '14 at 02:50
  • I'm not very experienced so grain of salt but this runtime validation of subclasses sounds like an odd use case and I wonder if we have an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here. Are these subclasses being defined dynamically or outside code you control? – grayshirt Jun 09 '14 at 02:54
  • Yes, this is for a library, and the user of the library would be defining the subclasses, so it would be outside code that I control. Also, what I need is to access class data members (like `x` in my edited code), I don't know if I need `self` or not, but that's the only way I know of to access `x`. – Nathan BeDell Jun 09 '14 at 03:08
  • I'll let someone who has drank less bourbon than I have during this coversation work out the example code to do that, but a question: Is it your job to ensure users of your library are doing their job properly? Is this a practice employed by other libraries? – grayshirt Jun 09 '14 at 03:27
  • That's a good question, I'd like to explain why exactly I got to the point of wanting to do this in the first place, but It's kind of hard to explain without rewriting my question. I'll give it a shot anyway: I need my subclasses to have constant class data, right now I'm using abstract functions that just return the relevant data that the user has to provide the implementation of in order to use the derived subclasses. Using functions for data doesn't seem like best practice to me, so I want to use static class data instead. This would allow me to do that. ...(cont) – Nathan BeDell Jun 09 '14 at 03:35
  • ...(cont) I'd also be able to do some quick validation of the data, but that's more of a secondary motivation for me. It just seems appropriate to me that the library (which deals with properties of magmas) would do some basic validation of data before the user goes through some expensive computation only to have it break in the middle of the whole process. – Nathan BeDell Jun 09 '14 at 03:38
  • So you can see what I'm talking about, this is the library as is, (reader be warned, this is very rough code): https://github.com/Sintrastes/magpy – Nathan BeDell Jun 09 '14 at 03:40

3 Answers3

3

Use a metaclass:

class MetaClass:
    def __init__(cls, name, bases, dictionary):
        if name is not 'Parent':
            print('Subclass created with name: %s' % name)
        super().__init__(name, bases, dictionary)

class Parent(metaclass=MetaClass):
    pass

class Subclass(Parent):
    pass

Output:

Subclass created with name: Subclass

Metaclasses control how classes themselves are created. Subclass inherits its metaclass from Parent, and thus that code gets run when it is defined.

Edit: As for your use case with an abstract base class, off the top of my head I think you'd just need to define your metaclass as a subclass of ABCMeta, but I didn't test that.

Community
  • 1
  • 1
grayshirt
  • 615
  • 4
  • 13
1

May this code can help you:

class Foo:
    def myCode(self):
        print('myCode within Foo')
    def __init__(self):
        if type(self) != Foo:
            self.myCode()

class Bar(Foo):
    def __init__(self):
        super(Bar, self).__init__()
    def baz(self):
        pass

Test:

>>> 
>>> f = Foo()
>>> b = Bar()
myCode within Foo
>>> 
Tok Soegiharto
  • 329
  • 1
  • 8
  • I need the code to run when a class is derived from my base class `Foo`, not when derived classes of `Foo` are instantiated. – Nathan BeDell Jun 09 '14 at 02:50
0

This works:

class MyMeta(type):
    def __new__(cls, name, parents, dct):
        if name is not 'Foo':
            if 'x' not in dct:
                raise Exception("Nein!")
            elif 'x' in dct and dct['x'] == 0:
                raise Exception("Nope!")
        return super(MyMeta, cls).__new__(cls, name, parents, dct)

Output:

class Bar(Foo):
    x = 0
> Exception: Nope!

This is my specific use case if anyone wants to comment on whether or not this is appropriate:

class MagmaMeta(type):
    def __new__(cls, name, parents, dct):
        # Check that Magma instances are valid.
        if name is not 'Magma':
            if 'CAYLEY_TABLE' not in dct:
                raise Exception("Cannot create Magma instance without CAYLEY_TABLE")
            else:
                # Check for square CAYLEY_TABLE
                for row in CAYLEY_TABLE:
                    if not len(row) == len(dct['CAYLEY_TABLE']):
                        raise Exception("CAYLEY_TABLE must be a square array")
                # Create SET and ORDER from CAYLEY_TABLE
                dct['SET'] = set([])
                for rows in CAYLEY_TABLE:
                    for x in rows:
                        dct['SET'].add(x)
                dct['ORDER'] = len(dct['SET'])
        return super(MyMeta, cls).__new__(cls, name, parents, dct)
Nathan BeDell
  • 2,263
  • 1
  • 14
  • 25