Everything is awesome... eh no, everything is an object

Everything is awesome... eh no, everything is an object

Introduction. Everything is an object

Everything in Python is an object, and almost everything has attributes and methods. All functions have a __doc__ attribute that returns the documentation string defined in their source code. The sys module is an object that contains (among other things) an attribute called path. And so with everything.

Still, the question remains unanswered. What is an object? Different programming languages define "object" in different ways. In some it means that all objects must have attributes and methods; in others this means that all objects can be subclassed. In Python the definition is broader: some objects have neither attributes nor methods, and not all objects can be subclassed. But everything is an object in the sense that it can be assigned to a variable or passed as an argument to a function.

id and type

In Python, every piece of data (number, string, list, etc.) that appears in a program is an object. Each object has an identifier, a type, and a value:

identifier:

Each object has a unique identifier that can be found using the id () function:

>>> id(5)   # This is the identifier of the integer object "5"

505894368

>>> id(3.14)  # This is the identifier of the decimal number object "3.14"

46522944

>>> id("hello") # This is the identifier of the string object "hello"

47533536

>>> id([3, 4]) # This is the identifier of the list object "[3, 4]"


47108264

In the Windows version of Python (programmed in C, which is why it is sometimes called CPython), the identifier of an object is its position in the memory of the computer, but it could be any other value, the only important thing is that each object have a different identifier.

type:

Each object has a type that can be found using the type () function:

>>> type(5)

<class 'int'>

>>> type(3.14)

<class 'float'>

>>> type("hola")

<class 'str'>

>>> type([3, 4])


<class 'list'>

value:

The value that the object contains is the data itself.

Mutable and inmmutable objects

that objects can change (mutable) or not (immutable) Can't the content of any object change, unless they are "final" Yes, that they are "final" means that the object is going to be a constant and not it will never change throughout the execution of the program code. Actually the value of the objects "below" change some yes and others no even though it seems that they really change.

Before we start, let's see a very simple example in Python to discover details:

my_text = "hello holberton"

We can change the value of the variable by adding something else:

my_text = my_text + "school" 

So when printing the variable on the screen:

print(my_text)

It will show:

hello holberton school

Seeing that the String value of the my_text variable can be changed, then I ask you, is Sting mutable? Well, no, String is Immutable because in RAM memory it has not expanded the "hello holberton" previously saved, but has copied it together with the addition of "school" to completely save "hello holberton school" elsewhere. from RAM, and the variable will point to the last saved value. That is, a String will always be created again (immutable) even if we believe that it is modified (false belief of mutability). Let's look at this in detail.

Immutable

The immutable are the easiest to use in programming (they are usually the simple types of data: String, Integer, Boolean, etc.) because they do exactly what they are expected to do at all times, and paradoxically to work like that they are the ones that most they punish memory (they are not optimized to take up less memory when copied, and they further degrade the useful life of memory when they are written more times).

An immutable object once created, as its name suggests, its state cannot change once it has been created. But what if I have changed String or int values many times after creating them? In appearance, but they do not change anything and I will explain it to you below (I suggest that as you read each of the next paragraphs you look at the image that accompanies it below; in the following images the "Console of Python ”with a green arrow indicating the added line of code, and then the part to the right with the memory and its occupants).

When we declare a variable (in the following image "my_variable") in Python (or any other programming language) a variable is created in memory in a memory address (in the image it will be stored in the memory address "id: 0" ). If we then assign it (the variable is a cursor or pointer that "will point to the memory address of its value") a mutable value, which for this example will be a text (String, in the image "something, something else" ) and it will be created in another memory location (in the image at memory address “id: 1”); then the above variable will point to the mutable object that contains its value.

Mutables

Mutables are objects that, once created, may change their state in the future.

Mutables are the most “complex” to use in programming (they are usually data structures such as: dict, list, etc.) and not only because they are more complex because they are structures that have things, but they tend to be related to the issue of pointers and, paradoxically, they are the least damaging to memory (they are written only once and are always reused). It must be said that mutables are designed that way on purpose, because copying an entire data structure (although it can be done) would take a long time and would imply using a lot of memory to surely not use the copy (copying a String object is not the same as copying a list with millions of String objects, and then not having needed the copy; it becomes a waste of processor and memory).

As we did before with Imutables, now we will see an example of Mutable, specifically a list of String values. The variable "my_variable" will be saved in a memory space (in the image “Id: 0”) that points to another memory position (in the image “Id: 1”) where our Immutable object of type list will be. And to start we will initialize the list with two Strings (in the image it points to “Id: 90” and “Id: 91”).

Types in Python classified into Immutable and Mutables

Inmutables:


Strings (Text)
Int (Integer)
Float (Floating point number)
Decimal (Decimal number)
Complex
Bool (Boolean)
Tuple (Tuple): It acts as an immutable list as long as its elements are Resumible ("Hashables").
Frozenset (Frozen Set): It acts as an immutable set as long as its elements are Resumible (“Hashables”)
Bytes
Range
None


Mutables:


List: Supports any type of values
Dict (Dictionary): Supports only Resumible keys (“Hashables”) and values   of any type
Set: Supports only Resumible values   (“Hashables”)
Bytearray (array of bits): Among other uses, it can be used as a mutable String.
MemoryView: Object Snap

Programmer-defined class. Learn more in the Hashable article.
No alt text provided for this image


Assignments and referrals

Consider the following case in which we assign the value 25 to the variable a:

a = 25

and then we equate b with a:

b = a

At this point both variables are assigned the same value, as expected. Next, we see what happens when lists instead of numbers are assigned to these variables.

Let's start with a:

a = [1, 2, 3, 4, 5, 6] # List of integers
b = a
a [2] = 10
print (a)

print (b)

In animation 1 we can see that both lists (a and b) are equal at the end of the execution. Also, we can see what happens to b when the value of the element corresponding to index 2 of a is changed:

No alt text provided for this image

How can this be? The answer is simple. When we assign a list to a variable, what we store in it is actually a reference to the list and not the list itself. As a consequence, when copying the variable to another, what we did was copy the reference and not a value. In this way, when modifying the list referenced in a, the change is also visible in b.

The deepcopy () function

Finally let's see how we can make the reference to a mutable element not copied to another in the above scenario. That is, the changes made to one variable will not be reflected in the other. For that we will use the deepcopy () function of the copy module as we see below:

import copy
a = [1, 2, 3, 4, 5, 6]
b = copy.deepcopy (a)
a [2] = 10
print (a)

print (b)

In Animation 2 we can see the execution of the previous code step by step:

No alt text provided for this image

It is important to note that in this case variables a and b contain references to two separate lists. That is why when altering one the changes are not observed in the other.

Integer - pre allocation of 262 integers when cpython starts

Since strings are immutable, integers appear to act the same way. To economize memory usage, CPython pre-allocates (or binds) the first 262 integers on start up. This means that numbers -5 - 256 (inclusive) are automatically bound to certain addresses in memory. That’s why a and b reference the same location in memory in the example above. Observe the following:

More technically, CPython has created macros called NSMALLPOSINTS and NSMALLNEGINTS. These macros refer to -5 and 256 respectively. CPython actually stores references to all of these integer objects in an array. When we “create” an integer in that range (Ex: a = 5), we're actually just telling our variable to point to an address stored in that array. The array is set to this particular scope because these are the most commonly used numbers. Once you ask for a number outside this range, CPython will be forced to start finding new memory locations where it can store numbers.

nssmallpoints and nsmallnegints (why have thos values)

Easy, because they are most used integers!

And this answer is from StackOverflow but is clear and to the point: To better understand how NSMALLPOSINTS and NSMALLNEGINTS work:

It is actually an array of 262 integers (most commonly used). And this structure is basically used to access these integers fast. They get allocated right when you initialize your NSMALLPOSINTS and NSMALLNEGINTS.

#define NSMALLPOSINTS 257 (257 non inclusive)

#define NSMALLNEGINTS 5

And last, an special case: What Are Frozen Sets?

Frozen sets are a native data type in Python that have the qualities of sets - including class methods - but are immutable like tuples.

To use a frozen set, call the function frozenset () and pass an iterable as the argument.

scores = {1,2}

scores.add (3) # {1,2,3}

If you pass a set to the function, it will return the same set, which is now immutable.

scores = {1,2}
frozen_scores = frozenset (scores)
frozen_scores.add (3)

# AttributeError: 'frozenset' object has no attribute 'add'


If you pass another data type such as a list, tuple, or string, then it will be treated as an iterable.

This means the value will be deconstructed into its individual parts, which will be reduced to a set of unique values that is immutable.

myList = [1,1,2,3,4]
myString = "Hello"
frozenList = frozenset(myList)
frozenString = frozenset(myString)

print(frozenList) 

# frozenset({1, 2, 3, 4})

print(frozenString) 

# frozenset({'e', 'l', 'H', 'o'})

Why Use Frozen Sets?

Honestly, there isn’t much to gain in terms of performance when choosing to use frozen sets.

The primary reason to use them is to write more clear, functional code. By defining a variable as a frozen set, you’re telling future readers: do not modify this!

Unfortunately, frozen sets cannot be declared with a character notation like sets with curly braces, lists with square brackets, or tuples with parentheses.

If you want to use a frozen set you’ll have to use the function to construct it.

So thats its, and you know now, everything is an AWESOME OBJECT.



要查看或添加评论,请登录

Aaron Valiente的更多文章

社区洞察

其他会员也浏览了