The data we work with in programs have essential attributes. In Python, they're built right into the language. Data also have methods — functions within properties. Properties and methods are expressions, just like variables or function calls. We can combine all of these in different ways. We cover these topics in more detail in separate courses that focus on the object-oriented features of Python. We will look at the basics of them in this lesson.
Objects
In programming, we operate with data, create numbers and strings, perform various operations on them, and use their results. We apply either operators or functions to operate:
# Adding with the `+`` operator
1 + 3 # 4
# Calculating the length with the `len()` function
name = 'Hexlet'
len(name) # 6
In the example above, there's a clear division: we separated the data and functions from each other. But this isn't the only way to organize code. In Python, there's another approach used alongside this separation, the Object Oriented approach.
Object-oriented code combines data and functions into one entity — an object. In this case, data is attributes, and functions are methods.
It is what it looks like:
name = 'Hexlet'
# upper() method
upper_name = name.upper()
print(upper_name) # => 'HEXLET'
Python strings are objects. In the example above, we are calling a method. It is a function that is associated with a string. We made the call using a period right after the variable name. Other than that, methods work like normal functions.
Also, we can make the call directly:
'Hexlet'.upper() # 'HEXLET'
There are many methods built into strings, which developers use all the time. See the documentation for a list of them. Here are some examples:
name = 'Python'
# We return the index of the first occurrence of a letter in the string
name.find('t') # 2
# We change it to the lowercase
name.lower() # 'python'
# We replace one substring with another
name.replace('on', 'off') # 'Pythoff'
The same goes for numbers and other data types we have not looked at. You could say that in Python, almost everything is an object:
x = -5
# Returns the modulus of a number
# The name looks odd, but it is the name of a real method
x.__abs__()
We see in the example above a method name with two underscores at the beginning and end. In Python, this is the name given to methods we do not usually call directly. Functions have been created for them that call methods themselves:
x = -5
abs(x) # We call x.__abs__()
# -5 to the power of 3
pow(x, 3) # We call x.__pow__(3)
The creator of Python decided that it would be more clear to express mathematical or mathematical-like operations in functions. He wanted these functions to resemble operations such as addition or subtraction. It is more familiar to those who have studied mathematics.
This is also how the len()
function works:
len('Hexlet') # We call 'Hexlet'.__len__()
In addition to methods, objects have attributes, but Python's built-in objects don't have many of them. For example, the __doc__
attribute, which returns the function documentation. Therefore, functions are also considered objects:
len.__doc__ # 'Return the number of items in a container.'
Attributes work and look like variables, only specified with a dot after the object.
Now let's talk about the immutability of data types.
Immutability
Imagine we have this call:
name = 'Tirion'
print(name.upper()) # => TIRION
# What will this call print on the screen?
print(name) # => ?
When you call the .upper()
method, it returns a new value with all letters converted to the uppercase, but it doesn't change the original string. So inside the variable will be the old value: 'Tirion'
. This logic holds for methods of all primitive types.
Instead of changing the value, you can replace it. It requires variables:
name = 'Tirion'
name = name.upper()
print(name) # => TIRION
Next, let us talk about methods.
Methods as expressions
Methods are expressions, like variables or function calls. It means we can combine them in different ways.
For example, you can use them in operations:
name = 'Shaya'
'hi, ' + name.upper() + '!' # hi, SHAYA!
Or in function arguments:
name = 'robb'
print(name.lower()) # => robb
num1 = 5
num2 = 30
# Here we see the `bit_length()` function
# It calculates the number of bits # needed to represent a number in binary form
print(num1.bit_length() + num2.bit_length()) # => 8