Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialericfromportland
Courses Plus Student 3,400 PointsQuestion 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
Treehouse Moderator 68,457 PointsGreat 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 thetype
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 newReversedStr
object. However, the object returned comes fromstr
slice, so itstype
isstr
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 newFilledList
object. Does not alter the type, so it stays typeFilledList
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 definedJavaScriptObject
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 callsint.__new__
to create a newDouble
object. In the doubling (that is, the "* 2
"), the__mul__
method is called which returns an newint
object. So the object type is nowint
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!!
ericfromportland
Courses Plus Student 3,400 Pointsericfromportland
Courses Plus Student 3,400 PointsChris,
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
Treehouse Moderator 68,457 PointsChris Freeman
Treehouse Moderator 68,457 PointsYou’re very welcome Eric! I really enjoy questions that challenge the boundaries of my knowledge!