Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lalunamel/485bb160c88cb043b0c39f9673583c32 to your computer and use it in GitHub Desktop.
Save lalunamel/485bb160c88cb043b0c39f9673583c32 to your computer and use it in GitHub Desktop.

Learning Python as a Ruby Developer

Contents

Lesson 1: Python Syntax and Semantics

  • Introduction to Python and its syntax
  • Differences between Ruby and Python syntax
  • Basic variable declaration, types, and operations in Python
  • Comparing data structures in Python and Ruby: Lists, Tuples, Dictionaries (HashMaps)

Lesson 2: Python Conditionals and Loops

  • Introduction to If-Else statements in Python
  • Understanding For and While loops in Python
  • Comparing Python's conditionals and loops to Ruby's

Lesson 3: Python Functions

  • Declaring and calling functions in Python
  • Differences between Python and Ruby functions
  • Understanding Python's way of defining and handling function arguments

Lesson 4: Python Classes and Objects

  • Understanding classes and objects in Python
  • Differences in class declaration, inheritance, and encapsulation between Python and Ruby
  • Introduction to Python's method resolution order (MRO)

Lesson 5: Python Exception Handling

  • Basics of Exception handling in Python
  • Comparing exception handling in Python and Ruby

Lesson 6: Python Modules and Libraries

  • Understanding Python's standard library
  • Importing and using modules
  • Differences between Ruby's gems and Python's packages

Lesson 7: Python File and I/O Operations

  • Basics of file handling in Python
  • Comparing Python and Ruby's I/O operations

Lesson 8: Python Decorators and Generators

  • Understanding the concept of decorators and generators in Python
  • How these concepts compare with Ruby's equivalents

Lesson 9: Advanced Python Features

  • Advanced features like list comprehension, lambda functions, and more
  • Compare these features to Ruby

Lesson 10: Real World Python

  • Exploring popular Python libraries (Pandas, NumPy, requests, etc.)
  • Building a small real-world project in Python

Lesson 11: Python Best Practices

  • Writing idiomatic Python
  • The Zen of Python

Lesson 12: Overview of Python Web Development

  • Basics of Flask and Django, comparing them to Ruby's Sinatra and Rails
  • Building a small web application

Lesson 1: Python Syntax and Semantics

Introduction to Python and its syntax

Python, designed by Guido van Rossum, is a high-level programming language that's widely known for its clear syntax and readability. It emphasizes simplicity and generality, making it popular for applications ranging from web development to data science.

A basic Python program can be as simple as:

print("Hello, World!")

In Python, indentation is not just for readability but it is syntactically significant. Python uses indentation to indicate blocks of code under classes, functions, and control structures.

Differences between Ruby and Python syntax

Both Ruby and Python prioritize programmer productivity and code readability, but there are key differences in their syntax:

  • Print statement: In Ruby, we use puts to print while in Python we use print().

    Ruby:

    puts "Hello, World!"

    Python:

    print("Hello, World!")
  • End of line: Python doesn't require a specific end-of-line character, whereas in Ruby, end is required to close code blocks.

  • String interpolation: In Ruby, we use #{} for string interpolation. In Python, we use the f-string format: f'{}'.

  • Instance variables: In Ruby, instance variables are denoted with an @ symbol, but there's no such notation in Python. In Python, you refer to instance variables just like any other variable.

Basic variable declaration, types, and operations in Python

Variable declaration in Python is straightforward. There's no need to specify the type of variable because Python is dynamically typed:

name = "Alice"
age = 25
pi = 3.1415
is_tall = True

Python supports several types of operations, such as arithmetic, comparison, and logical operations:

# Arithmetic Operations
a = 10
b = 3
print(a + b)  # 13
print(a - b)  # 7
print(a * b)  # 30
print(a / b)  # 3.3333333333333335

# Comparison Operations
print(a == b)  # False
print(a != b)  # True
print(a > b)   # True
print(a < b)   # False

# Logical Operations
t = True
f = False
print(t and f)  # False
print(t or f)   # True
print(not t)    # False

