buhtz
buhtz

Reputation: 12202

Get name of the current method, the class-name of the current instance, the class-name of the next sub-instance in a reusable way with Python3?

I want to extract the - the name of the "current" methode - the class name of the current object/instance - the class name of the next base class of that object/instance

The problem in the example below is that the methode-name is not really that what I want. But I need to make that code reusable for a lot of classes. I don't want to multiply that code for each class I need it for.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, inspect

def notimpl_str(obj):
    classname = obj.__class__.__name__

    methode = sys._getframe().f_code.co_name

    hi = inspect.getmro(obj.__class__)
    nistr = 'In {}.{}()\nLook in {}.{}()' \
        .format(classname,
                methode,
                hi[1].__name__,
                methode)

    return nistr


class Foo:
    def test(self):
        print(notimpl_str(self))

class Xoo(Foo):
    def __init__(self):
        self.test()

Xoo()

Background of my question: I want to implement a generic not-implemented-error-message if someone would call Xoo().test(). The message could like be Please implement Xoo.test(). See the documentation of Foo.test() for more details.

Upvotes: 1

Views: 3100

Answers (3)

buhtz
buhtz

Reputation: 12202

This is far away from perfect but a little bit more near to "generic". ;)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, inspect

def create_niestr(obj,
                  abstract_class,
                  methode):
    classname = obj.__class__.__name__
    hi = inspect.getmro(obj.__class__)
    this_method = getattr(obj, methode)
    base_method = getattr(abstract_class, methode)

    if classname is not abstract_class.__name__ and this_method.__func__ is base_method:
        nistr = 'Please implement {}.{}(). See the documentation of {}.{}() for more details.' \
                .format(classname, methode, hi[1].__name__, methode)
    else:
        nistr = 'Normal execution.'

    return nistr


class Foo:
    def test(self):
        nistr = create_niestr(self,
                              Foo,
                              sys._getframe().f_code.co_name)
        print(nistr)


class Xoo(Foo):
    pass

class Zoo(Foo):
    def test(self):
        super(self.__class__, self).test()

Foo().test()
Xoo().test()
Zoo().test()

Upvotes: 3

Swastik Padhi
Swastik Padhi

Reputation: 1909

Check this out!

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, inspect

class Foo:
    def test(self):
        classname = self.__class__.__name__
        methode = sys._getframe().f_code.co_name
        hi = inspect.getmro(self.__class__)
        this_method = getattr(self, 'test')
        base_method = getattr(Foo, 'test') #here 'Foo' is the base class and so this value has to be hard-coded for every base class inside which you paste this code
        if classname is not "Foo" and this_method.__func__ is base_method:
            nistr = 'Please implement {}.{}(). See the documentation of {}.{}() for more details.'\
            .format(classname, methode, hi[1].__name__, methode)
            print(nistr)
        else:
            print("Normal execution.") #put the actual code for the 'test' method here

class Xoo(Foo):
    pass

class Zoo(Foo):
    def test(self):
        super(self.__class__, self).test()

Foo().test() #test has been defined so normal execution
Xoo().test() #test has not been defined so a message is shown
Zoo().test() #test has been defined, even if it is just for calling the base class's 'test' method, it is still a valid definition so normal execution

Output-

Normal execution.
Please implement Xoo.test(). See the documentation of Foo.test() for more details.
Normal execution.

About implementing a generic method as you have shown in the question, well, I would say that will create a lot of confusion and may not be even possible as the value of self cannot contain the calling object as well as the called object. Also, if your call another method for checking this, then sys._getframe().f_code.co_name will no longer return the original method's name, which is what you want. So the best way would be to add this piece of code to every base class.

Upvotes: 1

Swastik Padhi
Swastik Padhi

Reputation: 1909

I think this is what you are looking for. I have added an additional parameter 'Inheritance hierarchy'.

import sys
import inspect

class Foo(object):
    def __init__(self):
        flag = 0
        print("Subclasses:", end=" ")
        for subclass in self.__class__.__subclasses__(): #prints the subclasses
            print(subclass.__name__, end=" ")
            flag = 1
        if flag == 0:
            print(None)
        else:
            print()
        print("Class name: ", self.__class__.__name__) #prints the class name
        print("Method name: ", sys._getframe().f_code.co_name) #prints the method name
        a = inspect.getmro(self.__class__)
        flag = 0
        print("Inheritance Hierarchy:", end=" ")
        for i in range(len(a)): #prints the inheritance hierarchy
            if not i == 0:
                print(a[(len(a)-1)-i].__name__, end=" ")
                flag = 1
        if flag == 0:
            print(None)
        else:
            print("\n")

class Xoo(Foo):
    def __init__(self):
        flag = 0
        print("Subclasses:", end=" ")
        for subclass in self.__class__.__subclasses__(): #prints the subclasses
            print(subclass.__name__, end=" ")
            flag = 1
        if flag == 0:
            print(None)
        else:
            print()
        print("Class name: ", self.__class__.__name__) #prints the class name
        print("Method name: ", sys._getframe().f_code.co_name) #prints the method name
        a = inspect.getmro(self.__class__)
        flag = 0
        print("Inheritance Hierarchy:", end=" ")
        for i in range(len(a)): #prints the inheritance hierarchy
            if not i == 0:
                print(a[(len(a)-1)-i].__name__, end=" ")
                flag = 1
        if flag == 0:
            print(None)
        else:
            print("\n")

class Zoo(Xoo):
    def __init__(self):
        flag = 0
        print("Subclasses:", end=" ")
        for subclass in self.__class__.__subclasses__(): #prints the subclasses
            print(subclass.__name__, end=" ")
            flag = 1
        if flag == 0:
            print(None)
        else:
            print()
        print("Class name: ", self.__class__.__name__) #prints the class name
        print("Method name: ", sys._getframe().f_code.co_name) #prints the method name
        a = inspect.getmro(self.__class__)
        flag = 0
        print("Inheritance Hierarchy:", end=" ")
        for i in range(len(a)): #prints the inheritance hierarchy
            if not i == 0:
                print(a[(len(a)-1)-i].__name__, end=" ")
                flag = 1
        if flag == 0:
            print(None)
        else:
            pass

Foo()
Xoo()
Zoo()

Running the above code, you get the following output:

Subclasses:  Xoo
Class name:  Foo
Method name:  __init__
Inheritance Hierarchy:  Foo 

Subclasses:  Zoo
Class name:  Xoo
Method name:  __init__
Inheritance Hierarchy:  Foo Xoo 

Subclasses:  None
Class name:  Zoo
Method name:  __init__
Inheritance Hierarchy:  Foo Xoo Zoo

Upvotes: 1

Related Questions