Python Basics
Tags: python, coding, basic, beginners
Published: 2024-06-26 08:49:50

## Python

```python
print("Hello, World!")
```

### Creating Variables

Python has no command for declaring a variable. A variable is created the moment you first assign a value to it.

```python
a = 10       # a is created and assigned an integer value
name = "Alice"   # name is created and assigned a string value
price = 19.99    # price is created and assigned a float value
```

Variables do not need to be declared with any particular type and can even change type after they have been set.

```python
x = 5         # x is an integer
x = "Hello"   # now x is a string
```

#### Rules for Variable Names

- A variable name must start with a letter or the underscore character
- A variable name cannot start with a number
- A variable name can only contain alpha-numeric characters and underscores (A-z, 0-9, and _)
- Variable names are case-sensitive (age, Age, and AGE are three different variables)
- A variable name cannot be any of the Python keywords

```python
my_var = 10        # Valid
_my_var = 20       # Valid
myVar = 30         # Valid
2myvar = 40        # Invalid, starts with a number
my-var = 50        # Invalid, contains a hyphen
```

#### Many Values to Multiple Variables

```python
a, b, c = 1, 2, 3
print(a)   # Output: 1
print(b)   # Output: 2
print(c)   # Output: 3
```

#### One Value to Multiple Variables

```python
x = y = z = "Python"
print(x)   # Output: Python
print(y)   # Output: Python
print(z)   # Output: Python
```

#### Global Variable and Global Keyword

A variable declared outside of a function is a global variable, and its value can be accessed and modified inside a function using the `global` keyword.

```python
x = "global"

def myfunc():
    global x
    x = "local"

myfunc()
print(x)   # Output: local
```

### Creating Comments

Comments in Python are created by using the hash (`#`) symbol. Comments can be used to explain Python code, make the code more readable, or prevent execution when testing code.

#### Single-Line Comments

Single-line comments start with a hash (`#`) symbol.

```python
# This is a single-line comment
print("Hello, World!")  # This comment is at the end of a line
```

#### Multi-Line Comments

Python does not have a specific syntax for multi-line comments, but you can use multiple single-line comments or triple quotes (although the latter is intended for multi-line strings).

```python
# This is a comment
# written in
# more than just one line

"""
This is also a way
to create a multi-line
comment, but it is technically
a multi-line string that is not assigned
to any variable.
"""
```

### Basic Input and Output

In Python, basic input and output operations are handled using the `input()` function for receiving user input and the `print()` function for displaying output.

#### Receiving Input

The `input()` function is used to take input from the user. It always returns the input as a string.

```python
name = input("Enter your name: ")
print("Hello, " + name + "!")
```

If you need to convert the input to another type, you can use functions like `int()`, `float()`, etc.

```python
age = int(input("Enter your age: "))
print("You are " + str(age) + " years old.")
```

#### Displaying Output

The `print()` function is used to display output to the console. It can take multiple arguments and automatically separates them with a space.

```python
print("Hello, World!")
print("My name is", name)
print("I am", age, "years old")
```

You can also format the output using f-strings (formatted string literals).

```python
name = "Alice"
age = 25
print(f"My name is {name} and I am {age} years old.")
```

#### Using Input and Output Together

Here is an example combining input and output operations:

```python
# Taking input from the user
name = input("Enter your name: ")
age = int(input("Enter your age: "))

# Displaying the input
print(f"Hello, {name}!")
print(f"You are {age} years old.")
```

Using these basic input and output functions, you can interact with users and create dynamic programs that respond to user input.

### Data Types in Python

Python has several built-in data types that allow you to store different kinds of data. Here are some of the most commonly used data types:

#### Numeric Types

- **int**: Integer numbers
- **float**: Floating-point numbers
- **complex**: Complex numbers

```python
x = 5           # int
y = 3.14        # float
z = 1 + 2j      # complex
```

#### Sequence Types

- **str**: String, a sequence of characters
- **list**: List, an ordered collection of items
- **tuple**: Tuple, an ordered and immutable collection of items

