GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing python-course.eu — 8. Functional Programming OOP | Advanced | python-course.eu 8. Functional Programming OOPBy Bernd Klein. Last modified: 09 Mar 2024. On this page ➤ When working with higher-order functions, especially in the context of decorators, we often encounter the need to make inner states or objects of our function visible or accessible from the outside. In our decorator examples, we have already observed that by using closures, inner objects were created that we couldn't access externally. This was intentional or the desired effect in many cases. However, what if we want to expose these inner states externally? In this chapter, we want to discuss various ways to achieve this. Essentially, we can achieve this "window to the outside" through attribution or through complex return objects such as tuples or dictionaries. We have used both techniques before, but here we aim to refine this approach. ExampleThe following For the purpose of this chapter of our tutorial the inner function def call_counter(func): calls = 0 def helper(*args, **kwargs): nonlocal calls calls += 1 return func(*args, **kwargs) def get_calls(): return calls helper.get_calls = get_calls return helper from random import randint @call_counter def f1(x): return 3*x @call_counter def f2(x, y): return x + 3*y for i in range(randint(100, 3999)): f1(i) for i in range(randint(100, 3999)): f2(i, 32) print(f"{f1.get_calls()=}, {f2.get_calls()=}") OUTPUT:f1.get_calls()=691, f2.get_calls()=3622 Thus, we have only a read-only access to the counter from outside through the get_calls function. If, for any reason, one desires to allow external modification of the counter – even though it may not seem very sensible – this can be easily achieved with an additional function called set_calls. We will go a step further in the following section and show how functional programming techniques can indeed be used to mimic aspects of class design. In Python, functions and closures can be leveraged to encapsulate state and behavior similar to how classes do. Live Python training See our Python training courses Object Oriented Programming (OOP) and Functional ProgrammingObject-oriented programming (OOP) and functional programming (FP) are among the most widely used programming paradigms. OOP focuses on using objects to represent data and behavior, while FP aims to design programs by combining functions for data transformation. Higher-order functions are a powerful concept in functional programming. They allow the abstraction and composition of code by treating functions as "first-class citizens," enabling manipulation like any other data type. Purely syntactically, one might get the impression that both programming paradigms are entirely different. But if you look at the following implementation, you might get the impression that somebody wanted to write a class and just made an error by writing def Person(name, age): def self(): return None def get_name(): return name def set_name(new_name): nonlocal name name = new_name def get_age(): return age def set_age(new_age): nonlocal age age = new_age self.get_name = get_name self.set_name = set_name self.get_age = get_age self.set_age = set_age return self # Create a person object person = Person("Russel", 25) print(person.get_name(), person.get_age()) person.set_name('Jane') print(person.get_name(), person.get_age()) OUTPUT:Russel 25 Jane 25 The above program defines a function Person that, in a sense, behaves like a class definition. One can instantiate "Person" objects using Person. Within Person, there is a function named person, serving as a container for other inner functions. In a proper class definition, these inner functions would be referred to as methods. Similar to a class, we have defined getters (get_name and get_age) and setters (set_name and set_age). The Person function creates a closure for the local variables name and age, preserving their state. Using nonlocal, inner functions can access them. To access inner functions externally, we attach them as attributes to self, returned by Person. In the following program, we define a corresponding "proper" class definition: class Person2(): def __init__(self, name, age): self.set_name(name) self.set_age(age) def get_name(self): return self.__name def set_name(self, new_name): self.__name = new_name def get_age(self): return self.__age def set_age(self, new_age): self.__age = new_age # Create a Person2 object person = Person2("Russel", 25) print(person.get_name(), person.get_age()) person.set_name('Jane') print(person.get_name(), person.get_age()) OUTPUT:Russel 25 Jane 25 We extend our "functional class" function by adding a def Person(name, age): def self(): return None def get_name(): return name def set_name(new_name): nonlocal name name = new_name def get_age(): return age def set_age(new_age): nonlocal age age = new_age def repr(): return f"Person(name={name}, age={age})" def equal(other): nonlocal name, age return name == other.get_name() and age == other.get_age() self.get_name = get_name self.set_name = set_name self.get_age = get_age self.set_age = set_age self.repr = repr self.equal= equal return self # Create a Person2 object person = Person("Russel", 25) print(person.get_name(), person.get_age()) person.set_name('Eve') print(person.get_name(), person.get_age()) OUTPUT:Russel 25 Eve 25 person2 = Person("Jane", 25) person3 = Person("Eve", 25) print(person.equal(person2)) OUTPUT:False print(person.equal(person3)) OUTPUT:True person.repr() OUTPUT:'Person(name=Eve, age=25)' type(person) OUTPUT:function Mimicking InheritanceIt's truly fascinating! This approach offers the flexibility to simulate inheritance or even multiple inheritance. By leveraging the power of higher-order functions, we can mimic the behavior of inheritance without explicitly defining classes. This purely functional approach opens up new possibilities for structuring and organizing our code, offering a unique perspective on object-oriented concepts. Let us first define our class-like function again. def Person(name, age): def self(): return None def get_name(): return name def set_name(new_name): nonlocal name name = new_name def get_age(): return age def set_age(new_age): nonlocal age age = new_age def repr(): return f"Person(name={name}, age={age})" def equal(other): nonlocal name, age return name == other.get_name() and age == other.get_age() methods = ['get_name', 'set_name', 'get_age', 'set_age', 'repr', 'equal'] # creating attributes of the nested function names to self for method in methods: self.__dict__[method] = eval(method) return self # Create a Person2 object person = Person("Russel", 25) print(person.get_name(), person.get_age()) person.set_name('Eve') print(person.get_name(), person.get_age()) OUTPUT:Russel 25 Eve 25 Employee is supposed to behave like a child class of Person: from functools import wraps def Employee(name, age, stuff_id): self = Person(name, age) # all attributes of Person are attached to self: self = wraps(self)(self) def get_stuff_id(): return stuff_id def set_stuff_id(new_stuff_id): nonlocal stuff_id stuff_id = new_stuff_id # adding 'methods' of child class methods = ['get_stuff_id', 'set_stuff_id'] for method in methods: self.__dict__[method] = eval(method) return self x = Employee('Homer', 42, '007') x.get_age() OUTPUT:42 x.set_stuff_id('434') x.get_stuff_id() OUTPUT:'434' ExercisesExercise 1Create a Python class called Attributes:
Live Python training See our Python training courses Methods:
Your task is to implement the Exercise 2Rewrite the previous class as a function SolutionsSolution to Exercise 1class Librarycatalogue: def __init__(self): self.books = {} def add_book(self, title, author): self.books[title] = author def remove_book(self, title): if title in self.books: del self.books[title] print(f"Book '{title}' removed from the catalogue.") else: print(f"Book '{title}' is not in the catalogue.") def find_books_by_author(self, author): found_books = [title for title, auth in self.books.items() if auth == author] return found_books def find_author_by_book(self, title): if title in self.books: return self.books[title] else: return f"Author of '{title}' is not found in the catalogue." def display_catalogue(self): print("catalogue:") for title, author in self.books.items(): print(f"- {title} by {author}") # Test the Librarycatalogue class library = Librarycatalogue() # Add books to the catalogue library.add_book("1984", "George Orwell") library.add_book("To Kill a Mockingbird", "Harper Lee") library.add_book("Ulysses", "James Joyce") # Display the catalogue library.display_catalogue() # Find books by author print("\nBooks by Harper Lee:", library.find_books_by_author("Harper Lee")) # Find author by book print("\nAuthor of '1984':", library.find_author_by_book("1984")) print("Author of 'Ulysses':", library.find_author_by_book("Ulysses")) # Remove a book library.remove_book("To Kill a Mockingbird") # Display the catalogue again library.display_catalogue() OUTPUT:catalogue: - 1984 by George Orwell - To Kill a Mockingbird by Harper Lee - Ulysses by James Joyce Books by Harper Lee: ['To Kill a Mockingbird'] Author of '1984': George Orwell Author of 'Ulysses': James Joyce Book 'To Kill a Mockingbird' removed from the catalogue. catalogue: - 1984 by George Orwell - Ulysses by James Joyce Solution to Exercise 2def Librarycatalogue(): books = {} def self(): return None # names of the nested functions to be exported methods = ['add_book', 'remove_book', 'find_books_by_author', 'find_author_by_book', 'display_catalogue'] def add_book(title, author): books[title] = author def remove_book(title): if title in books: del books[title] print(f"Book '{title}' removed from the catalogue.") else: print(f"Book '{title}' is not in the catalogue.") def find_books_by_author(author): found_books = [title for title, auth in books.items() if auth == author] return found_books def find_author_by_book(title): if title in books: return books[title] else: return f"Author of '{title}' is not found in the catalogue." def display_catalogue(): print("catalogue:") for title, author in books.items(): print(f"- {title} by {author}") # creating attributes of the nested function names to self for method in methods: self.__dict__[method] = eval(method) return self # Test the Librarycatalogue class library = Librarycatalogue() # Add books to the catalogue library.add_book("1984", "George Orwell") library.add_book("Hotel New Hampshire", "John Irving") library.add_book("Ulysses", "James Joyce") # Display the catalogue library.display_catalogue() # Find books by author print("\nBooks by Harper Lee:", library.find_books_by_author("Harper Lee")) # Find author by book print("\nAuthor of '1984':", library.find_author_by_book("1984")) print("Author of 'Ulysses':", library.find_author_by_book("Ulysses")) # Remove a book library.remove_book("To Kill a Mockingbird") # Display the catalogue again library.display_catalogue() OUTPUT:catalogue: - 1984 by George Orwell - Hotel New Hampshire by John Irving - Ulysses by James Joyce Books by Harper Lee: [] Author of '1984': George Orwell Author of 'Ulysses': James Joyce Book 'To Kill a Mockingbird' is not in the catalogue. catalogue: - 1984 by George Orwell - Hotel New Hampshire by John Irving - Ulysses by James Joyce |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |