Python ~ Everything is Object
From the time I started learning about Python and reading some tutorials, I have seen the sentence saying that?Everything is an object in Python; what does it mean?
An Object is?an instance of a Class. A class is like a blueprint while an instance is a copy of the class with actual values. Python is the object-oriented programming language that stresses objects i.e. it mainly emphasizes functions.
It means that?Everything from an int to a string to a function to a class is an object with methods, "constuctors" and the whole shebang. try help(int) in an interpreter. that whole (almost) list is functions you can run. for example, x.__float__() is the same as float(x)
id() function in Python
id() is an inbuilt?function in Python. As we can see the?function?accepts a single parameter and is used to return the?identity?of an object. This?identity?has to be unique and constant for this object during the lifetime. Two objects with non-overlapping lifetimes may have the same?id() value.
type() Method and Function
type()?method?returns the class?type?of the argument(object) passed as a parameter.?type()?function?is mostly used for debugging purposes. Two different?types?of arguments can be passed to?type()?function, single and three arguments. If the single argument?type(obj) is passed, it returns the?type?of the given object.
Mutable and Immutable objects
Simple put, a?mutable object?can be changed after it is created, and an immutable?object?can’t.?Objects?of built-in types like (int, float, bool, str, tuple, unicode) are immutable.?Objects?of built-in types like (list, set, dict) are?mutable.
let's create an example:
a = 10
b = y
We are creating an object of type int. identifiers a and b points to the same object.
id(a) == id(b)
id(b) == id(10)
if we do a simple operation.
a = a + 1
Now:
id(a) != id(b)
id(a) != id(10)
The object in which a was tagged is changed. object 10 was never modified.?Immutable objects don’t allow modification after creation
In the case of?mutable objects
l = list([1, 2, 3])n = l
We are creating an object of type list. identifiers l and l tagged to the same list object, which is a collection of 3 immutable int objects.
id(l) == id(n)
Now popping an item from the list object does change the object,
l.pop()
object id will not be changed
id(l) == id(n)
l and n will be pointing to the same list object after the modification. The list object will now contain [1, 2].
So what have we seen so far from the above examples?
Exceptions in immutability..
Not all of the immutable objects are actually immutable. Confused? Let me explain.
As discussed earlier, Python containers like tuples are immutable. That means the value of a?tuple?can't be changed after it is created. But the "value" of a tuple is, in fact, a sequence of names with unchangeable bindings to objects. The key thing to note is that the?bindings?are unchangeable, not the objects they are bound to.
Let us consider a tuple?t = (‘list_numbers’, [1, 2, 3])
The above tuple?t?contains elements of different data types, the first one is an immutable string and the second one is a mutable list. The tuple itself isn’t mutable. i.e. it doesn’t have any methods for changing its contents. Likewise, the string is immutable because strings don’t have any mutating methods. But the list object does have mutating methods, so it can be changed. This is a subtle point, but nonetheless important: the “value” of an immutable object?can’t?change, but it’s constituent objects?can.
How objects are passed to Functions
It's important for us to know the difference between mutable and immutable types and how they are treated when passed onto functions. Memory efficiency is highly affected when the proper objects are used.
For example, if a mutable object is called by reference in a function, it can change the original variable itself. Hence to avoid this, the original variable needs to be copied to another variable. Immutable objects can be called by reference because its value cannot be changed anyways.
def update_List(list1):
list1 += [10]n = [5, 6]
print(id(n)) # 140312184155336updateList(n)
print(n) # [5, 6, 10]
print(id(n)) # 140312184155336
As we can see from the above example, we have called the list via?call by reference, so the changes are made to the original list itself.
Let's take a look at another example:
def update_Number(n):
print(id(n))
n += 10b = 5
print(id(b)) # 10055680
update_Number(b) # 10055680
print(b) # 5
In the above example, the same object is passed to the function, but the variable's value doesn’t change even though the object is identical. This is called?pass?by value. So what is exactly happening here? When the value is called by the function, only the value of the variable is passed, not the object itself. So the variable referencing the object is not changed, but the object itself is being changed but within the function scope only. Hence the change is not reflected.
Assignments
There exist two types of assignment plain and augmented. Plain assignment to a variable is when a variable is assigned a value. Object. attributes can also be plainly assigned.
plain_variable = "Plain_string"
plain_object.attribute = "Another Plain String"
Augmented assignment can rebind a variable, ask an object to rebind one of its existing attributes or items, or request the target object to modify itself.
plain_variable = 1
augmented_assignment = plain_variable + 1
An?alias?is a second name for a piece of data.
a = 1
b = 1
a and b reference the same physical memory in this case.
Python assigns variables with the same value to the same memory location and so both a and b are referencing the same physical 1. So, a and b are aliases of each other. This is not true for lists, however.
a = [1, 2, 3]
b = [1, 2, 3]
a and b do not reference the same physical memory in this case.
Comparisons
To know if two variables’ values are identical(output is True) or not(output is False):
>>>> a = 1
>>>> b = 1
>>>> a == b
True
>>>> b = 2
>>>> a == b
False
To know if two variables are linked to the same object(True means yes, False means no):
领英推荐
>>>> a = [1, 2, 3]
>>>> b = [1, 2, 3]
>>>> a is b
False
>>>> a == b
True
a and b are referencing separate objects whose values are the equal.
To display a variable’s identifier (which is the memory address in the CPython implementation) use the id() function:
>>>> id(variable_name)
<the address will print here>
how arguments are passed to functions and what does that imply for mutable and immutable objects
Function parameters make it possible to use all kinds of objects from outside the scope of the function. Parameters are inside functions, while arguments are used in function calls, i.e. the values passed to the function at run-time.
function_call(argument)
def function_call(parameter):
Python uses a parameter-making mechanism known as “Call-by-Object”.
Passing variables that reference immutable like integers, strings or tuples to a function will actually pass the object reference as parameters. These objects can’t be changed within the function, because they can’t be changed.
—
It’s different when passing mutable arguments. They are also passed by object reference, but they can be changed in place in the function.
Let’s look at integer variables. The parameter in the function is a reference to the variable, as long as the parameter is not changed. If a new value is assigned to it, Python creates a separate local variable. The caller’s variable will not be changed this way:
def function(parameter):
print("param=",parameter ," id=",id(parameter))
parameter=42
print("local_param=",parameter ," id=",id(parameter))
id(parameter) returns the memory location of the parameter. Two different objects with non-overlapping lifetimes may have the same id() value.
If you call the function () we can see the id() of the object assigned to the parameter. After assigning the value 42 to the parameter, the parameter is assigned a new memory location. When we are back outside of the function, whatever variable we passed in for function() remains the same.
This shows that Python initially behaves like call-by-reference, but the value of a variable is changed a new object will be assigned, and Python behaves like to call-by-value, which is when parameters are separate objects than the input arguments but have the same value as the input arguments.
>>> x = 9
>>> id(x)
9251936
>>> function(x)
param= 9 id= 9251936
local_param= 42 id= 9252992
>>> x
9
Python is pass-by-assignment
Having laid out all of this, we are now ready to understand how Python passes arguments to functions.
When we call a function, each of the parameters of the function is assigned to the object they were passed in. In essence, each parameter now becomes a?new?nickname to the objects that were given in.
?In Pass by value,?a function is called by directly passing the value of the variable as an argument. So any changes made inside the function do not affect the original value. In Pass by value, parameters passed as arguments create their own copy.
Not only can you pass a parameter value into a function, a function can also produce a value. You have already seen this in some previous functions that you have used. For example,?len?takes a list or string as a parameter value and returns a number, the length of that list or string.?range?takes an integer as a parameter value and returns a list containing all the numbers from 0 up to that parameter value.
Functions that return values are sometimes called?fruitful functions. In many other languages, a function that doesn’t return a value is called a?procedure, but we will stick here with the Python way of also calling it a function, or if we want to stress it, a?non-fruitful?function.
Integer pre-allocation
The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object.
Which makes this comparison true:
>>> a = -3
>>> b = -3
>>> a is b
True
This is because the implementors of CPython have decided that that's a good range to have preallocated for performance reasons, as it covers the most commonly used integer values. There's nothing magical about the range?[-5,256]. The few negative numbers are probably included in the range for common error codes and negative indexing of lists, and the upper limit was just set to a nice, round power of two.
Mechanism of aliases
Since variables refer to objects, if we assign one variable to another, both variables refer to the same object:
>>> a = [1, 2, 3]
>>> b = a
>>> a is b
True
In this case, the state diagram looks like this:
Because the same list has two different names,?a?and?b, we say that it is?aliased. Changes made with one alias affect the other:
>>> b[0] = 5
>>> print a
[5, 2, 3]
In python programming, the second name given to a piece of data is known as an alias.?Aliasing happens when the value of one variable is assigned to another variable because variables are just names that store references to the actual value.
In this case, the reference diagram looks like this:
Because the same list has two different names,?a?and?b, we say that it is?aliased. Changes made with one alias affect the other. In the codelens example below, you can see that?a?and?b?refer to the same list after executing the assignment statement?b?=?a.
?NSMALLPOSINTS,?NSMALLNEGINTS
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.
Python source code for NSMALLPOSINTS and NSMALLNEGINTs
It turns out Python keeps an array of integer objects for “all integers between -5 and 256”. When we create an int in that range, we’re actually just getting a reference to the existing object in memory.
If we set x = 42, we are actually performing a search in the integer block for the value in the range -5 to +257. Once x falls out of the scope of this range, it will be garbage collected (destroyed) and be an entirely different object. The process of creating a new integer object and then destroying it immediately creates a lot of useless calculation cycles, so Python preallocated a range of commonly used integers.
The difference between frozen set and tuple
tuples are immutable lists, frozen sets are immutable sets. frozen sets aren't indexed, but you have the functionality of sets - O(1) element lookups, and functionality such as unions and intersections. They also can't contain duplicates, like their mutable counterparts.
tuples?are immutable?lists,?frozen sets?are immutable?sets.
tuples?are indeed an ordered collection of objects, but they can contain duplicates and unhashable objects and have slice functionality
frozen sets?aren't indexed, but you have the functionality of?sets?- O(1) element lookups, and functionality such as unions and intersections. They also can't contain duplicates, like their mutable counterparts.
One difference that comes to mind is the issue of duplicates. A tuple of?(1, 1, 1, 1, 2, 2, 2)?would be exactly what you expect, but a frozen set would remove all of those duplicates, leaving you with?the frozen set([1, 2]).
15