```python
name = "Alice"            # str
numbers = [1, 2, 3, 4, 5] # list
coordinates = (10, 20)    # tuple
```

#### Mapping Type

- **dict**: Dictionary, a collection of key-value pairs

```python
person = {
    "name": "Alice",
    "age": 25
} # dict
```

#### Set Types

- **set**: An unordered collection of unique items
- **frozenset**: An immutable version of a set

```python
fruits = {"apple", "banana", "cherry"}    # set
frozen_fruits = frozenset(["apple", "banana", "cherry"])  # frozenset
```

#### Boolean Type

- **bool**: Boolean values, either `True` or `False`

```python
is_valid = True   # bool
has_errors = False # bool
```

#### None Type

- **None**: Represents the absence of a value

```python
x = None   # NoneType
```

#### Type Conversion

You can convert between different data types using type conversion functions.

```python
x = 5         # int
y = float(x)  # convert int to float
z = str(x)    # convert int to string
```

#### Checking Data Types

You can check the type of a variable using the `type()` function.

```python
x = 5
print(type(x))   # Output: <class 'int'>

name = "Alice"
print(type(name)) # Output: <class 'str'>
```

### Data Type Constructor Functions and Conversions

In Python, you can use constructor functions to explicitly convert data from one type to another. Here are some common constructor functions and methods for type conversions, along with their syntax:

#### Constructor Functions

- **int()**: Converts a number or string to an integer.
  
  ```python
  x = int(5.6)     # x will be 5
  y = int("10")    # y will be 10
  ```

- **float()**: Converts a number or string to a floating-point number.
  
  ```python
  x = float(5)     # x will be 5.0
  y = float("10.5") # y will be 10.5
  ```

- **str()**: Converts an object to a string representation.
  
  ```python
  x = str(10)      # x will be "10"
  y = str(3.14)    # y will be "3.14"
  ```

- **list()**: Converts an iterable (like a tuple or string) to a list.
  
  ```python
  my_tuple = (1, 2, 3)
  my_list = list(my_tuple)    # my_list will be [1, 2, 3]
  ```

- **tuple()**: Converts an iterable (like a list or string) to a tuple.
  
  ```python
  my_list = [1, 2, 3]
  my_tuple = tuple(my_list)   # my_tuple will be (1, 2, 3)
  ```

- **dict()**: Creates a new dictionary from an iterable of key-value pairs.
  
  ```python
  my_list = [("a", 1), ("b", 2)]
  my_dict = dict(my_list)     # my_dict will be {'a': 1, 'b': 2}
  ```

#### Type Conversion Methods

Some data types in Python also have methods to convert themselves to other types. For example:

- **str() method**: Converts an integer or float to a string.
  
  ```python
  x = 5
  x_str = str(x)    # x_str will be "5"
  ```

- **int() method**: Converts a string or float to an integer.
  
  ```python
  y = "10"
  y_int = int(y)    # y_int will be 10
  ```

- **float() method**: Converts a string or integer to a float.
  
  ```python
  z = "3.14"
  z_float = float(z)  # z_float will be 3.14
  ```

### Type Casting: Implicit and Explicit

In Python, type casting refers to converting a variable from one data type to another. Type casting can be implicit (automatically handled by Python) or explicit (done explicitly by the programmer). Here's how both implicit and explicit type casting work:

#### Implicit Type Casting

Implicit type casting, also known as automatic type conversion, occurs when Python automatically converts one data type to another without any user intervention.

```python
x = 5       # integer
y = 2.5     # float

# Adding an integer and a float
result = x + y

print(result)   # Output: 7.5
```

In this example, Python automatically converts the integer `x` to a float before performing the addition with `y`.

#### Explicit Type Casting

Explicit type casting, also known as type conversion or type casting, occurs when the user manually changes the data type of a variable to another data type using constructor functions like `int()`, `float()`, `str()`, etc.

##### Converting to Integer

```python
x = 5.6     # float
y = int(x)  # explicit conversion to integer

print(y)    # Output: 5
```

##### Converting to Float

```python
x = 10     # integer
y = float(x)  # explicit conversion to float

print(y)    # Output: 10.0
```

##### Converting to String

```python
x = 10     # integer
y = str(x)  # explicit conversion to string

print(y)    # Output: "10"
```

### Strings in Python

In Python, strings are sequences of characters enclosed within either single quotes (`'`) or double quotes (`"`). Strings are immutable, meaning once defined, their content cannot be changed.

#### Creating Strings

```python
# Single line string
name = "Alice"

# Multi-line string using triple quotes
address = """123 Street
City
Country"""
```

#### String Concatenation

You can concatenate strings using the `+` operator. However, numbers cannot be directly added to strings; they must be converted to strings first.

```python
first_name = "John"
last_name = "Doe"
age = 30

# Correct way to concatenate strings and numbers
full_name = first_name + " " + last_name
print(full_name)   # Output: John Doe

# Convert number to string before concatenation
message = "My age is " + str(age)
print(message)     # Output: My age is 30
```

#### String Indexing and Slicing

Strings can be accessed using indexing and slicing.

```python
message = "Hello, World!"

print(message[0])       # Output: H (indexing starts at 0)
print(message[7:12])    # Output: World (slicing from index 7 to 11)
print(message[-1])      # Output: ! (negative indexing from the end)
```

#### String Length

You can find the length of a string using the `len()` function.

```python
message = "Hello, World!"
print(len(message))    # Output: 13
```

#### Escape Characters

Escape characters are used to insert characters that are difficult to type or to represent whitespace.

```python
escaped_string = "This string contains \"quotes\" and \nnewline."
print(escaped_string)
```

#### String Formatting

There are multiple ways to format strings in Python, including using f-strings (formatted string literals) and the `format()` method.

```python
name = "Alice"
age = 30

# Using f-string
message = f"My name is {name} and I am {age} years old."

# Using format() method
message = "My name is {} and I am {} years old.".format(name, age)
```

#### String Operations

Strings support various operations like repetition and membership testing.

```python
greeting = "Hello"
repeated_greeting = greeting * 3
print(repeated_greeting)   # Output: HelloHelloHello

check_substring = "lo" in greeting
print(check_substring)    # Output: True
```

#### String Literals

Raw string literals are useful when dealing with regular expressions and paths.

```python
raw_string = r'C:\new\text.txt'
print(raw_string)   # Output: C:\new\text.txt
```

#### Strings are Immutable

Once a string is created, you cannot modify its content.

```python
message = "Hello"
# This will cause an error
# message[0] = 'J'
```

### Booleans in Python

Booleans in Python are a fundamental data type used to represent truth values. A boolean value can either be `True` or `False`. They are primarily used in conditional statements, logical operations, and control flow structures to make decisions based on whether conditions are true or false.

#### Truthiness and Falsiness
In Python, every object has an associated boolean value, which determines its truthiness or falsiness in a boolean context:
- **True**: Objects that evaluate to `True` in a boolean context include:
  - Any non-zero numeric value (`1`, `-1`, `0.1`, etc.)
  - Non-empty sequences (lists, tuples, strings, dictionaries, sets, etc.)
  - `True` itself
  
- **False**: Objects that evaluate to `False` in a boolean context include:
  - The numeric value `0` (integer or float)
  - Empty sequences (`''`, `[]`, `()`, `{}`, `set()`, etc.)
  - `None`
  - `False` itself
  
#### Examples
```python
print(bool(10))        # Output: True
print(bool(0))         # Output: False
print(bool("hello"))   # Output: True
print(bool(""))        # Output: False
print(bool([]))        # Output: False
print(bool(None))      # Output: False
```

### Python Operators

#### Arithmetic Operators
Arithmetic operators are used for basic mathematical operations.