Comparing data structures in Python and Ruby: Lists, Tuples, Dictionaries (HashMaps)

Lists (Arrays in Ruby): Lists in Python are similar to Arrays in Ruby. They can contain any type of variable, and they can contain as many variables as you wish.

Python:

list1 = [1, 2, 3, 4, 5]

Ruby:

array1 = [1, 2, 3, 4, 5]

Tuples: Tuples are a kind of list that cannot be changed. Ruby does not have a direct counterpart for Python's tuples, but the closest would be a constant array.

Python:

tuple1 = (1, 2, 3, 4, 5)

Dictionaries (HashMaps in Ruby): Both Python and Ruby use key-value pairs for their Dictionaries and Hashmaps respectively.

Python:

dict1 = {"name": "Alice", "age": 25}

Ruby:

hash1 =

 {"name" => "Alice", "age" => 25}

In the upcoming lessons, we'll delve deeper into Python's syntax, control structures, functions, and more, comparing and contrasting each with its equivalent in Ruby to make your transition to Python smooth and intuitive.

Lesson 2: Python Conditionals and Loops

Introduction to If-Else statements in Python

Python's if-else statements allow us to perform different actions based on specific conditions.

Here's the basic syntax:

if condition:
    # block of code to execute if the condition is True
else:
    # block of code to execute if the condition is False

For instance:

x = 10
if x > 5:
    print("x is greater than 5")
else:
    print("x is not greater than 5")

Python also has an elif statement which can be used to check multiple conditions:

x = 10
if x > 10:
    print("x is greater than 10")
elif x == 10:
    print("x is exactly 10")
else:
    print("x is less than 10")

Understanding For and While loops in Python

Python's for loop is used for iterating over a sequence such as a list, tuple, dictionary, string, or a set.

Here's a basic syntax:

for item in iterable:
    # block of code to execute for each item

For example:

for i in [1, 2, 3, 4, 5]:
    print(i)

Python's while loop repeats a block of code as long as a certain condition is true.

Here's the basic syntax:

while condition:
    # block of code to execute while the condition is True

For instance:

i = 0
while i < 5:
    print(i)
    i += 1

Comparing Python's conditionals and loops to Ruby's

Ruby and Python both have if-else statements, while loops, and for loops. However, there are a few key differences.

In Ruby, elsif is used instead of Python's elif for multiple conditions. In Python, indentation determines the scope of the code block following control structures like if-else, while, or for. However, in Ruby, you use end to close a block.

Here is a Ruby if-else example:

x = 10
if x > 5
    puts "x is greater than 5"
else
    puts "x is not greater than 5"
end

As for loops, Python's for loop is more like a foreach loop in other languages. It's used to iterate over the items of any sequence such as a list or a string. However, Ruby's for loop works more like traditional for loops in languages like C or Java, with an initializer, condition, and increment/decrement.

Ruby also has a .each method that is more equivalent to Python's for loop:

(1..5).each do |i|
    puts i
end

In while loops, both languages are quite similar. Here's a Ruby while loop example:

i = 0
while i < 5 do
    puts i
    i += 1
end

As you learn Python's control flow structures, remember the key differences and similarities to Ruby's. It will help you to understand Python more quickly and deeply.

Lesson 3: Python Functions

In this lesson, we will dive into Python functions. If you're coming from a Ruby background, you're likely familiar with the concept of methods. In Python, we use a similar concept referred to as functions.

Declaring and Calling Functions in Python

Python provides a simple syntax to declare a function using the def keyword. Let's look at an example:

def greet():
    print("Hello, world!")

In the above snippet, greet is a simple function that prints "Hello, world!" when called. The function can be invoked using its name followed by parentheses:

greet()  # Outputs: Hello, world!

Differences Between Python and Ruby Functions

