Overrides method in object python

Overriding Methods in Python (with Examples)

Let’s create a parent class and a class. The class will inherit from the parent class, meaning it will have all of its methods.

overriding methods of a class

class Robot:
def action(self):
print(‘Robot action’)

class HelloRobot(Robot):
def action(self):
print(‘Hello world’)

r = HelloRobot()
r.action()

Instance r is created using the class HelloRobot, that inherits from parent class Robot.

The HelloRobot class inherits the method action from its parent class, but its overridden in the class itself.

Overriding methods

The method is overwritten. This is only at class level, the parent class remains intact.
If we add another class that inherits, let’s see what happens.

class Robot:
def action(self):
print(‘Robot action’)

class HelloRobot(Robot):
def action(self):
print(‘Hello world’)

class DummyRobot(Robot):
def start(self):
print(‘Started.’)

r = HelloRobot()
d = DummyRobot()

r.action()
d.action()

Источник

Method overriding in Python

What is overriding? Overriding is the ability of a class to change the implementation of a method provided by one of its ancestors.

Overriding is a very important part of OOP since it is the feature that makes inheritance exploit its full power. Through method overriding a class may «copy» another class, avoiding duplicated code, and at the same time enhance or customize part of it. Method overriding is thus a strict part of the inheritance mechanism.

A quick glance to inheritance¶

As for most OOP languages, in Python inheritance works through implicit delegation: when the object cannot satisfy a request, it first tries to forward the request to its ancestors, following the specific language rules in the case of multiple inheritance.

class Parent(object): def __init__(self): self.value = 5 def get_value(self): return self.value class Child(Parent): pass 

As you can see the Child class is empty, but since it inherits from Parent Python takes charge of routing all method calls. So you may use the get_value() method of Child objects and everything works as expected.

Читайте также:  Date java lang string is deprecated

Indeed get_value() is not exactly part of the Child class as if it were defined in it

>>> p = Parent() >>> c = Child() >>> >>> dir(p) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_value', 'value'] >>> >>> dir(c) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_value', 'value'] >>> >>> dir(Parent) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_value'] >>> >>> dir(Child) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_value'] >>> >>> Parent.__dict__ dict_proxy(  'get_value': , '__dict__': , '__weakref__': , '__doc__': None, '__init__': >) >>> >>> Child.__dict__ dict_proxy() 

This shows that the Child class does not actually contain the get_value() method and that a mechanism of automatic delegation is active under the hood. For an insight on this mechanism check this post.

First-class objects in Python¶

Higher-order functions, wrappers, and factories

Learn all you need to know to understand first-class citizenship in Python, the gateway to grasp how decorators work and how functional programming can supercharge your code.

Method overriding in action¶

In Python method overriding occurs simply defining in the child class a method with the same name of a method in the parent class. When you define a method in the object you make the latter able to satisfy that method call, so the implementations of its ancestors do not come in play.

class Parent(object): def __init__(self): self.value = 5 def get_value(self): return self.value class Child(Parent): def get_value(self): return self.value + 1 

Now Child objects behave differently

and taking a look inside the class we spot a difference

>>> Parent.__dict__ dict_proxy(  'get_value': , '__dict__': , '__weakref__': , '__doc__': None, '__init__': >) >>> >>> Child.__dict__ dict_proxy(  'get_value': , '__doc__': None>) 

since now the Child class actually contains a get_value() method with a different implementation (the id of the two functions are different).

This is of uttermost importance in Python. Inheritance delegation occours automatically, but if a method is overridden the implementation of the ancestors is not considered at all. So, if you want to run the implementation of one or more of the ancestors of your class, you have to call them explicitly.

Why should you want to call the implementation of objects that are deeper in the class hierarchy?

You may want to call it because many times you override a method to enhance its nature, that is to improve the «quality» of the result, and to improve something you need to first access it. So, by calling the original implementation, you get the result you later want to improve.

There is however a well defined reason why you must always call the original implementation. This reason may be called «hidden side effects».

When you inherit from a class, you are actually inheriting a whole class hierarchy which internal structure is (or shall be considered) unknown. This means that any method call may hide a complex set of operations on the whole class hierarchy, and some of them may be vital for the library or the framework you are using.