| Operator | Name            | Description                                    | Example          |
|----------|-----------------|------------------------------------------------|------------------|
| `+`      | Addition        | Adds two operands                              | `x + y`          |
| `-`      | Subtraction     | Subtracts the right operand from the left      | `x - y`          |
| `*`      | Multiplication  | Multiplies two operands                        | `x * y`          |
| `/`      | Division        | Divides the left operand by the right operand  | `x / y`          |
| `%`      | Modulus         | Returns the remainder of the division          | `x % y`          |
| `**`     | Exponentiation  | Raises the left operand to the power of the right operand | `x ** y`   |
| `//`     | Floor Division  | Returns the quotient without the decimal part  | `x // y`         |

```python
a = 10
b = 3
print(a + b)   # Output: 13
print(a / b)   # Output: 3.3333
print(a % b)   # Output: 1
```

#### Assignment Operators
Assignment operators are used to assign values to variables and perform operations.

| Operator | Name             | Description                                            | Example        |
|----------|------------------|--------------------------------------------------------|----------------|
| `=`      | Assignment       | Assigns the value on the right to the variable on the left | `x = 5`    |
| `+=`     | Addition         | Adds right operand to the left operand and assigns the result to the left | `x += 3`   |
| `-=`     | Subtraction      | Subtracts right operand from the left operand and assigns the result to the left | `x -= 3`   |
| `*=`     | Multiplication   | Multiplies right operand with the left operand and assigns the result to the left | `x *= 3`   |
| `/=`     | Division         | Divides left operand by right operand and assigns the result to the left | `x /= 3`   |
| `%=`     | Modulus          | Computes modulus of left operand with right operand and assigns the result to the left | `x %= 3`   |
| `//=`    | Floor Division   | Computes floor division of left operand by right operand and assigns the result to the left | `x //= 3`  |
| `**=`    | Exponentiation   | Computes exponentiation of left operand by right operand and assigns the result to the left | `x **= 3`  |

```python
x = 10
x += 5
print(x)   # Output: 15
```

#### Comparison Operators
`Comparison operators evaluate conditions and return Boolean values.`
Comparison operators are used to compare values.

| Operator | Name                  | Description                                      | Example        |
|----------|-----------------------|--------------------------------------------------|----------------|
| `==`     | Equal                 | Checks if two operands are equal                | `x == y`       |
| `!=`     | Not Equal             | Checks if two operands are not equal            | `x != y`       |
| `>`      | Greater Than          | Checks if left operand is greater than right    | `x > y`        |
| `<`      | Less Than             | Checks if left operand is less than right       | `x < y`        |
| `>=`     | Greater Than or Equal | Checks if left operand is greater than or equal to right | `x >= y` |
| `<=`     | Less Than or Equal    | Checks if left operand is less than or equal to right | `x <= y` |

```python
a = 5
b = 10
print(a == b)   # Output: False
print(a < b)    # Output: True
```

#### Logical Operators
`Logical operators combine Boolean expressions and return Boolean values.`
Logical operators are used to combine conditional statements.

| Operator | Description                                      | Example                  |
|----------|--------------------------------------------------|--------------------------|
| `and`    | Returns True if both statements are true         | `x < 5 and x < 10`       |
| `or`     | Returns True if at least one statement is true   | `x < 5 or x < 4`         |
| `not`    | Reverses the result, returns False if the result is true | `not(x < 5 and x < 10)` |

```python
x = 3
print(x < 5 and x < 10)   # Output: True
print(x < 5 or x < 2)     # Output: True
```

#### Identity Operators
`Identity operators compare object identities and return Boolean values.`
Identity operators are used to compare objects based on their memory location.

| Operator | Description                                       | Example              |
|----------|---------------------------------------------------|----------------------|
| `is`     | Returns True if both variables point to the same object | `x is y`         |
| `is not` | Returns True if both variables do not point to the same object | `x is not y` |

```python
x = ["apple", "banana"]
y = ["apple", "banana"]
z = x

print(x is z)      # Output: True
print(x is y)      # Output: False
print(x == y)      # Output: True (checks for equality)
```

#### Membership Operators
`Membership operators check for element presence in sequences and return Boolean values.`
Membership operators are used to test if a sequence is present in an object.

