ASSIGNEMENT EXPRESSION

Let’s start to deepen the topic “NEWS INTRODUCED IN PYTHON 3.8 AND 3.9” starting from a feature introduced in Python 3.8. A new operator has been introduced, it is called Walrus Operator and it is an assignment operator. Its symbol is: :=.  Before talking about this new operator let’s do a little review on the expressions. An expression is a particular type of Statement evaluated at runtime to produce a value. The simplest expressions are literals and identifiers.

Espessioni

You can create other expressions using the operators that Python makes available to us, moreover the invocation of a method is also an expression as it produces a return value. There are cases where we can’t assign the return value to a variable. Let’s see an example of a simple method that adds two numbers together. The assignment performed in a conditional test, as shown in the figure, fails before Python 3.8 as we could not assign the result of an expression to a variable within a conditional test.

THIS ASSIGNMENT BEFORE PYTHON 3.8 FAILS

Espessioni

Using the Walrus Operator and enclosing the operation in round brackets, the assignment operation is successful. x takes the return value from the method and then is compared to 6.

Operatore di assegnamento

Let’s see another example this time applied to a loop. For each single iteration we extract the last value with the pop() statement which also removes the element from the list. We then print the boolean content of x and the extracted element.

Operatore di assegnamento

Let’s go back to the sum method. If round brackets are not used, the expression generates an error because expressions with assignment are prohibited at the first level if they are not contained in parentheses.

Operatore di assegnamento
class MyClass(object):
    def __new__(cls,list):
        istanza = super().__new__(cls)
        print(f'Istanza creata')
        return istanza
    def __init__(self,list):
        self.list = list
    def example1(self):
        #Using assignment expressions in if statements
        if (length := len(self.list)) > 2:
            print("List length of", length, "is too long")
    @staticmethod
    def example2():
        #Use assignment expressions in loops
        while (directive := input("Enter text: ")) != "stop":
            print("Received directive", directive)
    @staticmethod
    def example3(x):
        print("Multiplying", x, "*", x)
        return x * x

l=[1,2,3,4,5]
e=MyClass(l)
e.example1()
MyClass.example2()
#Using assignment expressions in ranges
[result for i in range(6) if (result := MyClass.example3(i)) > 0]

POSITIONAL-ONLY PARAMETERS

Speaking of “NEWS INTRODUCED IN PYTHON 3.8 AND 3.9” we must mention a change that deals with the relationship between positional parameters and keyword parameters. There are cases in which a function definer may wish that only some of the expected parameters are of the keyword type, while other parameters must be positional only. In other words, we want to make it possible to decide what the positional and keyword parameters should be. Since version 3.8 of Python this is possible.

POSITIONAL-ONLY PARAMETERS

The bar after the parameter a indicates that this parameter must necessarily be positional. The remaining two can be indifferently positional or keyword.

Positional parameter only
Positional parameter only
Positional parameter only

UNION OPERATORS FOR DICTIONARIES

Version 3.9 introduced two new dictionary operators. Let’s see what it is with some example code. Suppose we create two dictionaries and then merge both to create a new dictionary. If in the second dictionary we define a key already present in the first dictionary, this key and the respective value will overwrite the key present in the first dictionary. To better handle cases like this and reduce the amount of code we have two new dictionary operators called union operators.

| -> Union Operator

|= -> In-Place Union

To see these two new operators at work, we help each other with the example code shown below.

class Dictionary(object):
    def __new__(cls,d1,d2):
        istanza = super().__new__(cls)
        print(f'Istanza creata')
        return istanza
    def __init__(self,d1,d2):
        self.d1 = {"a":"primo","b":"secondo","c":"terzo"}
        self.d2 = {"d":"quarto","a":"quinto"}
    def stampa(self):
        print(self.d1)
        print(self.d2)
    def merge(self):
        self.d3 = self.d1.copy()
        for chiave, valore in self.d2.items():
            self.d3[chiave]=valore
        print(self.d3)
    def operatoreUnion(self):
        self.d3 = self.d1 | self.d2
        print(f'Operatore Union applicato a d1 e d2 d3= {self.d3}')
    def inPlaceOperatore(self):
        #Unisce il secondo dizionario d2 al primo d1
        self.d1 |= self.d2
        print(f'Operatore Self In Place Union applicato a d1 e d2 d1= {self.d1}')

a=Dictionary([],[])
a.stampa()
a.merge()
#Suppose to insert the key a already present in d1
#the result as you can see is that this will overwrite
#the "a" key of d1.
a.operatoreUnion()
a.inPlaceOperatore()

REMOVEPREFIX() AND REMOVESUFFIX() METHODS

The two methods allow us to delete a substring at the beginning and at the end of a string itself. There are similar methods that have been around for a long time in Python. These methods are lstrip(), strip() and rstript(). Let’s help ourselves as usual with some example code.

