Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python Object-Oriented Python Advanced Objects Subclassing Built-ins

Question Regarding Subclassing and Type()

I have been working with the Python Advanced Objects material and there is something that I am a little puzzled by. Over the course of the last few videos, we have worked with the following:

  • In numstring.py we created NumString, which extended nothing and initialized as a str. When I check the type of my instance, I get: <class 'numstring.NumString'>

  • In reversedstr.py we created ReversedStr(str), which extended str and explicitly called the dunder new method from str. When I check the type of my instance, I get: <class 'str'> (not ReversedStr as I expected)

  • In filledlist.py we created FilledList(list), which extended list and made a super call to list's dunder init (with no arguments). When I check the type of my instance, I get: <class 'filledlist.FilledList'>

  • In javascriptobject.py we created JavaScriptObject(dict), which extended dict and we did not overload (correct term?) dunder init, so presumably it used dicts init method. When I check the type of my instance, I get: <class 'javascriptobject.JavaScriptObject'>

  • In doubler.py we created Double(int), which extended int and explicitly called the dunder new method from int. When I check the type of my instance, I get: <class 'int'> (not Double as I expected)

So... When I use the type() built-in, how come in the above examples I don't always get a 'type' that corresponds with my new class (like with NumString, FilledList, & JavaScriptObject)?

In other words, why was 'str' the type for ReversedStr, and 'int' the type for Double? Or conversely, why wasn't 'str' the type of NumString, 'list' the type of FilledList, or 'dict' the type of JavaScriptObject? It seems like in some cases, type() returns the super class, but in other cases, type() returns the sub class -- which seems confusing to me.

Am I misunderstanding how type() works? OOP is a new concept to me, so any help would be appreciated. I searched the community for a similar thread but came up empty -- I apologize if this has already been answered.

Thanks! Eric

1 Answer

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 67,982 Points

Great question! When an object is created, the __new__ method is called with the first argument being the class type, followed by any arguments passed into the creation string. For example NumString(14) translates to NumString.__new__(<class __main__.NumString>, 14). So unless self gets redefined, this class will stick as its type.

Let's look at each of the objects created:

  • NumString - Inherits from "nothing" (actually object), creates an attribute that is assigned a string. Objects assigned to attributes do not effect it's type. It's a new type so the type is the "module {dot} classname": numstring.NumString.
class NumString:
    # No __new__ method! object.__new__ is passed the class type
    # SO this will be of type NumString
    def __init__(self, value):
        self.value = str(value)
  • ReversedStr - Inherits from the class str. It uses __new__ to create the new ReversedStr object. However, the object returned comes from str slice, so its type is str
class ReversedStr(str):
    def __new__(*args, **kwargs):
        print(f"args: {args}")  # args are <class __main__.ReversedStr>, string_object)
        self = str.__new__(*args, **kwargs)  # self is a ReversedStr object!
        print(f"type: {type(self)}")  # Ah, a ReversedStr type
        self = self[::-1]  # the slice syntax returns a str object!!
        return self  # self is now a str object
  • FilledList - Inherits from the type list. It uses __new__ to create a new FilledList object. Does not alter the type, so it stays type FilledList
class FilledList(list):
    def __new__(*args, **kwargs):
        self = list.__new__(*args, **kwargs)  # self is a FilledList object!
        print(f"self: {self}, type:{type(self)}")
        # self.__init__(*args, **kwargs)
        return self
    def __init__(self, count, value, *args, **kwargs):
        print(self.__class__)
        super().__init__()
        for _ in range(count):
            self.append(copy.copy(value))
  • JavaScriptObject - Inherits from the type dict. Overrides the __getattribute__ method to first try to return a key/value pair using the attribute name as the key. If key not found in dict, then get the actual attribute from using the parent's method. The __new__ and __init__ methods are inherited from the parent. With the parent's __new__ method, the return object will have the defined JavaScriptObject class as the type.
class JavaScriptObject(dict):
    def __getattribute__(self, item):
        try:
            return self[item]
        except KeyError:
            return super().__getattribute__(item)
  • Double - inherits from int. It calls int.__new__ to create a new Double object. In the doubling (that is, the "* 2"), the __mul__ method is called which returns an new int object. So the object type is now int
class Double(int):
    def __new__(*args,**kwargs):
        self = int.__new__(*args,**kwargs)  # self is a Double 
    """* operator => calls Double.__mul__ (same as int.__mul__)
    which returns and int object. Type is now an int!!"""
        self = 2 * self
    return self  # self is now an int

So, in short, ReversedStr and Double call parent class methods that return a new version of the parent class resulting in a str and int, respectively. The others, NumString, FilledList, and JavaScriptObject do not call parent methods that return parent types.

Post back if you need more help. Good Luck!!

Chris,

THANKS for taking the time for a very thorough answer! OOP is new to me (I come from a mainframe structured programming background), and I really appreciate all the great help from folks like you out on the community forum.

Cheers, Eric

Chris Freeman
Chris Freeman
Treehouse Moderator 67,982 Points

You’re very welcome Eric! I really enjoy questions that challenge the boundaries of my knowledge!