Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do you not have to return objects in Python?

I am a newbie to Python and programming in general and I have just gotten into OOP. In Python, whatever is defined in a function's namespace is only valid while inside that namespace; once outside that space things like variables are forgotten:

def foo():
    a = 3

print(a)

NameError: name 'a' is not defined

As far as I know, besides returning data from functions, any information inside the function is lost at the end of the function call, and therein lies my question. Take the following code:

class Example:

    def __init__(self, name):
        self.name = name

    def foo(self):
        self.name = 'John'

bar = Example('Jake')
bar.foo()
print(bar.name)

'John'

My question is: why don't you have to return objects after methods? In normal functions any variables are forgotten, but in methods it seems data is actually appended to the object itself, such as the foo() method being able to reference self.name despite self.name first being referenced in another method. Is this correct? Or is there a better technical explanation?

like image 630
Matt Avatar asked Nov 25 '18 06:11

Matt


People also ask

Do you have to return something in Python?

A Python function will always have a return value. There is no notion of procedure or routine in Python. So, if you don't explicitly use a return value in a return statement, or if you totally omit the return statement, then Python will implicitly return a default value for you.

What does it mean to return an object in Python?

The return statement makes a python function to exit and hand back a value to its caller. The objective of functions in general is to take in inputs and return something. A return statement, once executed, immediately halts execution of a function, even if it is not the last statement in the function.

What does not return in Python?

In Python, it is possible to compose a function without a return statement. Functions like this are called void, and they return None, Python's special object for "nothing".

Why do we use return in Python instead of print?

The print() function writes, i.e., "prints", a string or a number on the console. The return statement does not print out the value it returns when the function is called. It however causes the function to exit or terminate immediately, even if it is not the last statement of the function.


2 Answers

First, you don't have to return a value in a method, not in python, and not in many other programming languages. for example:

def append_a(lst):
    lst.append('a')

bob = []
append_a(bob)
print(bob)
['a']

Above we do not return anything in the function, but we use it to modify an existing data structure, this is very common almost anywhere.

Secondly, in your second example, you created an instance of the class Example, when you look at self.something you are looking at a member of the class, unlike other languages, where often members are only declared once, in python you can dynamically add members.
Thus when looking at bar.name you are looking at a member of the class, its value on the instance bar. If you would look at a different instance, the value will be different.

class Example:

    def __init__(self, name):
        self.name = name

    def foo(self):
        self.name = 'John'

bar = Example('Jake')
bob = Example('Bob')
bar.foo()
print(bar.name)
print(bob.name)

John
Bob
like image 108
Dinari Avatar answered Sep 24 '22 10:09

Dinari


To understand this you will need to understand how self works. You can learn more here: Understanding self in python

In a nutshell, self refers to the calling object. Invoking self.variable refers to the variable associated with the calling object. Python is smart enough to create one if it doesn't exist.

Calling self.variable inside a class is the same as calling object.variable with your object reference

Consider the following example to prove this:

class Example:
    def print_x(self):
        print(self.x)

obj = Example()
obj.x = 5;    # Create a new attribute of the object and assign it a value 5
print(obj.x)  # Outputs 5
obj.print_x() # Outputs 5

In your example, I've added a couple of print statements to help you understand the state of the program during the execution:

    class Example:
    def __init__(self, name):
        print(dir(self)) # Printing object contents before initializing name
        self.name = name # New attribute 'name' created
        print(dir(self)) # Printing object contents after initializing name

    def foo(self):
        print("Before foo, self.name = "+ self.name)
        self.name = 'John'
        print("After foo, self.name = "+ self.name)


bar = Example('Jake')
bar.foo()
print(bar.name)

The output of the above code is

['__doc__', '__init__', '__module__', 'foo']
['__doc__', '__init__', '__module__', 'foo', 'name']
Before foo, self.name = Jake
After foo, self.name = John
John

I will walk you through this code. When we first create bar, the __init__() method is called. Here we print the contents of the object using dir(self). The output ['__doc__', '__init__', '__module__', 'foo'] indicates that the object has only one member, the 'foo' method.

Now we create a new attribute called 'name' and assign it the value 'Jake'. Thus the object now has another member, the 'name' attribute as seen by the output of the next dir(self) ['__doc__', '__init__', '__module__', 'foo', 'name']

Now we call the foo method and print the value before and after the method. Before the name is changed in foo, the value of name associated with the object is "Jake". However, after the name is changed, the value of self.name is "John". This is indicated by

Before foo, self.name = Jake
After foo, self.name = John`

We next verify that the change made by changing self.name has indeed changed the value of name in bar by printing bar.name which gives us the expected output, John

Now coming back to your question, self.name is not an ordinary variable inside some method that is lost when we are out of scope. self can be used essentially anywhere inside the class to refer to the calling object (bar in this case). It is used to manipulate this calling object. Now since bar is within the scope, we are able to print its name attribute.

In normal functions any variables are forgotten but in methods it seems data is actually appended to the object itself

self manipulates the attributes in the object and is not limited to the scope of the method.

like image 21
Piyush Saravagi Avatar answered Sep 24 '22 10:09

Piyush Saravagi