Understanding objects
Key 1: Objects are language's abstraction for data. Identity, value, and type are characteristic of them.
All data and items that we work on in a program are objects, such as numbers, strings, classes, instances, and modules. They possess some qualities that are similar to real things as all of them are uniquely identifiable just like humans are identifiable by their DNA. They have a type that defines what kind of object it is, and the properties that it supports, just like humans of type cobbler support repairing shoes, and blacksmiths support making metal items. They possess some value, such as strength, money, knowledge, and beauty do for humans.
Name is just a means to identify an object in a namespace similar to how it is used to identify a person in a group.
Identity
In Python, every object has a unique identity. We can get this identity by passing an object to built-in ID function ID (object).This returns the memory address of the object in CPython.
Interpreter can reuse some objects so that the total number of objects remains low. For example, integers and strings can be reused in the following manner:
>>> i = "asdf" >>> j = "asdf" >>> id(i) == id(j) True >>> i = 10000000000000000000000000000000 >>> j = 10000000000000000000000000000000 >>> id(j) == id(i) #cpython 3.5 reuses integers till 256 False >>> i = 4 >>> j = 4 >>> id(i) == id(j) True >>> class Kls: ... pass ... >>> k = Kls() >>> j = Kls() >>> id(k) == id(j) #always different as id gives memory address False
This is also a reason that addition of two strings is a third new string, and, hence, it is best to use the StringIO module to work with a buffer, or use the join attribute of strings:
>>> # bad ... print('a' + ' ' + 'simple' + ' ' + 'sentence' + ' ' + '') a simple sentence >>> #good ... print(' '.join(['a','simple','sentence','.'])) a simple sentence .
Value
Key 2: Immutability is the inability to change an object's value.
The value of the object is the data that is stored in it. Data in an object can be stored as numbers, strings, or references to other objects. Strings, and integers are objects themselves. Hence, for objects that are not implemented in C (or core objects), it is a reference to other objects, and we perceive value as the group value of the referenced object. Let's take an example of an object iC instance of the C class with the str
and lst
attributes, as shown in the following diagram:
The code snippet to create this object will be as follows:
>>> class C: ... def __init__(self, arg1, arg2): ... self.str = arg1 ... self.lst = arg2 ... >>> iC = C("arun",[1,2]) >>> iC.str 'arun' >>> iC.lst [1, 2] >>> iC.lst.append(4) >>> iC.lst [1, 2, 4]
Then, when we modify iC, we are either changing the objects references via attributes, or we are changing the references themselves and not the object iC. This is important in understanding immutable objects because being immutable means not being able to change references. Hence, we can change mutable objects that are referenced by immutable objects. For example, lists inside tuple can be changed because the referenced objects are changing, not the references.
Type
Key 3: Type is instance's class.
An object's type tells us about the operations and functionality that the object supports, and it may also define the possible values for objects of that type. For example, your pet may be of type dog
(an instance of the dog
class) or cat (an instance of the cat
class). If it is of type dog, it can bark; and if it is type cat, it can meow. Both are a type of animal (cat
and dog
inherit from the animal
class).
An object's class provides a type to it. Interpreter gets the object's class by checking its __class__
attribute. So, we can change an object's type by changing its __class__
attribute:
>>> k = [] >>> k.__class__ <class 'list'> >>> type(k) <class 'list'> # type is instance's class >>> class M: ... def __init__(self,d): ... self.d = d ... def square(self): ... return self.d * self.d ... >>> >>> class N: ... def __init__(self,d): ... self.d = d ... def cube(self): ... return self.d * self.d * self.d ... >>> >>> m = M(4) >>> type(m) #type is its class <class '__main__.M'> >>> m.square() #square defined in class M 16 >>> m.__class__ = N # now type should change >>> m.cube() # cube in class N 64 >>> type(m) <class '__main__.N'> # yes type is changed
Note
This will not work for built-in, compiled classes as it works only for class objects defined on runtime.