Python: the thing good Objects come in

twix bar class with two instances

Object-Oriented programming is a programming paradigm that revolves round these things called “Objects” which are just data blocks that contain, twix bars. I mean data. For sure data.

Python….

Python is at it’s core, an object-oriented programming language. There are different implementations of Python, but I’ll be referencing the popular C implementation, CPython. More specifically, v3.4.

In Python, all object types inherit from one master object, declared as PyObject . This master object has all of the information Python needs to process a pointer to an object as an actual object.

PyObject

Let’s take a look at the CPython implementation of the PyObject struct.

PyObject declaration

Breakdown:

_PyObject_HEAD_EXTRA :
- This is a macro for a doubly linked list pointing to a next
_object and a _prev object
- According to the documentation, these two field are only
present when the macro Py_TRACE_REFS is defined
Py__ssize_t ob_refcnt :
- references count of the object, initialized to 1
struct _typeobject *ob_typeob_type :
- The type's type. Or as the python documentation put's it, a
metatype.

ob_refcnt, id(), and type()

To get the reference count of an object, we can use the built in id() function which returns the memory address of the value. Then we can use the ctype library to get the ob_refcnt value of an object.

The id() function gets the memory address of the value, but as seen it is not the memory address in hex form but a Python Long object. That’s what the PyLong_FromVoidPtr() does.

Here’s an example:

Breakdown

import ctypes :
- this import is to be able to to use the c_long.from_address
function which retrieves a number from an address
twix_count :
- a variable set to entice Julien
twix_address :
- a variable to store the id of the enticing twix_count var
hex(twix_address) :
- when converted to hex, you can see the address of the object
ob_refcnt = ctypes.c_long.from_address(twix_address)
- this uses the function mentioned above

The return value, as you can see, is one. Because as mentioned previously, the ob_refcnt is initialized to 1.

Let’s now reference another variable to twix_count .

As you can see, after assigning another variable to twix_count the ob_refcnt of the object incremented to 2.

Note:

The type() function. Like id() types is a built in function that checks the ob_type of a PyObject struct to determine the type of an object.

This brings up a very interesting question with variable assignment.

Referencing

When I first started my twix_count demo, I used a smaller number. The number 10 to be precise. But there was some “odd behavior” with retrieving the ob_refcnt .

Why would the ob_refcnt count be 15? I restarted the python3 interpreter and restarted my computer but still, the number was 15.

After doing some research on the interwebs, I found that python3 keeps an array of integer objects in the range of and including -5 to 256 . So when you assign a variable to any number in that range, you are actually just referencing the existing object.

Immutable Data Types

An immutable data type is an object that cannot be manipulated, such as an int , float , string , tuple , just to name a few. You can see a full list of immutable data types at this link.

If you pass an immutable object to a function or method, you are not able to mutate the object. Furthermore, you are also not allowed to rebind the outside reference.

There’s an example

def twix_ownership_change(person):
person = 'Ju'
print('{:s} tries to take the twix'.format(person))
twix_owner = "Bobby"
print("Currently: This twix belongs to {:s}".format(twix_owner))
twix_ownership_change(twix_owner)
print('Now, This twix still belongs to {:s}'.format(twix_owner))

The output should be:

Currently: This twix belongs to Bobby
Ju tries to take the twix
Now, This twix still belongs to Bobby

Because a string is an immutable object, although the string was change within the scope of the function twix_ownership_change() , it was not affected on the outside.


Mutable Data Types

A mutable data types is an object that can be manipulated, such as a list , dictionary, set , just to name a few. Once again, you can see a full list of immutable data types at the same link above.

If you pass a mutable object into a function or method, because of the way arguments are passed in, the function receives a reference to the same object . Because of the function is referencing the same object, you are able to mutate the same object within the function. However, if you reassign or rebind the reference inside of your function, the outside reference will still be pointing at the original reference.

Here’s an example:

def twix_ownership_change(ownership_list):
ownership_list.append('Bobby')
print(ownership_list)
outside_ownership_list = ["The", "twix", "belongs", "to"]
print(outside_ownership_list)
twix_ownership_change(outside_ownership_list)
print(outside_ownership_list)

The output should be:

['The', 'twix', 'belongs', 'to']
['The', 'twix', 'belongs', 'to', 'Bobby']
['The', 'twix', 'belongs', 'to', 'Bobby']