What are the quirks of the Python? — Part 2

https://cbarkinozer.medium.com/pythonun-benzersiz-%C3%B6zellikleri-nelerdir-b%C3%B6l%C3%BCm-2-0cd4447ef3e4

 Part 2 of the quirks of the Python series.

Created with Bing AI

Object-oriented programming (OOP) architecture is a widely used application design pattern nowadays. It allows you to structure programs so that properties and methods are connected to specific objects. OOP represents real-world entities as software objects with associated data and the ability to perform specified activities.

Python scopes and namespaces will be discussed, along with global, local, and built-in types. Loop objects will be examined, including integrators and generators. The iteration tool module will be used to show how to speed up sequence looping in Python.

Namespace and Scope

A function can be assigned to a variable in the same way that a value can.

person_name = “John”

def my_function():
print(“hello function”)
hello_function = my_function

Namespace

A namespace is a method for ensuring that all program names are unique and can be used without conflict. Namespace is a simple dictionary structure in which the keys represent the names and the values reflect the actual item. It manages the relationship between names and objects in the program, which means that all names are unique, can exist independently of one another, and are organized in a specific hierarchy.

An example of how namespace is stored in the background:

namespace_example = {"name_first":object_1, "name_second":object_2}

Python uses the following namespaces:

  1. Global namespaces are based on imported module definitions within the program. Each module has its namespace. It is available till the program concludes.
  2. Local namespaces: These are based on local names within a function. Every function in a program is called, or the namespace is formed. It remains available till the function returns.
  3. Namespaces are created based on built-in functions and exception names. The namespace is formed until the interpreter starts, and it is then retained until you quit. These are the Python language’s outermost tiers.

Scope

Scope refers to the parts of a program that can be named without a prefix. In general, the scope in Python specifies the hierarchical level at which we search namespaces for specific name-to-object mappings. Variable scope in Python refers to references or pointers to objects in memory. When you assign a variable with the symbol = to an instance, you are binding (or mapping) the variable to that instance. Multiple variables can be tied to a single instance. In general, Python uses namespaces to track all of these mappings. They are containers that map variable names to objects. You can think of them as dictionaries that hold name-object mappings. This allows you to access objects using the names you assign to them.

Scopes are classified into four types: locales, functions, modules, and built-ins.

  1. The local scope (innermost scope) defines all local names in the current function.
  2. The outermost scope defines a list of all built-in names in the application.
  3. Module scope defines global names for the current module.
  4. The enclosing functions scope specifies a name from the nearest scope and extends outward.
# Outermost scope - built-in names
import math

# Module scope - global names within the module
global_var = 10

def outer_function():
outer_var = 20

def inner_function():
# Local scope - local names within the function
local_var = 30
print("Inner function local_var:", local_var)
print("Inner function outer_var:", outer_var) # Accessing outer function's variable

inner_function()
print("Outer function outer_var:", outer_var)
# print("Outer function local_var:", local_var) # This would cause an error because local_var is not defined in the outer function scope

print("Module scope global_var:", global_var)
outer_function()
print("Module scope global_var:", global_var)
print("Module scope math.sqrt:", math.sqrt(16)) # Accessing built-in name

Nonlocal and Global Keywords in Python

With Python, the keywords nonlocal and global are used to declare variables with nested scope. Here is what they mean:

  1. nonlocal keyword:
  • Used throughout nested functions.
  • Allows you to change a variable in the nearest enclosing scope that is not global.
  • When you use nonlocal within a nested function, it means that the variable being referenced or assigned is in an outer (enclosing) scope.
  • This is especially beneficial when you need to change a variable that is not in either the local or global scope.

2. global keyword:

  • Used to indicate that a variable within a function is in the global scope (outside of the current function).
  • You can edit a variable defined in the global scope from within a function.
  • When you use global within a function, it means that the variable being referenced or assigned is in the global scope, rather than establishing a new local variable with the same name.
  • This keyword is useful when you wish to change a global variable from within a function, but it’s generally regarded as best practice to avoid global variables if feasible because they might cause unforeseen side effects and make code more difficult to reason about.

Here’s a simple example of using nonlocal and global keywords:

# Global variable
global_var = 10

def outer_function():
# Enclosing function variable
outer_var = 20

def inner_function():
nonlocal outer_var
global global_var

outer_var = 30 # Modifying the variable from the outer function's scope
global_var = 40 # Modifying the global variable
print("Inner function modified outer_var:", outer_var)
print("Inner function modified global_var:", global_var)

inner_function()
print("Outer function outer_var after inner_function:", outer_var)

# Before calling outer_function
print("Global variable before outer_function:", global_var)

outer_function()

# After calling outer_function
print("Global variable after outer_function:", global_var)

Classes and Inheritance

Object-oriented programming (OOP) is a programming design pattern that allows you to structure programs by grouping properties and methods into individually defined objects. OOP is all about improving programs’ capacity to reuse code while making it easier to create and understand. OOP’s encapsulation allows developers to treat code as a black box. Using OOP elements such as inheritance makes it easy to extend the functionality of existing code while significantly reducing program development and testing time.

Python is an object-oriented programming language, meaning it is designed around objects that mix data and functionality. Python treats everything as an object, complete with properties (attributes) and methods. One of the most fundamental ideas in OOP is the distinction between classes and objects.

A class is a building component for linking data and functions together. A class serves as the “blueprint” for a newly formed object. An object is an instance of a class. Objects have member variables, and behaviour is linked to them. When you create a new class, you create a new type of object that may be used to create new instances. Classes allow you to group data and functionality.

class Person:
"""
This class represents a person with a name and age.
"""