| Operator | Description                                         | Example              |
|----------|-----------------------------------------------------|----------------------|
| `in`     | Returns True if a sequence is present in the object | `"a" in "apple"`     |
| `not in` | Returns True if a sequence is not present in the object | `"z" not in "apple"` |

```python
print("a" in "apple")     # Output: True
print("z" not in "apple") # Output: True
```

#### Bitwise Operators
Bitwise operators are used to perform bitwise operations on integers.

| Operator | Name              | Description                                        | Example        |
|----------|-------------------|----------------------------------------------------|----------------|
| `&`      | AND               | Sets each bit to 1 if both bits are 1              | `x & y`        |
| `|`      | OR                | Sets each bit to 1 if one of two bits is 1         | `x | y`        |
| `^`      | XOR               | Sets each bit to 1 if only one of two bits is 1    | `x ^ y`        |
| `~`      | NOT               | Inverts all the bits                               | `~x`           |
| `<<`     | Left Shift        | Shifts bits to the left                            | `x << 2`       |
| `>>`     | Right Shift       | Shifts bits to the right                           | `x >> 2`       |

```python
x = 5
y = 3
print(x & y)   # Output: 1
print(x | y)   # Output: 7
```

### Conditions

A condition in programming refers to an expression that evaluates to a Boolean value, either `True` or `False`. Conditions are used to make decisions in code, controlling the flow of execution based on whether certain criteria are met. 

### If-Else Statements in Python

In Python, `if` statements are used for conditional execution based on the evaluation of an expression called a "condition". A condition is an expression that evaluates to `True` or `False`, determining which block of code to execute. Optionally, `else` and `elif` (short for else if) can be used to specify alternative blocks of code to be executed based on different conditions.

#### Syntax and Usage
The basic syntax of an `if` statement in Python is:

```python
if condition:
    # Executes if the condition is True
    statement(s)
```

If there is a need for alternative execution when the condition is false, you can use `else`:

```python
if condition:
    # Executes if the condition is True
    statement(s)
else:
    # Executes if the condition is False
    statement(s)
```

To handle multiple conditions, you can use `elif`:

```python
if condition1:
    # Executes if condition1 is True
    statement(s)
elif condition2:
    # Executes if condition1 is False and condition2 is True
    statement(s)
else:
    # Executes if both condition1 and condition2 are False
    statement(s)
```

#### Examples
**1. Simple if statement:**

```python
x = 10

if x > 5:
    print("x is greater than 5")  # Output: x is greater than 5
```

**2. if-else statement:**

```python
x = 3

if x % 2 == 0:
    print("x is even")
else:
    print("x is odd")  # Output: x is odd
```

**3. if-elif-else statement:**

```python
x = 20

if x > 50:
    print("x is greater than 50")
elif x > 30:
    print("x is greater than 30 but less than or equal to 50")
else:
    print("x is 30 or less")  # Output: x is 30 or less
```

#### Truthy and Falsy Values
In Python, conditions in `if` statements are evaluated based on their truthiness.

#### Truthy and Falsy Examples
```python
# Truthy examples
if 10:
    print("10 is truthy")    # Output: 10 is truthy

if "hello":
    print("hello is truthy") # Output: hello is truthy

# Falsy examples
if 0:
    print("0 is truthy")
else:
    print("0 is falsy")      # Output: 0 is falsy

if []:
    print("Empty list is truthy")
else:
    print("Empty list is falsy")  # Output: Empty list is falsy
```

### Iterable
An "iterable" in Python refers to an object capable of returning its members one at a time. Examples of iterables include lists, tuples, strings, dictionaries, and sets. 

### Loops in Python

Loops in Python allow you to repeatedly execute a block of code until a certain condition is met. Python supports two main types of loops: `for` loops and `while` loops. They are used to iterate over sequences, perform operations on data structures, and automate repetitive tasks.

#### `for` Loops
A `for` loop iterates over elements in a sequence or other iterable objects.

##### Syntax:
```python
for item in iterable:
    # Execute block of code
    statement(s)
```