Python makes you call the original implementation of an overridden method explicitly (not differently from other object-oriented languages). This surely follows the Python idea that «Explicit is better than implicit» (The Zen of Python), but this advice is not just a matter of taste or some sort of programming mannerism.

When you override you have to think if you want to filter the arguments for the original implementation, if you want to filter its results, or both. You typically want to filter arguments (pre-filter) if you want to change the data that the parent implementation shall process while you filter the results (post-filter) if you want to add an additional processing layer. Obviously both things may be done together in the same method. Since you have to explicitly call the parent implementation you are free to do it where you want in the code of the new method: the decision about the type of filtering you want to achieve affects the position of the call.

An example of pre-filtering

import datetime class Logger(object): def log(self, message): print message class TimestampLogger(Logger): def log(self, message): message = "  ".format(ts=datetime.datetime.now().isoformat(), msg=message) super(TimestampLogger, self).log(message) 

The TimestampLogger object adds some information to the message string before calling the original implementation of its log() method.

>>> l = Logger() >>> l.log('hi!') hi! >>> >>> t = TimestampLogger() >>> t.log('hi!') 2014-05-19T13:18:53.402123 hi! 

An example of post-filtering

import os class FileCat(object): def cat(self, filepath): f = file(filepath) lines = f.readlines() f.close() return lines class FileCatNoEmpty(FileCat): def cat(self, filepath): lines = super(FileCatNoEmpty, self).cat(filepath) nonempty_lines = [l for l in lines if l != '\n'] return nonempty_lines 

When you use the FileCatNoEmpty object you get the result of the FileCat object with the empty lines stripped.

As you can see while in the first example the original implementation has been called as the last thing, in the second one it is called before everything else. There is therefore no fixed position for the call of the original method, and it depends on what you want to do.

Always call super()?¶

Shall we always call the original method implementation? In theory a well designed API should make it always possible but we know that boundary cases exist: the original method may have side effect that you want to avoid and sometimes the API cannot be refactored to avoid them. In those cases you may prefer to skip the call to the original implementation of the method; Python does not make it mandatory, so feel free to walk that path if you think the situation requires it. Be sure to know what you are doing, however, and document why you are completely overwriting the method.

First-class objects in Python¶

Higher-order functions, wrappers, and factories

Learn all you need to know to understand first-class citizenship in Python, the gateway to grasp how decorators work and how functional programming can supercharge your code.

Summary¶

  • Call the original implementation of a method you are overriding whenever possible. This meakes the underlying API work as expected. When in need of skipping the call be sure to document the reasons.
  • Always use super(cls, self) for Python 2.x or super() for Python 3.x to call the original implementation of a method. This respects the resolution order in case of multiple inheritance and, for Python 3.x, protects from changes in the class hierarchy.
  • If you call to the original implementation of a method do it as soon as you have all the data you need to run it.

Updates¶

2014-05-21: As suggested by caseyweb in this comment the conclusions of the post were too strong, so I updated the post with a new section («Always call super()?») and the summary. Many thanks!

Resources¶

Feedback¶

Feel free to reach me on Twitter if you have questions. The GitHub issues page is the best place to submit corrections.

python-oop-tdd

A simple example of Python OOP development (with TDD) — Part 1

python-oop-tdd

A simple example of Python OOP development (with TDD) — Part 2

accessing-attributes-in-python

Accessing attributes in Python

multiple-inheritance-and-mixin-classes-in-python

Multiple inheritance and mixin classes in Python

delegation-composition-and-inheritance-in-object-oriented-programming

Delegation: composition and inheritance in object-oriented programming

first-class-objects-in-python

First-class objects in Python — Higher-order functions, wrappers, and factories

clean-architectures

Clean architectures in Python: a step-by-step example

cabook

Clean Architectures in Python: the book

tdd-in-python-with-pytest-part-5

TDD in Python with pytest — Part 5

tdd-in-python-with-pytest-part-1

TDD in Python with pytest — Part 1

Источник

Оцените статью