
Objects in Python
In the world of Python, everything dons the hat of an object. Whether we're talking about numbers, strings, functions, classes, modules, or even the types themselves, each one of them is an object in its own right.
But what exactly constitutes an object? In layman's terms, an object is a unique data structure, an intricate blend of data (dubbed 'attributes') and code (referred to as 'methods'). These objects are given life, or "instantiated", via expressions in your code.
When you breathe life into an object, what you're really doing is storing a reference to that object in a variable.
Consider this: if you pen down x = 77, you're essentially crafting an integer object that cradles the value 77 and stowing away a reference to it in the variable 'x'.
x=77
This action paves the way for you to harness the variable 'x' to tap into or modify the object at a later stage.
The name of the variable "x" is merely a label that we pin to the object's reference.
Python tucks away the object in a designated nook of memory. Hence, the variable 'x' isn't home to the actual object; it simply holds a "reference" or a tether to the memory address where the object is stationed.
Decoding the Type and Identity of an Object
In Python's universe, every object prides itself on possessing a unique type and identity.
Type
The type of an object is indicative of its data type, essentially the nature of the data it encapsulates.
Each object in Python is kitted out with a specific attribute, a type descriptor if you will, where the object's data type is held.
If you're keen to delve into an object's type, the type() function is your best bet.
>>> type(x)
Another route to uncover an object's type is through leveraging the object's class attribute:
>>> x.__class__
Whichever path you choose, Python will dutifully return the object's type, for example '<class 'int'>' if the object is the variable x=7.
<class 'int'>
Identity
Contrarily, an object's identity is synonymous with its memory address. This value can be garnered using the id() function.
>>> id(x)
The id() function hands out a unique identifier, the numeric badge tied to the memory address where the object is nestled.
140551262192048
This allows you to discern whether two variables point to the same object in memory or otherwise.
You can confirm if two objects share the same identifier using the "is" operator
x is y
The operator will return True if the identifiers are identical or False if they each sport a unique identifier.
x=7
y=8
print(x is y)
In this case, it will return False as the identifiers of the objects "x" and "y" differ.
False
Bear in mind that two objects wearing different identifiers are considered separate entities. On the flip side, if two objects share an identifier, they are deemed to be the same object.
Garbage Collection
In Python, there's a neat system working behind the scenes, automatically freeing up memory whenever an object outlives its usefulness.
This kicks into gear when there's not a single reference left to an object.
Now you may ask, how does this whole garbage collection business work? Well, each object carries along a personal tally of references, tucked away in a special attribute known as the "reference count". Once this tally hits rock bottom and reads zero, it's a clear sign that the object has been retired from active use by the program. At this point, the garbage collector steps in to liberate the memory space once claimed by the object.
Let's say, for instance, you create an object and store its reference in a variable called x.
x=77
The variable name or "label" "x" now cradles a reference to the integer object 77.
With Explicit Assignment, both attributes and methods are hitched to labels that hold value only within the object's realm, offering you a pathway to access their content. Here, the label is simply the name. For instance, the attribute "my_car.color" is tagged with the label "color", whereas the method "my_car.accelerate()" is tagged with "accelerate". Assigning x=77 builds a bridge between the label "x" and the object 77.
Now imagine you assign a different value to the same variable.
x=10
This reshuffling creates a fresh bond between the label x and a new object, distinct from the previous one.
Consequently, the label "x" now points towards a new integer object, 10.
The link to the old object is severed, leaving the object 77 without any incoming references.
Hence, the garbage collector swoops in to safely sweep away the "77" object from the memory landscape, thus reclaiming the space it once occupied.
So, why do we even need garbage collection? It serves as a memory-usage optimiser, scrubbing the computer's memory clean of objects that have outstayed their welcome. This minimises the risk of tripping over a "memory overflow" error, which rears its head when all memory addresses are already taken.
Shaping an Object
Let's set things straight - objects aren't up for direct modification through simple assignment. This approach just switches up the reference between a label (you might know it as a variable name) and an object.
To truly manipulate an object, you've got to call upon the methods that the object itself provides.
But hold up! Not every object is ready to change. Take tuples, for example; these guys are immovable, steadfast in their being. On the other hand, lists are more flexible and are open to mutation.
Allow me to paint a clearer picture with a tangible example.
Suppose you've got a list comprised of three integer values.
myList = [1, 2, 3]
If you feel like adding a value to the object, you can call in the append() method.
myList.append(4)
This move modifies the object straightaway, sidestepping the creation of a new one.
Yet another method that tweaks the object is the sort() method.
myList.sort()
And if you're in the mood for more radical changes, you can wield the "del" statement, which lets you scrape away elements from within the object. Case in point:
del myList[0:1]
The Ins and Outs of Object Attributes and Methods
In the Python realm, an object is a class's offspring.
The class itself serves as a blueprint or schema that lays down the attributes and methods its objects can possess.
Attributes (or their alter ego, Properties)
The attributes are the variables tucked away within an object where you can stash information. They're also known as "properties."
The state of an object is determined by the sum of these attributes.
Picture this: you've got a "Car" class. The objects that spring from this class could possess attributes like the car's color, make, and model, among other things.
Here's a quick look at a class with some attributes:
- class Car:
- def __init__(self, color, make, model):
- self.color = color
- self.make = make
- self.model = model
Once the class is sculpted, you can bring into existence one or more instances of the Car class.
For instance, let's conjure up an object and tag it "my_car".
my_car = Car("red", "Ferrari", "488 GTB")
This object inherits all the traits of its class, be it attributes or methods.
For example, you can read off the information stored in each attribute of the object.
print(my_car.color)
In this scenario, Python scans the value saved in the "color" attribute and spits out "red."
red
In a similar vein, you can peek into the other attributes "make" and "model" of the object.
You also have the liberty to tweak the values contained in the attributes.
Methods
Methods are akin to the functional routines that belong to an object, molding its behavior.
For instance, the Car class could boast methods like accelerate(), brake(), steer(), and so forth.
Here's how you can structure a class with some methods:
- class Car:
- def __init__(self, color, make, model):
- self.color = color
- self.make = make
- self.model = model
- def accelerate(self):
- print("The car is accelerating.")
- def brake(self):
- print("The car is braking.")
Time to breathe life into an instance of the "Car" class.
my_car = Car("red", "Ferrari", "488 GTB")
The object inherits both the attributes and methods of its parent class.
Now you're free to ring up and run the object's methods.
my_car.accelerate()
Here, Python runs the script within the accelerate() method.
The car is accelerating.
You're also free to reach out to the object's other methods.
In Python, both attributes and methods of an object are at your disposal, courtesy of the dot notation (.). Suppose you've given life to an object "my_car." To pry into an attribute of the object, all you've got to do is jot down the object's name, followed by a dot and the attribute label. For instance, "my_car.color". You can tap into the methods in the same way. For instance, "my_car.accelerate()".
Labels are connected to both attributes and methods, valid solely within the object, offering access to their content. A label is, quite simply, a name.
For instance, the attribute "my_car.color" goes by the label "color," and the method "my_car.accelerate()" is known by the label "accelerate."
To skim through the labels of an object's methods and properties, you can enlist the help of the dir() function, plugging in the object's name in parentheses.
dir(my_car)
The dir function pulls up the list of labels belonging to the "my_car" object.
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'accelera', 'colore', 'frena', 'marca', 'modello']
Some labels come prepackaged with all objects, while others are those you've coined to define a class's methods and properties.
For example, the "__class__" label stores the name of the "Car" class used to mold the "my_car" object.
Labels like "accelerate", "color", "brake", "make", "model" belong to the methods and attributes you've sketched out in the "Car" class.