Call method from top class in hierarchy instead of override - python

Let's say I have classes Base(object) and Derived(Base). These both implement a function foo, with Derived.foo overriding the version in Base.
However, in one of the methods, say Base.learn_to_foo, I want to call Base.foo instead of the derived version regardless of whether it was overridden. So, I call Base.foo(self) in that method:
class Base(object):
# ...
def learn_to_foo(self, x):
y = Base.foo(self, x)
# check if we foo'd correctly, do interesting stuff
This approach seems to work and from a domain standpoint, it makes perfect sense, but somehow it smells a bit fishy. Is this the way to go, or should I refactor?

The answer is NOT to use the super() function. The way you are doing is exactly right as you don't want to invoke the virtual method that is overridden in the super class. Since you seem to want the base class' exact implementation all the time, the only way is to get the base class' unbound method object back, bound it to self, which could be an instance of Base or Derived. Invoke the unbound method with self supplied explicitly as the first parameter gives you back a bound method. From this point forward, Base.foo will be acting on the instance self's data. This is perfectly acceptable and is the way Python deals with non-virtual method invocation. This is one of the nice things that Python allows you to do that Java does not.

It is recommended:
def learn_to_foo(self, x):
super(Derived, self).foo(x)
More information at http://docs.python.org/library/functions.html#super

An alternative is to use the 'super' built-in:
super(Derived, self).foo(x) # Python 2
super().foo(x) # Python 3

Related

How are dunder methods accessed?

number = 5
print(number.__class__)
print(number.__str__())
print(number.__add__(1))
#Output
<class 'int'>
5
6
I guess I'm trying to see if I understand this correctly.
So there's different ways to access the dunder methods. In the first case, it looks as though I'm accessing the method as though it were an attribute of the class int. In this case, is __class__ using an #property decorator to access it as though it were an attribute? If that's the case, then it makes sense.
Second and third make sense. __str___ is a dunder method defined in the class int that takes no arguments. You call it like a method. __add__ takes a argument, which is the number you're adding to it. So you have to call it like a method with an argument. So none of these use #property decorators.
I guess what makes it more confusing is that technically the __str__ can be made into an attribute with the #property decorator. I tested it out myself on my own class and it definitely works either way, so it seems a bit arbitrary which ones are accessed like attributes and which ones are accessed like methods. Assuming my theory is correct.
So there's different ways to access the dunder methods. In the first case, it looks as though I'm accessing the method as though it were an attribute of the class int.
That's because __class__ is an attribute, not a method.
Another distinction to make is that the way you access methods is always the same. Dunder methods are not magical in this. You use a . between the instance and the method name. Whether you are referring just to the method or calling it to get its result is another matter.
While this is a reasonable exercise to understand how Python works, remember that you should not usually call dunder methods directly. Python offers other syntax that will call these methods under the hood. For example, use str(number) instead of number.__str__() and a + b rather than a.__add__(b).
The main reason for dunder methods is that they allow you to override default behavior in your own classes. They aren't intended to be called directly.

Reusing method from another class without inheritance or delegation in Python

I want to use a method from another class.
Neither inheritance nor delegation is a good choice (to my understanding) because the existing class is too complicated to override and too expensive to instanciate.
Note that modifying the existing class is not allowed (legacy project, you know).
I came up with a way:
class Old:
def a(self):
print('Old.a')
class Mine:
b = Old.a
and it shows
>>> Mine().b()
Old.a
>>> Mine().b
<bound method Old.a of <__main__.Mine object at 0x...>>
It seems fine.
And I tried with some more complicated cases including property modification (like self.foo = 'bar'), everything seems okay.
My question:
What is actually happening when I define methods like that?
Will that safely do the trick for my need mentioned above?
Explanation
What's happening is that you are defining a callable class property of class Mine called b. However, this works:
m = Mine()
m.b()
But this won't:
Mine.b()
Why doesn't the second way work?
When you call a function of a class, python expects the first argument to be the actual object upon which the function was called. When you do this, the self argument is automatically passed into the function behind the scenes. Since we called Mine.b() without an instantiated instance of any object, no self was passed into b().
Will this "do the trick"?
As for whether this will do the trick, that depends.
As long as Mine can behave the same way as Old, python won't complain. This is because the python interpreter does not care about the "type" of self. As long as it walks like a duck and quacks like a duck, it's a duck (see duck typing). However, can you guarantee this? What if someone goes and changes the implementation of Old.a. Most of the time, as a client of another system we have no say when the private implementation of functions change.
A simpler solution might be to pull out the functionality you are missing into a separate module. Yes, there is some code duplication but at least you can be confident the code won't change from under you.
Ultimately, if you can guarantee the behavior of Old and Mine will be similar enough for the purposes of Old.a, python really shouldn't care.

How exactly do parentheses work when it comes to classes, methods, and instances in Python?

