 Most objects in Python have what in JavaScript we call properties, but Python calls attributes. You should recall from JavaScript that when you look up a property on an object, if the object itself does not have a property of that name, JavaScript will follow this special link from that object to another object, and it'll keep looking up the chain until it finds a property of that name. A similar thing happens in Python, except the search follows the chain of inheritance through the classes. So here, for example, we have an errant object, which is an instance of the errant class, which itself inherits from Natalie, which in turn inherits from object. If we try to get an attribute named foo from the errant object, Python first looks in the errant object, and if there is no such attribute of that name, Python then looks in errant, failing there it then looks in Natalie, and failing there it looks in object. And if object doesn't have any attribute of that name, Python will throw an exception. Things work slightly differently when we look up an attribute on a class object. If we look up an attribute foo on the errant class here, Python first of course looks in that errant class, and failing to find it there, it then looks in the Natalie class, and then it looks in the object class, but then unlike with an attribute search on a non-class object, Python will look in the type class. This makes sense because errant a class object should have all the things which type has, and so when we look up attributes on errant, we expect to find the stuff which is in the type class. This attribute search behavior is the essence of the relationship between instances in their class and classes and their parents. When I create a class errant which inherits from Natalie, the errant class and the Natalie class each have their own independent set of attributes, but when I look up something on the errant class or in an errant object, the attribute search will reach Natalie. So if the Natalie class say has a method named foo, but the errant class does not, invoking a method foo on an errant instance will invoke that method foo in the Natalie class. Likewise, if the errant class has an attribute bar which is assigned a function, then if we invoke a method bar on an errant instance, that errant instance doesn't have to have its own function bar. When the attribute search for bar doesn't find anything in the errant instance itself, it'll look to the type, it'll look to the errant class. For fields though, it's a different story. When we create instances of class, each instance should have its own fields. So say if errant objects are supposed to have a field named x, then every errant instance should have its own attribute named x. Now it's not the case that every object in Python has attributes which we can assign to and retrieve. First off, the attributes of the built-in classes such as object and type can't be modified. If you started messing with these classes, you'd very likely break the whole language. So Python simply doesn't let you do it. It's also the case that for some of the built-in types, their instances don't have any attributes at all. So for example, while the string class and the number class and the list class all have attributes, the instances produced from those classes do not. This effectively means we can never assign to an attribute of these instances because they don't have any attributes, but it doesn't mean we can't look up attributes on these instances because when we do so, the search for that attribute will of course not find the attribute in the instance itself, but it'll look for the attribute according to the normal search rules. It'll look in the class and then up the chain of inheritance. So here for example, the string literal hello, that's a string instance which we assign to the variable x. And then if we write x.foo equals 3, that's attempting to assign 3 to an attribute foo of that string instance. And that's not legal, so this will throw an exception. But it does make sense to write x.upper because upper is an attribute in the string class. So x.upper here will retrieve the value of that attribute in the string class. Recall that in JavaScript, writing x.foo is basically just a shorthand syntax for writing x and then in square brackets, a string foo. This is not the case in Python. In Python, dot is the only way to access attributes and the square bracket operator is a totally different operation. Square brackets are used to retrieve and set the key value pairs of dictionary objects. A dictionary in Python is an instance of the dict class. And when we look up values in a dictionary, there is no concept of a search order. The search is done just in that dictionary itself, not in any other object. So the intention in Python is that you don't just use any object with attributes as a grab bag in which to stuff a bunch of key value pairs. For that purpose, you're supposed to use a dictionary. It's really kind of weird how JavaScript does it, where these objects are used both as collections and then also to represent data types organized into a hierarchy of inheritance. It's clever how they only have the one simple mechanism to cover both those bases, but it's just weird. To make what's going on in Python clearer, let's look at dictionaries. First off, to create a dictionary, we can simply invoke the dictionary class called dict. This returns a new empty dictionary. More commonly, however, we create dictionaries with curly braces, just like we create objects in JavaScript. So here this empty pair of curly braces, that returns a new empty dictionary. And then the second pair of curly braces has two key value pairs. The first with the key string of foo and the value three, the second with the key number four, and the value 12. Two differences from JavaScript to note here. The first is that the key string foo must be written explicitly as a string. We can't just leave off the quote marks as we can in JavaScript. If we were to just write the identifier foo here, that would be the variable foo. So the key value pair here would be the value three, with the key of whatever is in the variable foo when this dictionary was created. The other difference from JavaScript is that Python dictionaries can have keys which aren't strings. So whereas in JavaScript writing four colon 12 would represent a key value pair of the string with just the character for here in Python, this is a key which is actually the number value four. So as mentioned, to retrieve the values from a dictionary or to set key value pairs, we use the subscript operator, the square brackets. Here we're creating a dictionary with three key value pairs and assigning it to x. And then in the next line, the expression x subscript 56 retrieves the value of the key 56, which here is 11. And then in the next line, when we write x subscript string orange and assign it 14, that is assigning a new key value pair with the key of the string orange and the value 14. And then in the last line, we're assigning another key value pair. This time the key is negative 30. And the value is a new empty dictionary. So to reiterate the subscript operator, the square brackets is used just to set and retrieve key value pairs from dictionaries, it is not used to retrieve or set attributes of objects. However, the subscript operator is also used with lists to get and set the items of that list. So very briefly, we can create a new list object by invoking the list class that returns a new empty list, or more commonly, we just used square brackets just like we did in JavaScript to create what it calls arrays. Here we are assigning the variable x a new list with three items first 67, then none, and then a string George Washington. And then the next line, the expression x subscript zero, that returns the first item 67 in this case. And then if we write x subscript two equals the string hello, that is reassigning the third slot in the list. So previously it had the string George Washington, now it has the string hello. Unlike with JavaScript arrays, we can't assign out of bounds on a Python list. So here when we write x subscript three equals nine, that's going to trigger an exception, because index three would be the fourth item in this list. This list we created only has three items. If we want to add a fourth thing to this list, we have to first expand the list. And there are methods in the list class for doing that, which we won't cover here. We'll get to that stuff in the supplementary material.