Example of a `for` loop iterating over a list:
```python
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)  # Output: apple, banana, cherry (each on a new line)
```

Example of a `for` loop iterating over a range:
```python
for num in range(1, 5):
    print(num)  # Output: 1, 2, 3, 4 (each on a new line)
```

#### `while` Loops
A `while` loop executes a block of code repeatedly as long as a specified condition is `True`.

##### Syntax:
```python
while condition:
    # Execute block of code
    statement(s)
```

Example of a `while` loop:
```python
count = 0
while count < 5:
    print(count)
    count += 1  # Incrementing count to avoid infinite loop
```

### Functions in Python

Functions in Python are reusable blocks of code that perform a specific task. They allow you to organize your code into manageable parts and facilitate code reuse and modularity. Python functions are defined using the `def` keyword and can accept parameters and return values.

#### Defining a Function
To define a function in Python, use the `def` keyword followed by the function name and parentheses `()`. Parameters (if any) are placed within the parentheses.

##### Syntax:
```python
def function_name(parameter1, parameter2, ...):
    # Function body - execute block of code
    statement(s)
```

Example of a function that prints a greeting message:
```python
def greet(name):
    print(f"Hello, {name}!")

# Calling the function
greet("Alice")  # Output: Hello, Alice!
```

#### Parameters and Arguments
- **Parameters**: These are placeholders in the function definition.
- **Arguments**: These are the actual values passed into the function when calling it.

#### Return Statement
Functions in Python can optionally return a value using the `return` statement. If no `return` statement is specified, the function returns `None` by default.

Example of a function that calculates the square of a number and returns the result:
```python
def square(x):
    return x * x

# Calling the function and storing the result
result = square(5)
print(result)  # Output: 25
```

#### Lambda Functions
Lambda functions, also known as anonymous functions, are a concise way to define small and unnamed functions in Python. They are defined using the `lambda` keyword and can have any number of arguments but only one expression.

##### Syntax:
```python
lambda arguments: expression
```

Example of a lambda function that adds two numbers:
```python
add = lambda x, y: x + y

# Calling the lambda function
result = add(3, 5)
print(result)  # Output: 8
```

Lambda functions are often used in situations where a small function is needed for a short period of time or as an argument to higher-order functions (functions that take other functions as arguments).

### Default Arguments and Keyword Arguments

#### Default Arguments
In Python, you can define a function with default parameter values. These default values are used when the function is called without providing a specific argument for that parameter. Default arguments allow you to make a function more flexible by providing a fallback value.

##### Syntax:
```python
def function_name(parameter1=default_value1, parameter2=default_value2, ...):
    # Function body - execute block of code
    statement(s)
```

Example of a function with default argument:
```python
def greet(name="Guest"):
    print(f"Hello, {name}!")

# Calling the function without arguments
greet()  # Output: Hello, Guest!

# Calling the function with an argument
greet("Alice")  # Output: Hello, Alice!
```

In the example above, `name` has a default value of `"Guest"`. When `greet()` is called without any argument, it uses the default value.

#### Keyword Arguments
Keyword arguments are arguments passed to a function with the parameter name explicitly specified. This allows you to pass arguments in any order and makes the function call more readable.

##### Syntax:
```python
function_name(parameter1=value1, parameter2=value2, ...)
```

Example of a function with keyword arguments:
```python
def describe_pet(animal_type, pet_name):
    print(f"I have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name}.")

# Using keyword arguments (order doesn't matter)
describe_pet(animal_type="dog", pet_name="Buddy")
describe_pet(pet_name="Fluffy", animal_type="cat")
```

Output:
```
I have a dog.
My dog's name is Buddy.

I have a cat.
My cat's name is Fluffy.
```

#### Combining Default and Keyword Arguments
You can use both default arguments and keyword arguments together in Python functions. Default arguments are initialized with their default values unless overridden by explicitly passing a value as a keyword argument.

Example:
```python
def greet(name="Guest", message="Hello"):
    print(f"{message}, {name}!")

# Using default and keyword arguments
greet()  # Output: Hello, Guest!
greet("Alice")  # Output: Hello, Alice!
greet(message="Hi")  # Output: Hi, Guest!
greet(name="Bob", message="Greetings")  # Output: Greetings, Bob!
```

### Arbitrary Arguments

Arbitrary arguments allow a function to accept an indefinite number of arguments. This is useful when you don't know in advance how many arguments might be passed to your function. In Python, arbitrary arguments are specified using the `*args` and `**kwargs` syntax.

#### Using `*args` for Arbitrary Positional Arguments

When you prefix a parameter with an asterisk (`*`), it collects all positional arguments into a tuple. This allows the function to accept any number of positional arguments.

```python
def greet(*args):
    for name in args:
        print(f"Hello, {name}!")

# Example usage
greet("Alice", "Bob", "Charlie")
```

In this example, `greet` can accept any number of names and will print a greeting for each one.

#### Using `**kwargs` for Arbitrary Keyword Arguments

When you prefix a parameter with two asterisks (`**`), it collects all keyword arguments into a dictionary. This allows the function to accept any number of keyword arguments.

```python
def display_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

# Example usage
display_info(name="Alice", age=30, city="Wonderland")
```

In this example, `display_info` can accept any number of keyword arguments and will print out each key-value pair.

#### Combining `*args` and `**kwargs`

You can combine both `*args` and `**kwargs` in the same function to accept any combination of positional and keyword arguments.

```python
def process_data(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)

# Example usage
process_data(1, 2, 3, name="Alice", age=30)
```

In this example, `process_data` accepts and prints any positional and keyword arguments passed to it.

### Positional-Only Arguments

Positional-only arguments are specified using a forward slash (`/`) in the function definition. Any argument before the slash can only be passed positionally, not as a keyword argument. This feature enforces that certain arguments must be provided in the correct order without using their names.

```python
def greet(name, /, greeting="Hello"):
    print(f"{greeting}, {name}!")

# Example usage
greet("Alice")               # Works
greet("Alice", "Hi")         # Works
greet(name="Alice")          # Error: name must be positional
```

In this example, `name` is a positional-only argument, so it must be passed positionally.

### Keyword-Only Arguments

Keyword-only arguments are specified using an asterisk (`*`) in the function definition. Any argument after the asterisk must be passed as a keyword argument, not positionally. This feature enforces that certain arguments must be provided using their names.

```python
def display_info(*, name, age):
    print(f"Name: {name}, Age: {age}")

# Example usage
display_info(name="Alice", age=30)   # Works
display_info("Alice", 30)            # Error: must use keywords
```

In this example, `name` and `age` are keyword-only arguments, so they must be passed as keyword arguments.

### Combining Positional-Only, Positional-or-Keyword, and Keyword-Only Arguments

You can combine positional-only, positional-or-keyword, and keyword-only arguments in the same function definition for maximum flexibility.

```python
def process_data(a, b, /, c, *, d):
    print(f"a: {a}, b: {b}, c: {c}, d: {d}")

# Example usage
process_data(1, 2, c=3, d=4)       # Works
process_data(1, 2, 3, d=4)         # Works
process_data(a=1, b=2, c=3, d=4)   # Error: a and b must be positional
```

In this example:
- `a` and `b` are positional-only arguments.
- `c` is a positional-or-keyword argument.
- `d` is a keyword-only argument.

This setup enforces that `a` and `b` must be provided positionally, `d` must be provided as a keyword argument, and `c` can be provided either way.

### Recursion

Recursion is a programming technique where a function calls itself directly or indirectly to solve a problem. It is particularly useful for problems that can be broken down into smaller, similar subproblems.

#### Key Components of Recursion:

1. **Base Case**: This is the condition that stops the recursion. It provides a straightforward solution to the smallest instance of the problem.

2. **Recursive Case**: This part of the function breaks down the problem into smaller subproblems and calls itself to solve those subproblems.

#### Example: Factorial Function

A classic example of recursion is calculating the factorial of a number. The factorial of a non-negative integer n is the product of all positive integers less than or equal to n.

```
n! = n * (n-1)!
```

The base case is when n is 0, which directly returns 1.

```python
def factorial(n):
    if n == 0:
        return 1  # Base case
    else:
        return n * factorial(n - 1)  # Recursive case

# Example usage
print(factorial(5))  # Output: 120
```

In this example:
- The function `factorial` calculates the factorial of n using recursion.
- The base case `if n == 0:` returns 1, stopping further recursion.
- The recursive case `return n * factorial(n - 1)` breaks down the problem by multiplying n with the factorial of n-1.

### `pass` in Python

In Python, `pass` is a null statement. When it is executed, nothing happens. It’s often used as a placeholder in situations where a statement is syntactically required, but you have nothing specific to write at the time. This can be particularly useful during the development process, allowing you to outline the structure of your code before filling in the details.

#### Uses of `pass`

**1. Function or Method Definitions**

   When defining a function or method that you intend to implement later, you can use `pass` to provide an empty body.

   ```python
   def my_function():
       pass

   class MyClass:
       def my_method(self):
           pass
   ```

**2. Class Definitions**

   Similarly, `pass` can be used in class definitions to create an empty class that you plan to implement later.

   ```python
   class MyEmptyClass:
       pass
   ```

**3. Control Structures**

   You can use `pass` in control structures like `if`, `for`, `while`, etc., when you haven't yet decided what to do in those blocks.

   ```python
   if condition:
       pass
   else:
       pass

   for i in range(10):
       pass

   while condition:
       pass
   ```

### Python Try...Except: Full Guide

The `try...except` block in Python is used for handling exceptions, allowing you to gracefully manage errors that might occur during the execution of your program. This mechanism is essential for building robust and fault-tolerant applications.

#### Basic Syntax

```python
try:
    # Code that may raise an exception
    risky_code()
except SomeException:
    # Code that runs if the exception occurs
    handle_exception()
```

### Key Components

1. **try Block**: The code that might throw an exception goes inside the `try` block.
2. **except Block**: This block catches and handles the exception. You can specify the type of exception you want to catch.
3. **else Block**: This optional block runs if the `try` block does not raise an exception.
4. **finally Block**: This optional block runs regardless of whether an exception was raised or not. It is commonly used for cleanup actions.

### Examples

#### Basic Try...Except

```python
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero!")
```

In this example, the `ZeroDivisionError` is caught, and a message is printed instead of the program crashing.

#### Catching Multiple Exceptions

```python
try:
    value = int("abc")
except ValueError:
    print("ValueError: Cannot convert to integer.")
except TypeError:
    print("TypeError: Incompatible type.")
```

You can catch multiple exceptions by specifying multiple `except` blocks.

#### Using Else Block

```python
try:
    result = 10 / 2
except ZeroDivisionError:
    print("Cannot divide by zero!")
else:
    print("Division successful, result:", result)
```

The `else` block runs only if no exceptions are raised in the `try` block.

#### Using Finally Block

```python
try:
    file = open("example.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("File not found.")
finally:
    file.close()
    print("File closed.")
```

The `finally` block ensures that the file is closed whether an exception occurs or not.

### Raising Exceptions

You can also raise exceptions using the `raise` keyword.

```python
def check_positive(number):
    if number <= 0:
        raise ValueError("Number must be positive.")
    return number

try:
    check_positive(-5)
except ValueError as e:
    print("Caught an exception:", e)
```

In this example, `raise ValueError` is used to trigger an exception when the number is not positive.

### Custom Exceptions

You can define custom exceptions by creating a new exception class.

```python
class CustomError(Exception):
    pass

try:
    raise CustomError("This is a custom error.")
except CustomError as e:
    print("Caught custom exception:", e)
```

### Nested Try...Except

You can nest `try...except` blocks to handle different exceptions separately.

```python
try:
    try:
        result = 10 / 0
    except ZeroDivisionError:
        print("Inner try: Cannot divide by zero!")
    value = int("abc")
except ValueError:
    print("Outer try: Cannot convert to integer.")
```