Classes, methods, and instances have me thoroughly confused. Why is it that this works:
class Computer:
def config(self):
print('i5, 16gb, 1TB')
com1 = Computer()
com1.config()
But this does not?:
class Computer:
def config(self):
print('i5, 16gb, 1TB')
com1 = Computer()
Computer.config()
But, if you modify the code directly above to include the instance as an argument when calling the method, like so:
Computer.config(com1)
it works? I see the difference between the two, but I don't understand it.
In your example Computer.config is an instance method, means it is bound to an object, or in other words requires an object. Therefore in your second example you would write:
com1.config()
You can also write it in the following style:
Computer.config(com1)
which is the same and just syntactic sugar. In same special cases you might prefer the latter one, e.g. when duck typing is required, but the first one is definitely the preferred way.
In addition, there are staticmethod or classmethod functions, which don't require any objects. An example would be this:
class Computer
#staticmethod
def config():
print("Foo")
Computer.config() # no object required at all, because its a staticmethod
Please notice the missing self reference. Technically Computer.config as a staticmethod is just a simple function, but it helps to group functionalities so to make them a class member.
This is because config() is an instance method, and not a static method. An instance method, simply said, is shared across all objects of a class, and cannot be accessed by just using the class name. If you want to call config() by just using class name, you have to tell compiler that it is static. To do this use #staticmethod tag, and remove the self argument:
class Computer:
#staticmethod
def config():
print('i5, 16gb, 1TB')
com1 = Computer()
com1.config()
And as you said:
if you modify the code directly above to include the instance as an
argument when calling the method, like so:
This is because, every instance function takes self as it's first argument. Basically self is somewhat similar to this in C++ or Java, and it tells which object to work upon, i.e., self acts as a pointer to the current object. When you passed the com1 object to the argument, it took self for com1.
That means that when you invoke com1.config() what you are actually doing is calling config with com1 as its argument. So com1.config(() is the same as Computer.config(com1).
Why doesn't the second approach work?
Because Computer is an instance of something called a meta-class. It is somewhat the class definition and not actually an instance of Computer. com1 is an instance created by Computer. You can use com1 to invoke its non-static functions but you can't use the meta-class Computer to invoke non-static functions, which need an object to be ran on.
Why does the third approach work?
Have you noticed the self in the definition of the config function? That means that when you invoke com1.config() what you are actually doing is calling config with com1 as its argument. So com1.config() is the same as Computer.config(com1).

Why using self keyword when calling parent method from child method in Python?

Why it's required to use self keyword as an argument when calling the parent method from the child method?
Let me give an example,
class Account:
def __init__(self,filepath):
self.filepath = filepath
with open(self.filepath,"r") as file:
self.blanace = int(file.read())
def withDraw(self,amount):
self.blanace = self.blanace - amount
self.commit()
def deposite(self,amount):
self.blanace = self.blanace + amount
self.commit()
def commit(self):
with open(self.filepath,"w") as file:
file.write(str(self.blanace))
class Checking(Account):
def __init__(self,filepath):
Account.__init__(sellf,filepath) ######## I'm asking about this line.
Regarding this code,
I understand that self is automatically passed to the class when declaring a new object, so,
I expect when I declare new object, python will set self = the declared object, so now the self keyword will be available in the "'init'" child method, so no need to write it manually again like
Account.__init__(sellf,filepath) ######## I'm asking about this line.
All instance methods are just function-valued class attributes. If you access the attribute via an instance, some behind-the-scenes "magic" (known as the descriptor protocol) takes care of changing foo.bar() to type(foo).bar(foo). __init__ itself is also just another instance method, albeit one you usually only call explicitly when overriding __init__ in a child.
In your example, you are explicitly invoking the parent class's __init__ method via the class, so you have to pass self explicitly (self.__init__(filepath) would result in infinite recursion).
One way to avoid this is to not refer to the parent class explicitly, but to let a proxy determine the "closest" parent for you.
super().__init__(filepath)
There is some magic here: super with no arguments determines, with some help from the Python implementation, which class it statically occurs in (in this case, Checking) and passes that, along with self, as the implicit arguments to super. In Python 2, you always had to be explicit: super(Checking, self).__init__(filepath). (In Python 3, you can still pass argument explicitly, because there are some use cases, though rare, for passing arguments other than the current static class and self. Most commonly, super(SomeClass) does not get self as an implicit second argument, and handles class-level proxying.)
Specifically, the function class defines a __get__ method; if the result of an attribute lookup defines __get__, the return value of that method is returned instead of the attribute value itself. In other words,
foo.bar
becomes
foo.__dict__['bar'].__get__(foo, type(foo))
and that return value is an object of type method. Calling a method instance simply causes the original function to be called, with its first argument being the instance that __get__ took as its first argument, and its remaining arguments are whatever other arguments were passed to the original method call.
Generally speaking, I would tally this one up to the Zen of Python -- specifically, the following statements:
Explicit is better than implicit.
Readability counts.
In the face of ambiguity, refuse the temptation to guess.
... and so on.
It's the mantra of Python -- this, along with many other cases may seem redundant and overly simplistic, but being explicit is one of Python's key "goals." Perhaps another user can give more explicit examples, but in this case, I would say it makes sense to not have arguments be explicitly defined in one call, then vanish -- it might make things unclear when looking at a child function without also looking at its parent.

Is it a good idea to call a staticmethod in python on self rather than the classname itself

If I have the following class.
class Foo:
#staticmethod
def _bar():
# do something
print "static method"
def instancemethod(self):
Foo._bar() # method 1
self._bar() # method 2
In this case, is method 1 a preferred way of calling staticmethod _bar() or method 2 in the Python world?
Write the code that expresses what you want to do. If you want to call this method right here:
class Foo:
#staticmethod
def _bar(): # <-- this one
# do something
print "static method"
then specify that particular method:
Foo._bar()
If you want to call whatever self._bar resolves to, meaning you've actually decided that it makes sense to override it and made sure your code still behaves sensibly when that method is overridden, then specify self._bar:
self._bar()
Most likely, this method isn't designed to be overridden, and the code that uses it isn't designed to anticipate overriding it, so you probably want Foo._bar().

Categories