goFrendiAsgard
goFrendiAsgard

Reputation: 4084

How to not accidentally override a method in python?

I know there are a bunch of similar questions out there. But my question is different. I don't want to make a method which can't be overridden. I want to protect my newly created class to not accidentally override something.

Using underscore as a prefix is pretty good, but soon I'll get a lot of methods with a lot of underscores. and somewhere in my inherited class, I will override the grand-ancestor's class method.

What I really want is something as simple as this:

class Cat(Mammal):
    def walk(self):
        if ancestor_has_function('walk'):
            parent.walk();
        do_something_here();

If any of Cat's ancestor (Either it is Mammal, Animal, or LifeForm) has "walk" method, then the parent method should be executed first.

Is that any possibility to do this in python?

EDIT: For instance this is the resume of answers I considered as good. Hope this will help others:

class Animal(object):
    pass
    #def walk(self):
    #    print('animal walk')

class Mammal(Animal):
    def walk(self):
        if hasattr(super(Mammal, self), 'walk') and callable(super(Mammal,self).walk):
            super(Mammal, self).walk()
        print('mammal walk')

class Cat(Mammal):
    def walk(self):
        super(Cat, self).walk()
        print('cat walk')
    
if __name__ == '__main__':
    cat = Cat()
    cat.walk()

And here is the output:

mammal walk

cat walk

Try to uncomment Animal's walk method, and you will have it work as well too.

Upvotes: 1

Views: 639

Answers (5)

tobias_k
tobias_k

Reputation: 82899

You can use the dir() function to get all the names declared for some module or class. Methods declared in classes higher up in the hierarchy will also be included. Note, however, that this will also include attributes, so check with callable() first.

Also, calling the parent method looks a bit different in python, see the code below.

def walk(self):
    if "walk" in dir(Mammal) and callable(Mammal.walk):
        Mammal.walk(self)
    # do something

Upvotes: 1

Mp0int
Mp0int

Reputation: 18727

import inspect

class SomeClass():
    def __init__(self):
        ...

    def somefunc(self):
        ....

    def someOtherFunc(self):
        ....


allmembers = inspect.getmembers(SomeClass, predicate=inspect.ismethod)

getmembers returns a list of all methods define within the given class, it is a list of tuples that contains method names and definitions:

[('__init__', <unbound method SomeClass.__init__>),
 ('somefunc', <unbound method SomeClass.somefunc>),
 ('someOtherFunc', <unbound method SomeClass.someOtherFunc>)]

Since first elements of the tuple are strings, you can use string based methods to filter base methods like __init__

allmembers = filter(lambda x: not x.startswith('__'), [x[0] for x in inspect.getmembers(SomeClass, predicate=inspect.ismethod))])

[('somefunc', <unbound method SomeClass.somefunc>),
('someOtherFunc', <unbound method SomeClass.someOtherFunc>)]

You can get a list of all methods defined within the class and check if you have a similarly named method, Sincegetmembers returns you an unbound method instance, you can also reach that function easily.

Upvotes: 0

Pi Delport
Pi Delport

Reputation: 10598

Generally speaking, you'll probably want to provide at least a stub method in whichever superclass is the most generic:

class Mammal(object):
    def walk(self):
        pass

Then, extend it in subclasses by calling super():

class Cat(Mammal):
    def walk(self):
        super(Cat, self).walk()  # or just super().walk(), in Python 3+
        do_something_here()

Making the super() call conditional is not hard, but it's probably a bad idea: it's verbose, fragile, and only encourages bad practices. If you really, really have good reason to do it, you can just use hasattr() on the super object, like you would with any other object:

class Cat(Mammal):
    def walk(self):
        if hasattr(super(Cat, self), 'walk'):
            super(Cat, self).walk()
        do_something_here()

You would only want to do this in unusual situations, though, such as subclassing classes from a third-party library where you can't rely on certain methods being present, for some reason.

Upvotes: 6

slallum
slallum

Reputation: 2241

Yep. hasattr checks if there is an attribute with a specific name. and callable checks if the specific attribute is callable.

class Mammal(object):
    def walk(self):
        print "walking"


class Cat(Mammal):
    def walk(self):
        if hasattr(Mammal,'walk') and callable(Mammal.walk):
            Mammal.walk(self);
        print "another walking!"

and now:

>>> my_cat = Cat()
>>> my_cat.walk()
walking
another walking!

Note that you can also use super to get your parent class like that:

if hasattr(super(Cat, self),'walk'):

Upvotes: 3

you can keep your original method in a field

class MyClass:
    def __method(self):
        pass

    def __init__(self):
        self.method = __method

and than check for identity and call the saved method

Upvotes: 0

Related Questions