we do “things” with “stuff”

Malek Salem
6 min readMar 21, 2021

A look into python programming language structure, “Everything is an object”.

Introduction

In an informal sense in Python, we do things with stuff. “Things” take the form of operations like addition and concatenation …, and “stuff” refers to the objects on which we perform those operations. Somewhat more formally in Python, data takes the form of objects, either built-in objects that Python provides or objects we create using Python classes or external language tools such as C extension libraries.

Objects are essentially just pieces of memory with values and sets of associated operations, A cup is a stronger resemble of an object, Cups can hold tea or coffee, an object can store data in variables, Cups have several functions, which is similar to object methods.

Id and type

In Python everything is an object: every integer, string, list, and function.

When we assign a value to a variable we are actually binding a name to an object.

One implication of this is that multiple names can be bound to a single object. Here is a concrete example of that:

# <name> = <object>
>>> a = "Hello"
>>> b = "Hello"
>>> print(id(a))
1400...80
>>> print(id(b))
1400...80
>>> print(a is b)
True

The id function returns the actual memory location where the variable is stored.

Since id(a) = id(b), we know that a and b are both pointing to a single variable that resides in a single memory location.

This behavior occurs because strings are immutable, and that’s why python memory manager optimizes resources by making two names that refer to the same string value, refers to the same object. This is what we mean by “multiple names bound to a single object”. In a more formal way, it’s called Shared Reference which means that a single object can be referenced by multiple variables.

If you’ve used lower-level languages such as C or C++, you know that much of your work centers on implementing objects also known as data structures to represent the components in your application’s domain. You need to layout memory structures, manage memory allocation, implement search and access routines, and so on.

These chores are about as tedious and error-prone as they sound, and they usually distract from your program’s real goals. In typical Python programs, most of this grunt work goes away. Because Python provides powerful object types as an intrinsic part of the language.

Python’s built-in object types and some of the syntax used to code their literals
print(type(4))
>> <class 'int'>
print(type(4.2))
>> <class 'float'>
print(type("3"))
>> <class 'str'>
print(type( [3, 4, 5] ))
>> <class 'list'>
print(type( (3, 4, 5) ))
>> <class 'tuple'>

The type function either returns the type of the object or returns a new type object based on the arguments passed.

Immutability

Once an object is created and initialized, it cannot be modified.

Every object in Python is classified as either immutable (unchangeable) or not. In terms of the core type, numbers, strings, frozen sets, and tuples are immutables. This type of object doesn’t support in-place changes, the only operations allowed for immutable objects are calling the accessor methods “methods used to obtain information about an object”, copying the objects, or passing them around. though we can always run expressions to make new objects and assign their results to variables as needed.

For example, you can’t change a string by assigning it to one of its positions, but you can always build a new one and assign it to the same name. Because Python cleans up old objects :

>> S = 'Spam'
>> S[0] = 'z' # Immutable objects cannot be changed
...error text omitted...
TypeError: 'str' object does not support item assignment
>> S = 'z' + S[1:] # An expression to make new object
>> S 'zpam'

Mutability

Conversely, the mutable types (lists, dictionaries, sets, byte-arrays) can always be changed in place, with operations that don’t create new objects.

For objects that support such in-place changes, you need to be more aware of shared references, since a change from one name may impact others. Otherwise, your objects may seem to change for no apparent reason.

To illustrate, let’s take look at the list-objects.

>>> L1 = [2, 3, 4]
>>> L2 = L1

L1 here.. is a list containing objects 2, 3, and 4.

Items inside a list are accessed by their positions, so L1[0] refers to object 2, which is the first item in the list L1.

After running the two prior assignments, L1 and L2 reference the same shared object (same as a and b in the image below).

To variables sharing references to the same object

Now let’s extend this interaction to say the following:

>>> L1 = 24

This assignment simply sets L1 to a different object, and L2 still references the original list. But if we change this statement’s syntax slightly, however, it has a radically different effect:

>> L1 = [2, 3, 4]  # A mutable object 
>> L2 = L1 # Make a reference to the same object
>> L1[0] = 24 # in-place change
>>> L1 # L1 is different [24, 3, 4]
>> L2 # But so is L2! [24, 3, 4]

We haven’t changed L1 itself here, we’ve changed a component of the object that L1 references. This sort of change overwrites part of the list object’s value in place because the list object is shared by other variables. That's why you must be aware, that when you make such changes they can impact other parts of your program.

As you see in this example, the effect shows up in L2 as well because it references the same object as L1.

This behavior only occurs for mutable objects that support in-place changes and is usually what you want, but you should be aware of how it works so that it’s expected. If you don’t want such behavior, you can request Python to copy objects instead of making references.

Mutability vs Immutability

You might think that mutability seems like an innocuous topic, but when writing an efficient program it is essential to understand. For instance, the following code is a straightforward solution to concatenate a string together:

string_build = ""
for data in container:
string_build += str(data)

But in reality, this is very inefficient. If you want to write the most efficient code, you should know the differences between mutable and immutable objects. Since strings are immutable, concatenating two strings together actually creates a third-string which is the combination of the previous two. If you are iterating a lot and building a large string, you will waste a lot of memory creating and throwing away objects. Also, at the end of the iteration, you will be allocating and throwing away very large string objects which is even more costly.

Python handles mutable and immutable objects differently. Immutables are quicker to access than mutable objects. Also, immutables are fundamentally expensive to “change”, because doing so involves creating a copy but changing mutable objects is cheap.

Mutable objects are great for efficiently passing around data. Let’s say object X and Y have access to the same list object. X adds “something” to the list, and so on Y automatically has access to this information. But if both were using tuples, then X would have to copy the entries of his tuple, add the new element, create a new tuple, then send that to Y. Even if both can talk directly, that is a lot of work.

You should generally use mutable objects when you need to deal with growing data. For example, when parsing a file, you may append information from each line to a list. Custom objects are usually mutable, buffering data, adjusting to new conditions, and so on.

In general, whenever something can change, mutable objects are much easier.

Python Parameters Mechanism

In C, and other language values are passed to functions by two widely known concepts “Pass by Value” and “Pass by Reference”, But in Python Neither of these two concepts are applicable.

Python uses a parameter-making mechanism known as “Call-by-Object”. Passing variables that reference mutable/immutable objects to a function will actually pass the object reference as parameters.

  • Immutable arguments are effectively passed “by value.” Objects such as integers and strings are passed by object reference instead of copying, but because you can’t change immutable objects in place anyhow, the effect is much like making a copy.
  • Mutable arguments are effectively passed “by-pointer”. Objects such as lists and dictionaries are also passed by object reference, which is similar to the way C passes arrays as pointers.

--

--