DEV Community

Arindam Dawn
Arindam Dawn

Posted on • Originally published at tabandspace.com

30 Days of Python 👨‍💻 - Day 6 - Loops II & Functions

The joy of coding Python should be in seeing short, concise, readable classes that express a lot of action in a small amount of clear code -- not in reams of trivial code that bores the reader to death.
Guido van Rossum

Starting off from where I left, I continued exploring more loops while playing around with some code. Then I moved on to learning one of the most important things of programming, functions. I came across functions and methods in Python, docstrings, best practices along with scopes in detail. I will try my best to explain in lucid and simple terms.

Loops Continued

While Loops

While loops are another way to run a block of code several times based on a specific condition. We need to be a bit careful when dealing with while loops to accidentally not create an infinite loop that would keep executing till they crash our systems!

hungry = True
while(hungry): # This is always true so it keeps printing until system crashes!
  print('Give me something to eat!!')
Enter fullscreen mode Exit fullscreen mode

But while loops are cool. They are simple to write and easy to read. We just need to tell the interpreter when to stop the loop. It can be either done by using a break statement which stops the loop execution and breaks out of the loop. Another way is to make the condition falsy to stop the execution.

hungry = True
while(hungry):
    print('Give me something to eat!!')
    break # prints only once and stops the loop execution
Enter fullscreen mode Exit fullscreen mode
hungry = True
satisfaction = 0
while(satisfaction < 10):
  satisfaction += 1
  print('Give me something to eat!!') # prints 10 times
Enter fullscreen mode Exit fullscreen mode

Another feature of while loop is that it can be combined with an else block.

hungry = True
satisfaction = 0
while satisfaction < 10:
  satisfaction += 1
  print('Give me something to eat!!')
else:
    print('I am super satisfied now') # gets printed once condition is falsy
Enter fullscreen mode Exit fullscreen mode

It needs to be noted that if the break statement is used in the while block, then the else block is not executed.

While vs For Loop

For loops are usually useful when knowing the range of the iterable that needs to be looped. Whereas while loops can come in handy when we want to perform some task multiple times not knowing the range beforehand.

while True:
  response_from_user = input('Enter some message. Enter bye to exit')
  if(response_from_user == 'bye'):
    break
Enter fullscreen mode Exit fullscreen mode

Apart from the break statement, there are two other statements, continue which instead of breaking out of the loop continues to continue the iteration. pass is not often used but it is sometimes used as a placeholder to continue executing the statements after the loop.
This stack-overflow thread provides a great explanation

Quick Coding Exercise

Let's find the duplicate emails in a list of emails and print them.

email_list = ['roger@hey.com','michael@hey.com','roger@hey.com','prince@gmail.com']
duplicate_emails = []
for email in email_list:
  if email_list.count(email) > 1 and email not in duplicate_emails:
    duplicate_emails.append(email)
print(duplicate_emails)
Enter fullscreen mode Exit fullscreen mode

That was my approach to solve the problem. Let me know how you would have solved it in the comments. Alright! Time to switch to an interesting topic, function.

Functions

Functions are a very important concept and they are present in all programming languages. Functions allow us to define an action(block of code) and then execute the action any number of times without repeating ourselves following the principle of DRY. Up until now, I have been using some of the in-built functions provided by Python such as print, input, len etc. It's time to create one.

def blow_fire(): # This part is called function definition
  print('fire 🔥 🔥 🔥')

blow_fire() # This part is called function invocation
blow_fire() # It can be called any number to times to perform some action
Enter fullscreen mode Exit fullscreen mode

Arguments and Parameters

The above function was kind of cool but it has some limitations as well. It can perform only the same action. Let's make it more extensible and make it perform actions at will by passing some data to it

def blower(name, emoji): # parameters
  print(f'{name} {emoji} {emoji} {emoji}')

blower('fire', '🔥') # arguments
blower('water', '🌊')
Enter fullscreen mode Exit fullscreen mode

While defining a function, when we provide some data to it to perform some action based on the data, the provided data is called a parameter. A function can be provided with any number of parameters.

When the same function is called or invoked to perform the action, it accepts the data as arguments. It is just terminology but it often confusing and used interchangeably.

The above is also called positional parameters or positional arguments because of the order or position of the parameters or arguments matter.

def blower(name = 'fire', emoji = '🔥'): # default parameters
  print(f'{name} {emoji} {emoji} {emoji}')

blower('fire', '🔥') # arguments
blower('water', '🌊')
blower() # fire 🔥 # this works as the function has default parameters defined
Enter fullscreen mode Exit fullscreen mode

Return

return is a keyword in Python that is used to return a value from a function. To makes functions more useful, it needs to return some value based on evaluating an expression. If no return statement is specified or the expression of the return statement does not evaluate to a data type, then the function returns None. In JavaScript world, this None can be linked to void.

The return statement terminates the function and exits out of it.

def multiplier(num1, num2):
  return num1 * num2

result = multiplier(2,3)
print(result) # 6
Enter fullscreen mode Exit fullscreen mode

Time to do something cool using the return statement.

def sum(num1):
  def child(num2):
    return num1 + num2
  return child

add_10 = sum(10)
print(add_10(20)) # 30  (Closure!!!)
print(add_10(50)) # 60
print(add_10(100)) # 110
Enter fullscreen mode Exit fullscreen mode

Yay! I just validated that there is the concept of closure in Python as well just like in JavaScript. It is so effective in creating factory functions. In the above block of code, I was able to create a generic function add_10 and pass dynamic arguments to it to generate a different result. How cool is that!

Will explore more about this next week when learning about the functional programming concepts in Python.

Methods are simply functions that are defined inside an object or in other terms, they are 'owned' by the objects. They are called using the object name followed by the . operator to execute or invoke them.

Docstrings

When writing custom functions, it would be very helpful if the objective of the function is mentioned so that other co-workers or users of the code can easily understand what the function does while using them. The IDE would show the info of the function. Also, there are some helper functions and methods to view the information about a function if present.

“Code tells you how; Comments tell you why.”
— Jeff Atwood (aka Coding Horror)

def test_function():
  '''
  This function just prints test to the console.
  '''
  print('test')

test_function()
Enter fullscreen mode Exit fullscreen mode

“Code is more often read than written.”
— Guido van Rossum

https://www.programiz.com/python-programming/docstrings

*args and **kwargs

*args is the short form of arguments and **kwargs stands for keyword arguments. These are used when a function needs to accepts any number of arguments or keyword arguments.

def powerful_function(*args):
  print(args)

powerful_function(1,3,2) # (1, 3, 2) --> Returns a tuple

def super_function(**kwargs):
  print(kwargs)

super_function(name='John', age='45') # {'name': 'John', 'age': '45'} --> Returns a dict
Enter fullscreen mode Exit fullscreen mode

https://www.programiz.com/python-programming/args-and-kwargs

In the JavaScript world, the ES6 rest parameters is used to extract all the arguments passed to the function. Another mental model created!

Scope

Scope in simple terms means "Which variables do I have access to ?". This is the kind of question the interpreter asks when reading the code to find the scope of variables. In Python, variables have function scope which means variables defined inside a function cannot be accessed outside of the function.

num = 1
def confusing_function():
    num = 10
    return num

print(num) # 1 => Global Scope
print(confusing_function()) # 10 => Local Scope
Enter fullscreen mode Exit fullscreen mode

These are the scope rules that the Python interpreter follows:

  • Start with local. Is the variable present? Then fetch the value. If not then proceed
  • Is the variable defined in the parent function local scope? Fetch value if present else proceed
  • Is the variable present in the global scope? Fetch value if present else proceed
  • Is the variable a built-in function? Fetch value else exit

Read more about Scope in Python

This is also a great article on scope

That's it for today. I have been able to cover most of the Python basics now. It's time to dig deeper into the advanced topics but before that, I would like to explore and set up the developer environment and all the different kinds of tooling and programs available tomorrow to complete the roadmap for the first week.

I hope I tried to explain my learning process about the language in easy to understand words. Please let me know in the comments if something felt confusing or if you have any other suggestions, thoughts.

Have a great one!

Top comments (5)

Collapse
 
matteodellirocioli profile image
MatteoDelliRocioli

Hey, I came up with this odd solution. It's obviously not as clean as yours but I'm trying to apply some concepts you have explained in the previous articles, so there you go :)

email_list = ['roger@hey.com','michael@hey.com','roger@hey.com','prince@gmail.com']
email_set = set(email_list)
duplicate_dict = dict.fromkeys(email_set, 0)

if (email_list.__len__() > 1):
  for email in email_list:
    if (email in email_set):
      duplicate_dict[email]+= 1

  for key in duplicate_dict.keys():
    if (duplicate_dict[key] > 1):
      print(f'"{key}"')

What do you guys think about it?

Collapse
 
arindamdawn profile image
Arindam Dawn

Nice try :) but as Adrian suggested it is not recommended to use dunder methods unless you are planning to override some built-in utility function in a class.

Let me provide an alternate one-line solution using comprehensions which I discovered lately :)

email_list = ['roger@hey.com','michael@hey.com','roger@hey.com','prince@gmail.com']
duplicates = list(set([email for email in email_list if email_list.count(email) > 1 ]))

print(duplicates)

Comprehensions look way cool but they can impact the code readability.

Collapse
 
matteodellirocioli profile image
MatteoDelliRocioli • Edited

Very interesting indeed!

Noice

Collapse
 
mowat27 profile image
Adrian Mowat • Edited

You should use len(email_list) instead of _ _ len() _ _ (excuse the spaces, the text goes bold if I leave them out). The _ _ method is really an internal method len() looks for on a class. There are lots of them, actually check out the special methods section the docs for info.

Collapse
 
matteodellirocioli profile image
MatteoDelliRocioli

Thanks for the hint! 😄