While the concepts of functions in Python and methods in Ruby are similar, there are a few differences:

  1. Declaration: In Ruby, methods are declared using def followed by the method name. Python also uses the def keyword, but it enforces the use of parentheses even if the function doesn't accept any parameters.

    Ruby:

    def greet
      puts "Hello, world!"
    end

    Python:

    def greet():
        print("Hello, world!")
  2. Implicit Return: Ruby methods automatically return the value of the last statement. Python functions, on the other hand, don't have an implicit return. If you don't specify a return value, a Python function will return None.

    Ruby:

    def add(a, b)
      a + b  # Implicitly returned
    end

    Python:

    def add(a, b):
        return a + b  # Must be explicitly returned
  3. Scope: In Ruby, methods can't be nested, whereas Python allows for nested functions.

Understanding Python's Way of Defining and Handling Function Arguments

Python provides a lot of flexibility when it comes to defining and handling function arguments. You can specify default values for arguments, pass arguments by keyword, and even accept arbitrary numbers of arguments.

Here are the key ways to define and handle function arguments in Python:

  1. Positional Arguments: These are the most basic type of arguments. They're defined in the function signature and passed in order during function call.

    def add(a, b):
        return a + b
    
    print(add(2, 3))  # Outputs: 5
  2. Keyword Arguments: These are similar to positional arguments, but they are identified in the function call by parameter name.

    def greet(name, greeting):
        print(f"{greeting}, {name}!")
    
    greet(name="Alice", greeting="Hello")  # Outputs: Hello, Alice!
  3. Default Arguments: You can specify default values for arguments. If a value for such an argument is not provided in the function call, Python uses the default value.

    def greet(name, greeting="Hello"):
        print(f"{greeting}, {name}!")
    
    greet("Alice")  # Outputs: Hello, Alice!
  4. Variable-Length Arguments: Python allows you to define functions that can accept any number of arguments. This is done using the asterisk (*) for non-keyword variable arguments and double asterisk (**) for keyword variable arguments.

    def add(*args):
        return sum(args)
    
    print(add(1, 2, 3, 4, 5))  # Outputs: 15
    
    def print_data(**

Lesson 4: Python Classes and Objects

Welcome to lesson 4, where we'll explore Python's class-based system. Python, like Ruby, supports object-oriented programming (OOP). In this lesson, we'll understand how Python approaches OOP by understanding classes, objects, and some nuanced differences between Python and Ruby's OOP implementation. We'll also introduce Python's method resolution order (MRO).

Understanding Classes and Objects in Python

In Python, almost everything is an object, with its properties and methods. A class is like an object constructor, or a blueprint for creating objects.

Here's a basic example of a class in Python:

class MyClass:
  x = 5

You can create an object of this class using the following syntax:

p1 = MyClass()
print(p1.x)  # Outputs: 5

Differences in Class Declaration, Inheritance, and Encapsulation Between Python and Ruby

In Python, you declare a class using the class keyword followed by the class name. In contrast, in Ruby, you declare a class with the class keyword followed by the class name and ending with an end keyword.

Python uses the def keyword to define a method inside the class. Similarly, Ruby uses def to define methods, but it also uses the end keyword to signify the end of the method.

Python supports multiple inheritance (a class can inherit from multiple parent classes), while Ruby supports single inheritance but offers mixins as an alternative to multiple inheritance.

Python does not support true private encapsulation. In Python, the concept of private variables doesn't exist as it does in Ruby. However, Python does follow a convention using underscores to indicate the level of privacy of a variable.

Introduction to Python's Method Resolution Order (MRO)

Python's MRO is the order in which Python looks for a method in a hierarchy of classes. The MRO ensures that a class always precedes its parents and a subclass precedes its siblings. Python uses the C3 linearization or just C3 algorithm to compute this order.

Here is an example:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

print(D.mro()) # Outputs: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

The output <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'> shows the order in which Python would look for methods or attributes.

That's the end of this lesson! Remember, the goal is to understand the mechanics of Python and compare them with Ruby's. This will help you become a more flexible and adaptable developer. Happy coding!

Lesson 5: Python Exception Handling

Basics of Exception Handling in Python

Python provides several built-in exceptions which include IOError, ValueError, ZeroDivisionError, ImportError, EOFError, KeyboardInterrupt, etc. You can also define your own exceptions by creating a new exception class derived from the base Exception class.

Try and Except

The simplest way to handle exceptions in Python is by using try and except blocks. Here's a basic example:

try:
    # your code
except ExceptionType:
    # code to handle the exception

If an exception is thrown in the try block, the execution immediately moves to the corresponding except block and the program continues. If the exception type is not specified, it will catch all exceptions which are not caught by preceding except blocks.

For instance:

try:
    x = 1 / 0
except ZeroDivisionError:
    print("You can't divide by zero!")

Finally

Python also provides a finally block that will be executed regardless of whether an exception was caught or not. This can be useful for cleaning up resources or performing tasks that must always be executed:

try:
    # your code
except ExceptionType:
    # handle exception
finally:
    # code that is always executed

Comparing Exception Handling in Python and Ruby

Ruby and Python handle exceptions differently. Let's examine their differences.

Syntax

In Python, you use the try, except, and finally keywords for exception handling. In contrast, Ruby uses begin, rescue, and ensure.

Python:

try:
    # code
except ExceptionType:
    # handle exception
finally:
    # code that is always executed

Ruby:

begin
    # code
rescue ExceptionType
    # handle exception
ensure
    # code that is always executed
end

Default Behavior

In Python, an uncaught exception will cause the program to stop and print an error message. In contrast, Ruby will by default rescue any StandardError (and its subclass exceptions) and allow the program to continue running.

Python:

try:
    1 / 0
except Exception as e:
    print(e)  # prints "division by zero"

Ruby:

begin
    1 / 0
rescue Exception => e
    puts e  # prints "divided by 0"
end

Exception Classes

Both Python and Ruby allow for defining custom exception classes, but the process is different in each language. In Python, exceptions are just special classes derived from the built-in Exception class. In Ruby, you create a new class that includes the StandardError module.

Python:

class MyException(Exception):
    pass

Ruby:

class MyException < StandardError
end

Despite the differences in syntax and approach, both Ruby and Python provide robust exception handling mechanisms. In the next lesson, we'll look into more advanced aspects of Python exception handling like raising exceptions and using the else clause.

Lesson 6: Python Modules and Libraries

Welcome to Lesson 6. In this lesson, we'll explore Python's module system and libraries, understanding how they contrast with Ruby's gems. Let's dive right in.

Understanding Python's Standard Library

Python's standard library is an extensive suite of modules that comes with Python itself. It provides a broad range of facilities as part of the Python distribution. It includes everything from data types (like date and time), to file I/O, web services, and even operating system interfaces. The Python standard library is incredibly robust and is one of the factors that make Python such a powerful and versatile language.

In contrast, Ruby has a similar concept with its Standard Library, which includes modules for JSON, XML, file I/O, web services, and more. The primary difference is in how these are used and imported, which we'll cover in the next section.

Importing and Using Modules

Modules in Python are simply Python files that can contain functions, variables, and classes. You can use any Python source file as a module by executing an import statement in some other Python source file.

## Importing entire module
import math
print(math.pi)

## Importing specific function or class from a module
from datetime import datetime
print(datetime.now())

In Ruby, modules are similar, but there's an important difference: Ruby uses require instead of import.

require 'json'

Keep in mind that Python uses the file system to find modules (that is, Python files), whereas Ruby's require looks for Ruby files in the directories listed in your $LOAD_PATH.

Differences between Ruby's Gems and Python's Packages

Ruby Gems and Python Packages are similar in that they're both ways to distribute code libraries. Both offer dependency management and versioning. The key differences lie in how they are installed and managed.

In Ruby, you use the gem command to install gems:

gem install rails

And then use require to use the installed gem:

require 'rails'

On the other hand, Python packages are typically installed with pip, the Python package installer:

pip install requests

And then you use import to use the installed package:

import requests

While both systems are intended to distribute reusable code, there are some differences in the packaging philosophy. Ruby Gems are often used to provide entire application frameworks (like Rails), while Python's packages tend to be smaller modules and libraries, which adhere to Python's philosophy of "do one thing and do it well".

Conclusion

In conclusion, Python's approach to modules and libraries is an integral part of the language. It provides a rich standard library and an easy-to-use system for packaging and distributing reusable code. Although there are some differences between Python's approach and Ruby's, the underlying principles are the same: provide reusable, manageable pieces of code to build larger and more complex applications.

In the next lesson, we will dive into Python's object-oriented programming and how it compares to Ruby's approach. See you there!

Lesson 7: Python File and I/O Operations

As a Ruby developer learning Python, you already understand the importance of file handling and I/O operations in software development. In this lesson, we will explore these concepts in Python and highlight the differences and similarities between Python and Ruby.

Basics of File Handling in Python

Opening and Closing a File

Python's built-in open function is used to open a file. It returns a file object and is most commonly used with two arguments: open(filename, mode). The mode argument is optional and 'r' (read) is the default value. Other common modes include 'w' (write), 'a' (append), and 'b' (binary).

file = open('filename.txt', 'r')
## perform file operations
file.close()

Always remember to close the file using the close method when you're done with it.

Reading from a File

Python provides several methods to read from a file. read() reads the entire file, readline() reads a single line, and readlines() returns a list of lines.

file = open('filename.txt', 'r')
print(file.read())  # prints entire file
file.close()

Writing to a File

To write to a file, you need to open it in write 'w', append 'a' or exclusive creation 'x' mode.

file = open('filename.txt', 'w')
file.write("This is a new line")  # writes a string to the file
file.close()

NOTE: The 'w' mode will overwrite the existing file content.

With Statement

Python provides a special syntax for file operations, which automatically takes care of closing the file once the operations are finished, even if an error occurs.

with open('filename.txt', 'r') as file:
    print(file.read())

Comparing Python and Ruby's I/O Operations

Similar to Python, Ruby also uses an open method to handle files. However, there are some differences in syntax and usage.

Opening and Closing a File

In Ruby, you can open a file with the File.open method.

file = File.open("filename.txt", "r")
## perform operations
file.close

Reading from a File

Ruby has similar functions to Python for reading files: read, readline, and readlines.

file = File.open("filename.txt", "r")
puts file.read  # prints entire file
file.close

Writing to a File

Writing to a file in Ruby is very similar to Python.

file = File.open("filename.txt", "w")
file.write("This is a new line")  # writes a string to the file
file.close

Blocks for File Operations

Instead of Python's with statement, Ruby uses blocks for automatic file closing.

File.open("filename.txt", "r") do |file|
  puts file.read
end

The main difference between Python and Ruby file handling lies in the syntax and certain method names. However, the principles remain the same, so transitioning between the two languages is not overly complicated. As you continue to learn Python, you will gain an intuitive understanding of these differences. Happy coding!

Lesson 8: Python Decorators and Generators

Understanding the concept of decorators and generators in Python

Python Decorators

Decorators in Python allow us to wrap another function in order to extend the behavior of the wrapped function, without permanently modifying it. They are very powerful and are used extensively in Python, especially in web frameworks.

In Python, functions are first-class objects. This means that functions in Python can be passed around and used as arguments just like any other object (e.g., a string, a list, a dictionary). Because of this, it becomes possible to create a function that takes a function and wraps it in another function.

Here is a basic example of a decorator:

def my_decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

@my_decorator
def say_hello():
    print("Hello, world!")
    
say_hello()

This will output:

Before function call
Hello, world!
After function call

The @my_decorator is Python's decorator syntax. say_hello is passed as an argument to the my_decorator function. This setup allows any calls to say_hello() to first call the wrapper() function.

Python Generators

Generators are a special type of iterator, which are a more general concept. An iterator is an object that can be iterated upon and is required to return the next value when the next() function is called on it. A Python generator is a function that uses the yield statement. When the generator function is called, it returns a generator object without even beginning execution of the function.

Here's a simple example:

def my_generator():
    i = 1
    while i <= 3:
        yield i
        i += 1

gen = my_generator()
print(next(gen))  # Outputs: 1
print(next(gen))  # Outputs: 2
print(next(gen))  # Outputs: 3

When next() is called on the generator object, the function begins executing until it encounters the yield keyword. The function then returns the yielded value and pauses. On the next call to next(), the function continues from where it left off, again running until it hits yield.

How these concepts compare with Ruby's equivalents

Python Decorators vs Ruby Method Wrapping

Ruby doesn't have a direct equivalent to Python's decorators, but the closest thing is probably method wrapping using modules. Here's an example:

module Decorator
  def my_method
    puts "Before function call"
    super
    puts "After function call"
  end
end

class HelloWorld
  prepend Decorator

  def my_method
    puts "Hello, world!"
  end
end

HelloWorld.new.my_method

In this case, prepend Decorator in the HelloWorld class allows the my_method in the Decorator module to wrap the my_method in the HelloWorld class.

Python Generators vs Ruby Enumerators

Ruby has an equivalent concept to Python's generators, and that's Enumerators. The Enumerator class in Ruby allows you to create an object that produces a sequence of values. Here's a similar example to the Python generator example:

def my_generator
  Enumerator.new do |enum|
    i = 1
    while i <= 3
      enum.yield i
      i += 1
    end
  end
end

gen = my_generator
puts gen.next  # Outputs: 1
puts gen.next  # Outputs

: 2
puts gen.next  # Outputs: 3

In this case, an Enumerator is created that yields values from 1 to 3, and next is used to iterate through them.

In summary, Python decorators and generators offer some powerful, flexible ways to manipulate code and control flow, and while Ruby doesn't have exact analogs for these features, it offers similar capabilities via method wrapping and Enumerators.

Lesson 9: Advanced Python Features

In this lesson, we will delve into advanced Python features such as list comprehensions, lambda functions, and more. We will also compare these features with their Ruby counterparts for a better understanding.

List Comprehension

List comprehension is a concise way to create lists in Python. It consists of brackets containing an expression followed by a for statement, then zero or more for or if clauses.

numbers = [1, 2, 3, 4, 5]
squares = [n**2 for n in numbers]
print(squares)

This creates a new list squares with the squares of the numbers in the numbers list.

In Ruby, the similar approach would be using map:

numbers = [1, 2, 3, 4, 5]
squares = numbers.map {|n| n ** 2}
puts squares

Lambda Functions

Lambda functions in Python are anonymous functions that are defined with the lambda keyword, and can have any number of arguments but only one expression.

multiply = lambda x, y: x * y
print(multiply(5, 3))

This defines a lambda function multiply that takes two arguments and returns their product.

In Ruby, the lambda function can be defined using lambda or the -> (stabby lambda) operator:

multiply = lambda {|x, y| x * y}
puts multiply.call(5, 3)

or

multiply = ->(x, y) { x * y }
puts multiply.call(5, 3)

Decorators

Python decorators allow us to wrap another function in order to extend the behavior of the wrapped function, without permanently modifying it. In Python, decorators are implemented as functions:

def decorator_func(original_func):
    def wrapper_func():
        print('Wrapper function executed this before {}'.format(original_func.__name__))
        return original_func()
    return wrapper_func

@decorator_func
def display():
    print('display function ran')

display()

In the above code, @decorator_func is a decorator that extends the behavior of the display() function.

Ruby doesn't support decorators natively like Python does. But, similar functionality can be achieved using modules or other Ruby features.

For instance, using Ruby modules:

module Decorator
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def display
      'Wrapper function executed this before display'
      super
    end
  end
end

class TestClass
  include Decorator
  def self.display
    'display function ran'
  end
end

puts TestClass.display

In this lesson, we have explored a few advanced Python features and compared them with their Ruby counterparts. With this knowledge, you can start to leverage the power of Python while bringing in the experience you have from Ruby.

Lesson 10: Real World Python

In this lesson, we will explore some of the most popular Python libraries, including Pandas, NumPy, and requests. These libraries will expand your knowledge and capability with Python, enabling you to create more complex and comprehensive programs. We will then integrate this knowledge into building a small real-world project.

Exploring Popular Python Libraries

Python has a vast ecosystem of libraries that can help us perform various tasks, from data analysis and machine learning to web scraping and data visualization. Let's look at three commonly used libraries.

Pandas

Pandas is a powerful data analysis and manipulation library. If you're familiar with Ruby's active_record, you'll find some similarities here.

import pandas as pd

## Creating a DataFrame
data = {'Name': ['John', 'Anna', 'Peter', 'Linda'],
        'Age': [28, 24, 35, 32],
        'City': ['New York', 'Paris', 'Berlin', 'London']}

df = pd.DataFrame(data)

## Selecting data
young_people = df[df['Age'] < 30]

NumPy

NumPy is a library for the Python programming language, adding support for large, multi-dimensional arrays and matrices, along with a large collection of high-level mathematical functions to operate on these arrays.

import numpy as np

## Creating an array
arr = np.array([1, 2, 3, 4, 5])

## Perform operations
print(arr + 2)
print(arr * 2)
print(np.sqrt(arr))

Requests

Requests is a simple, yet elegant HTTP library, comparable to Ruby's net/http.

import requests

## Making a GET request
response = requests.get('https://jsonplaceholder.typicode.com/posts')

## Parsing the response
data = response.json()

Building a Small Real-World Project in Python

Now that we have a basic understanding of these libraries, let's build a small real-world project: A script that fetches data from a web API, manipulates it, and outputs the result.

Fetching Data from an API

We will use the requests library to fetch data from the JSONPlaceholder API.

import requests

## Fetch posts
response = requests.get('https://jsonplaceholder.typicode.com/posts')

## Ensure we got a successful response
response.raise_for_status()

## Parse the JSON in the response
data = response.json()

Manipulating the Data with Pandas

Next, we will convert this data into a Pandas DataFrame, which will allow us to manipulate it more easily.

import pandas as pd

## Convert data to DataFrame
df = pd.DataFrame(data)

## Manipulate data
top_posts = df[df['id'] <= 5]

Analysis with NumPy

Let's say we want to analyze the length of the post titles. We can use NumPy to perform this analysis.

import numpy as np

## Create a new column for title length
top_posts['title_length'] = top_posts['title'].apply(len)

## Calculate the average length of the post titles
average_length = np.mean(top_posts['title_length'])

print(f'The average length of the top post titles is {average_length} characters.')

That's it! We've created a script that fetches data from an API, manipulates it, and performs some basic analysis. This is a small example, but it showcases how you can use Python and its libraries in real-world applications.

In the next lesson, we will delve into Python's more advanced features, such as generators and decorators. Happy coding!

Lesson 11: Python Best Practices

In this lesson, we'll cover some of the best practices in Python programming. We'll delve into writing idiomatic Python and discuss the guiding principles of Python, as stated in "The Zen of Python".

Writing Idiomatic Python

Writing idiomatic Python, often called Pythonic code, means writing code that leverages the language's most distinctive features in a way that is natural and easy to understand. This approach not only makes your code cleaner but also improves readability and efficiency. Here are some guidelines to help you write more Pythonic code:

1. List Comprehensions

In Python, you can use list comprehensions to create lists concisely and efficiently. This feature is somewhat similar to Ruby's array comprehensions. Here is an example:

## Non-idiomatic way
squares = []
for i in range(10):
    squares.append(i * i)

## Pythonic way
squares = [i * i for i in range(10)]

2. Use of Underscores

Python uses underscores in several ways: for instance, in variable and method names (snake_case), to ignore values, and for name mangling. Here's how you can use underscores to ignore values in Python:

## Ignoring a value
for _ in range(10):
    print("Hello, world!")

3. Generators

Generators are a Pythonic way to work with sequences that are too large to fit in memory, or when the cost of computing each value is high and you want to do it as needed. They're somewhat akin to Ruby's Enumerator:

## A simple generator function
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

4. String Formatting

Python provides several ways to format strings. The most Pythonic way is to use f-strings:

name = "Alice"
print(f"Hello, {name}!")

5. Use with for File Operations

When working with files, the Pythonic way is to use the with statement. This ensures that the file is properly closed when done, even if an error occurs:

with open("myfile.txt", "r") as file:
    content = file.read()

The Zen of Python

"The Zen of Python" is a collection of 19 guiding principles for writing computer programs in Python. They were written by Tim Peters, a major contributor to the Python community. You can access these principles by typing import this in a Python console:

import this

While it's not mandatory to strictly follow these aphorisms, they serve as a valuable guide towards writing clean, efficient, and Pythonic code. Here are a few key principles:

  • "Beautiful is better than ugly."
  • "Explicit is better than implicit."
  • "Simple is better than complex."
  • "Complex is better than complicated."
  • "Readability counts."

Each of these aphorisms could be the subject of an entire lesson, but in general, they emphasize readability, simplicity, and the "Pythonic" way of doing things.

In conclusion, writing idiomatic Python is all about understanding and embracing the philosophy of the language. By doing so, you can write code that is efficient, easy to understand, and maintainable.

Lesson 12: Overview of Python Web Development

In this lesson, we will dive into Python web development. We will discuss two of the most popular Python web frameworks: Flask and Django. As you already know Ruby, we'll compare these to Ruby's Sinatra and Rails, respectively, to give you a familiar point of reference.

By the end of this lesson, we'll develop a small web application to give you hands-on experience with Python's web development capabilities.

Part 1: Basics of Flask and Django

Flask and Django are among the most widely used web frameworks in Python. Each of them serves different use-cases and offers unique advantages.

Flask

Flask is a micro web framework in Python. It's designed to be minimalistic and simple, similar to Sinatra in Ruby. It does not include form validation or a database abstraction layer out of the box, allowing you to choose your tools, which results in a more flexible approach to development.

The philosophy behind Flask is that it should provide only the components you need to build an application, giving the developer the flexibility and control that might be necessary for more complex applications.

Django

Django, on the other hand, is a high-level Python web framework that follows the "batteries-included" philosophy, similar to Rails in Ruby. It means that Django includes everything you need to build a web application, reducing the need to use third-party tools.

Django includes functionalities such as an Object-Relational Mapper (ORM), form handling, and user authentication. This comprehensive set of tools makes Django a powerful framework for rapid web development, but it can be too heavy for simple applications or microservices.

Comparison with Sinatra and Rails

Here is a table comparing Flask and Django with their Ruby counterparts:

Sinatra Rails Flask Django
Philosophy Minimalistic Batteries-included Minimalistic Batteries-included
Learning Curve Low High Low High
Flexibility High Low High Low
Built-in Features Few Many Few Many

As a Ruby developer, you might find Flask similar to Sinatra in terms of simplicity and flexibility. In contrast, Django's approach to providing a comprehensive set of tools out of the box might feel familiar if you have experience with Rails.

Part 2: Building a Small Web Application

To better understand how Python web frameworks operate, let's build a small web application with Flask. Since it's more lightweight and simpler, Flask is a good starting point for developers transitioning from Ruby to Python.

Our web application will be a simple blog platform where users can post messages.

Here's a brief code snippet to give you a sense of how a Flask application looks:

from flask import Flask, request, render_template
app = Flask(__name__)

posts = []

@app.route('/')
def home():
    return render_template('index.html', posts=posts)

@app.route('/post', methods=['POST'])
def post():
    post = request.form.get('post')
    posts.append(post)
    return render_template('index.html', posts=posts)

if __name__ == '__main__':
    app.run()

In this code:

  • We import the Flask class and instantiate it to create our application.
  • We define a simple in-memory store for our posts (posts = []).
  • We create routes with the @app.route decorator. In this case, we have two routes: one for displaying posts (/) and another for creating new posts (/post).
  • The home() function is triggered when a user navigates to the home page. It renders an index.html template (which you

would need to create) with the list of posts.

  • The post() function is triggered when a user submits a new post. It retrieves the post from the form data, appends it to our list of posts, and then renders the index.html page again with the updated list of posts.
  • Finally, the if __name__ == '__main__': app.run() line is a common pattern in Python to ensure the server only runs if the script is executed directly (i.e., not imported as a module).

That's it for Lesson 12! This overview should help you get started with web development in Python. As you go deeper, you'll find that Python, just like Ruby, has a rich ecosystem of tools and libraries to build robust web applications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment