Python has many powerful features that provide great flexibility when dealing with classes. Here, I'll show you five advanced techniques that can help you write better code.
1. Create a constant value
Suppose we are creating a class Circle. We may need a method to calculate the area and a method to calculate the perimeter:
class Circle(): def __init__(self, radius): self.radius = radius def area(self): return 3.14 * self.radius * self.radius def perimeter(self): return 2 * 3.14 * self.radius
This implementation seems feasible, but what if we want to change the value of pi to be more precise? We need to change it in two places. A better idea is to save the value of PI in a variable:
class Circle(): def __init__(self, radius): self.radius = radius self.pi = 3.14 def area(self): return self.pi * self.radius * self.radius def perimeter(self): return 2 * self.pi * self.radius
However, now anyone can change the value of pi. This is not a good idea. What if the user writes the following:
c = Circle(4) c.pi = 5
This will destroy all our functions! Obviously, we must do something about it. Unfortunately, Python does not allow the creation of constant variables. What we can do is use a function that returns a pi with the tag @ property. In this way, functions can be called like variables without parentheses.
class Circle(): def __init__(self, radius): self.radius = radius @property def pi(self): return 3.14 def area(self): return self.pi * self.radius * self.radius def perimeter(self): return 2 * self.pi * self.radius
Now the value of pi cannot be changed by the user, it can only be stored in the attribute pi.
2. Multiple class constructors
In Python, the constructor of a class is__ init__ Method defined. Unfortunately, function overloading is not allowed, so we can only define a function with the same function. However, there is a way to use a method similar to a class constructor. This can be done using the tag @ classmethod.
For example, suppose we are creating a class Date. The first constructor is:
class Date(): def __init__(self, day, month, year): self.day = day self.month = month self.year = year
Now we may want to add another constructor to get the Date from the string in the format dd/mm/yyyy. We can create a class method in the Date class:
@classmethod def fromString(cls, s): day = int(s[:2]) month = int(s[3:5]) year = int(s[6:]) return cls(day, month, year) # Returns a new object
Now we can create a new Date object by calling the fromString method:
d = Date.fromString("21/07/2020") print(d.day, d.month, d.year) Output: 21 7 2020
3. Create enumeration
Suppose you have a variable in your program that represents the day of the week. You might express it as an integer, for example, Monday is 1, Tuesday is 2, and so on. However, this is not the best, because you must remember whether to start from zero or 1. In addition, when you print the value, it will only be an integer, so you need a function to convert it to the correct name.
Fortunately, we can use the enum class in the enum library. When you create a subclass of enum, it contains a set of variables, each with its own value, which you can use in your program. For example:
from enum import Enum class WeekDay(Enum): Monday = 1 Tuesday = 2 Wednesday = 3 Thursday = 4 Friday = 5 Saturday = 6 Sunday = 7
Now set the variable to the value "Friday", you can write:
day = WeekDay.Friday
You can also get the integer value represented by "Friday":
intDay = day.value # This value is 5
Iterators are classes that allow you to iterate over all elements in a data structure. An example of an iterator is the range function: you can iterate over all integer values in a range (for example, using a for loop).
If you want to create your own iterator, you just need to implement it__ next__ And__ iter__ method.
- __ iter__ The iterator object should be returned (so it returns self in most cases)
- __ next__ The next element of the data structure should be returned
Suppose we are creating an iterator to loop through a list from back to front. The correct approach is:
class Backward(): def __init__(self, data): self.data = data # We're going to iterate over a list type of data self.index = len(self.data) def __iter__(self): return self def __next__(self): if(self.index == 0): raise StopIteration else: self.index -= 1 return self.data[self.index]
As you can see, if there are no more elements to iterate__ next__ The function will raise a StopIteration error. Otherwise, it returns the next element to view. Now we can use the Backward object in the for loop:
bw = Backward([1,2,3,4,5]) for elem in bw: print(elem)
The output is as follows:
5 4 3 2 1
5. Access a class as a list
When we use lists, we can use square brackets to access specific values. We can also use__ getitem__ And__ setitem__ To implement similar functions for our own classes. These are the methods when we use square brackets for list calls:
- x = l[idx] and x = L__ getitem__ (IDX) has the same function.
- l[idx] = x and L__ setitem__ (IDX, x) has the same function.
So if we implement these two methods in a custom class, we can use square brackets like a list.
This is a very simple example (it is a list whose size cannot be changed and whose index starts from 1):
class MyList(): def __init__(self, dimension): self.l = [0 for i in range(dimension)] def __getitem__(self, idx): return self.l[idx-1] def __setitem__(self, idx, data): self.l[idx-1] = data ml = MyList(5) ml = 50 # Set the first element to 50 ml = 100 # Set the first element to 100 x = ml # The value of x is now 50