def __init__(self, name, age):
"""
Constructor for Person class.

Parameters:
name (str): The name of the person.
age (int): The age of the person.
"""

self.name = name
self.age = age

def greet(self):
"""
Method to greet the person.
"""

print(f"Hello, my name is {self.name} and I am {self.age} years old.")


# Creating an instance of the Person class
person1 = Person("Alice", 30)

# Accessing instance variables
print("Name:", person1.name)
print("Age:", person1.age)

# Calling the greet method
person1.greet()

As can be seen, the “self” keyword defines the initial parameter of each class method. It always refers to the current instance of the used class. In our situation, the “self” option ensures that the method print_book_title() has a means of referencing object characteristics.

Constructor Method

In object-oriented programming, a constructor is a custom method of a class or structure that initializes a specific type of object. A constructor is an instance method that typically has the same name as the class and can be used to set the values of an object’s members, either to default or user-defined values. When a class object is instantiated, its constructor method is called.

Python defines the constructor method with the reserve name “__init__”.

Class Inheritance

In computer programming, inheritance is a valuable aspect of object-oriented programming (OOP). Inheritance enables computer programmers to construct classes that inherit all of the methods and properties of another class. Inheritance implementation consists of two elements.

  1. Parent class — This is the class being inherited from; it is also known as the base or superclass class.
  2. Child class — This is the class that inherits from another, often known as a subclass or derived class.

One of the primary benefits of inheritance is that it reduces the amount of duplicate code by storing the general code in the base class and allowing various derived classes to access it. This is a significant advantage for improved code structure and future improvements. Here is some general class inheritance implementation code.

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

def speak(self):
pass # Abstract method


class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"


class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"


class Cow(Animal):
def speak(self):
return f"{self.name} says Moo!"


# Creating instances of derived classes
dog = Dog("Buddy")
cat = Cat("Whiskers")
cow = Cow("Bessie")

# Polymorphism: calling speak() method on different objects
print(dog.speak()) # Output: Buddy says Woof!
print(cat.speak()) # Output: Whiskers says Meow!
print(cow.speak()) # Output: Bessie says Moo!

As you can see, in Python, any new class (BaseClassName) derives from the generic class “object”.

The super() function allows these classes to inherit all of the methods and attributes from the PersonClass base class. This function returns a proxy object that can call methods and use base class properties through delegation.

Method Overriding

A class can also define an entirely new implementation in any base class method. Method overriding is a word used in object-oriented programming. Overriding is the ability of a class to modify the implementation of a method given by one of its base classes. Overriding is a key aspect of OOP since it allows inheritance to use its full potential. Method overriding allows a class to clone another class, avoiding duplicating code while also upgrading or changing a portion of it.

Iterators and Generators

Iterable objects are any Python objects that can be utilized in a loop statement. Python contains various built-in iterator objects, including lists, tuples, strings, dictionaries, and files. Iterators produce clean, well-organized code.

Python has a built-in method called “iter()”. This function accepts an iterable object and returns an iterator. The “next of the iterable object” method retrieves the item (element). If there are no more elements, the exception “StopIteration” will occur.

# Define an iterable object (list)
my_list = [1, 2, 3, 4, 5]

# Get an iterator from the iterable using iter()
my_iterator = iter(my_list)

# Iterate over the elements using the iterator
while True:
try:
# Get the next element from the iterator using next()
element = next(my_iterator)
print(element)
except StopIteration:
# Stop iteration when all elements have been exhausted
break

Iterators

Iterators are implemented as classes that follow the iterator protocol description. An iterator protocol is a specialized class that implements the __iter__() and __next__() methods.

  • __iter__ returns the iterator object itself.
  • __next__ returns the next iteration value.
class MyIterator:
def __init__(self, max_num):
self.max_num = max_num
self.current_num = 0

def __iter__(self):
return self

def __next__(self):
if self.current_num < self.max_num:
self.current_num += 1
return self.current_num
else:
raise StopIteration

# Creating an iterator object
my_iterator = MyIterator(5)

# Iterating over the iterator
for num in my_iterator:
print(num)

Generators

A generator is an object that allows you to easily generate iterators. To create a generator, no bespoke class with methods __iter__() and __next__() is required. A generator can be created with a simple function that does not return anything and uses the “yield” keyword.

def my_generator(max_num):
current_num = 0
while current_num < max_num:
yield current_num
current_num += 1

# Creating a generator object
gen = my_generator(5)

# Iterating over the generator
for num in gen:
print(num)

When the loop begins and reaches the yield statement, the value of the local variable low is returned, and the generator terminates. When the generator receives the next call, it restarts at the previous value and increases it by one. In general, the yield statement returns the result of the function that simulates the return statement while keeping the value of the local variable low.

Itertools Module

Python contains an “itertools” module that includes efficient iterator routines for fast looping.

Use count(start=0, step=1) to create an iterator that returns evenly spaced values beginning with the number start.

from itertools import count

# Creating a count generator starting from 3 with step 2
counter = count(start=3, step=2)

# Iterating over the count generator
for i in range(5):
print(next(counter))

use cycle(iterable) to create an iterator that returns elements and saves a duplicate of each one.

from itertools import cycle

# Define an iterable
my_list = [1, 2, 3]

# Create a cycle iterator from the iterable
cycler = cycle(my_list)

# Print the first 10 elements of the cycle
for _ in range(10):
print(next(cycler))

repeat(object[, times]) creates an iterator that returns the item repeatedly. It will run indefinitely unless the times parameter is supplied.

from itertools import repeat

# Create a repeat iterator with a specified object and number of times
repeater = repeat("Hello", times=3)

# Print the repeated object
for item in repeater:
print(item)


Post a Comment

0 Comments