Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python how to get the base instance of an instance?

In C# I would go:

myObj.base

I have a Date class which inherits from date.datetime. The Date class overrides __gt__() and __lt__() so when using the < and > operators they are called. I do not want to use these overrides - I want to use the date.datetime methods on an instance of Date.

like image 740
markmnl Avatar asked Dec 01 '11 02:12

markmnl


2 Answers

Use super() to get the superclass object. Type help(super) in the Python command prompt.

From the manual:

class super(object)
 |  super(type) -> unbound super object
 |  super(type, obj) -> bound super object; requires isinstance(obj, type)
 |  super(type, type2) -> bound super object; requires issubclass(type2, type)
 |  Typical use to call a cooperative superclass method:
 |  class C(B):
 |      def meth(self, arg):
 |          super(C, self).meth(arg)
like image 150
hochl Avatar answered Oct 18 '22 17:10

hochl


If I understand your question correctly, this doesn't make sense in Python. There's no "base instance" inside the instance of a subclass.

A Python instance is just one thing, containing a collection of attributes (any of which may have been set/modified by any of its base classes, or indeed from code outside any of its classes at all). Indeed it's possible to change the class of an instance at runtime, even transplanting it into an entirely different inheritance heirarchy (this is not frequently a good idea, but it's well-defined). No matter what, the instance remains a single unitary object, which only knows what attributes it has and which class it's an instance of (and in fact that's just an attribute: __class__).

Edit: If what you want is to be able to invoke overridden methods on an instance, then you do that by using super, as hocl answered. However it seems from your comments that you do not fully grok what super is doing (which is natural, as it's quite complex).

super(Date, myObj) doesn't return "the underlying datetime.date" instance, because there's no such thing, only the myObj object. Although for your purposes it sounds like this will fill your needs (and you can probably stop at this sentence).

What it does is return is a magical wrapper around myObj that looks up methods starting just "behind" Date; i.e. it finds the method that would be called if Date didn't override it. So in this case it will find all methods from datetime.date, because you only have single inheritance going on.

A key difference is that this supports multiple inheritance. If someone makes another class that inherits from Date and also inherits from datetime.date by another path, then super(Date, instanceOfThatClass) may not actually hit datetime.date's methods. It depends on the details of that inheritance heirarchy. This is actually the situation super was designed for though; it enabled classes in complex multiple inheritance hierarchies to cooperatively call each other's implementations, ensuring that each is called only once, and in an order that is sensible (though it may not be the same order for each ultimate leaf class, so classes in the middle of the hierarchy actually don't know which super-class implementation they're calling). My understanding is this complex situation cannot arise in C#, hence it can provide a simple .base syntax.

My understanding is also (and this is a bit of a guess), that because C# is statically typed and supports inheriting from classes defined in pre-compiled libraries, that when you have class Sub inheriting from class Base, inside an instance of Sub there really is a complete instance of Base which you can get at and then call methods on. This will affect shadowed fields; I would expect (again, as a non-C# programmer guessing a bit) that after getting the Base instance from a Sub instance, any direct reference to a field overridden by Sub would hit the field from Base, not the one from Sub.

In Python, OTOH, there is no base instance, and classes can't override fields. If the constructor and methods of Date and datetime.date both refer to the same field, it's just the one field in the instance that they're both sharing. So using super won't change what field you'll access, as you might expect if you think of it as getting the base instance.

Given that you're not using a complex multiple inheritance situation, if you wanted a simple syntax you could actually call Date.__lt__(myObj, otherObj) directly, though it looks ugly because you don't get to use the infix operator syntax when you do it that way. It's less horrible if you're considering ordinary methods; in that case it's possibly simpler than using super.

Take home message: I'm pretty sure super(Date, myObj) is what you want in this case. But if you get into more complicated situations, you don't want to think of super as the way to get "the base instance", like you would in C#. That understanding will trip you up when in multiple inheritance (which can be bloody confusing anyway), but also when you have multiple layers in the inheritance hierarchy using the same field.

like image 29
Ben Avatar answered Oct 18 '22 17:10

Ben