class String(object):
    def __new__(cls,nome):
        istanza = super().__new__(cls)
        print(f'Istanza creata')
        return istanza
    def __init__(self,nome):
        self.nome=nome
    def rStrip(self,sottostringa):
        #DELETE CHARACTERS ON THE RIGHT. A STRING IS RETURNED
        #IN CASE OF SUCCESS SHORTER THAN THE ORIGINAL ONE
        print(f'rstrip: {self.nome.rstrip(sottostringa)}') 
    def strip(self,spazi,sottostringa):
        if spazi == False:
            print(f'strip: {self.nome.strip(sottostringa)}')
        else:
             #LEAVING THE PARENTHESES EMPTY, SPACES ARE ELIMINATED
             #RIGHT AND LEFT
            print(f'strip: {self.nome.strip()}')
    def lStrip(self,sottostringa):
        print(f'lstrip: {self.nome.lstrip(sottostringa)}')
    def removePrefix(self,sottostringa):
        print(f'removeprefix: {self.nome.removeprefix(sottostringa)}')
    def removeSuffix(self,sottostringa):
        print(f'removesuffix: {self.nome.removesuffix(sottostringa)}')

a = String("remore e tenore")
a.rStrip('re')
a.strip(False,'re')
a.lStrip('re')
b=String("   Mario Rossi  ")
b.strip(True,'')
#A SIDE EFFECT OF THESE METHODS THAT SOMETIMES HAS
#GENERATED MISUNDERSTANDING AMONG THE PROGRAMMERS IS DUE TO FACT
#THE PASSING THE REVERSE SEQUENCE 'er' INSTEAD OF '' re 'WE GET IT
#SAME RESULT. THIS IS DUE TO THE FACT THAT THESE THREE FUNCTIONS
#THEY USE A SET OF CHARACTERS NOT STRINGS. THE PRESENCE
#OF THESE CHARACTERS APPLIED IN ANY SEQUENCE IT CAUSES
#SAME BEHAVIOR.
#a.rStrip('er')
#a.strip(False,'er')
#a.lStrip('er')
a.removePrefix('re')
a.removePrefix('er')
a.removeSuffix('re')
a.removeSuffix('er')

DEEPENING

The “Walrus Operator” (:=) in Python, introduced in version 3.8, is an operator that allows a value to be assigned to a variable as part of an expression. It is called the “Walrus Operator” because its := form vaguely resembles a walrus (walrus) with eyes and fangs.

Use of the Walrus Operator

The := operator is useful in situations where you want to assign and use a value in the same expression, avoiding writing redundant code.

Example 1: Reading Input

Without Walrus Operator:

data = input(“Inserisci un valore: “)
while data != “exit“:
print(f”Hai inserito: {data}”)
data = input(“Inserisci un valore: “)

With Walrus Operator:

while (data := input(“Inserisci un valore: “)) != “exit“:
       print(f”Hai inserito: {data}”)

In this example, date is assigned and used at the same time within the while loop condition.

Example 2: Use in Lists

Suppose we want to compute the lengths of strings and filter only those that are longer than a certain threshold:

Without Walrus Operator:

lengths = [] for s in lista_di_stringhe:
l = len(s)
if l > 5:
lengths.append(l)

With Walrus Operator:

lengths = [l for s in lista_di_stringhe if (l := len(s)) > 5]

Here, l is calculated and simultaneously verified in the if condition.

Advantages

More compact code: Reduces the need to declare separate temporary variables.

Better readability: In some cases, can make code more readable, as it merges the assignment and use of a variable.

Considerations

Clarity: Although the code becomes more compact, it is important not to abuse it so as not to compromise the overall readability of the code.

Compatibility: The Walrus Operator is available only as of Python 3.8, so code using it will not be compatible with earlier versions.

In summary, the Walrus Operator is a powerful tool that, if used judiciously, can simplify many common assignment and condition operations in Python code.

REMOVEPREFIX() AND REMOVESUFFIX()

In Python, the removeprefix() and removesuffix() methods have been introduced since version 3.9. These methods are used to remove a specific prefix or suffix from a string, if present.

removeprefix()

The removeprefix() method removes the specified prefix from the string, if the string begins with that prefix. If the string does not begin with the given prefix, the original string is returned unchanged.

Syntax:

stringa.removeprefix(prefisso)

testo = “HelloWorld
risultato = testo.removeprefix(“Hello“)
print(risultato) # Output: “World”

removesuffix()

The removesuffix() method works similarly, but removes a specified suffix from the end of the string. If the string does not end with the given suffix, the original string is returned unchanged.

Syntax:

stringa.removesuffix(suffisso)

testo = “HelloWorld
risultato = testo.removesuffix(“World“)
print(risultato) # Output: “Hello”

These methods are particularly useful when you want to remove a specific part of a string without having to resort to more complex techniques such as slicing or regular expressions.

LINK TO THE GITHUB CODE

GITHUB

LINKS TO PREVIOUS POST

PREVIOUS POST LINKS