- Expert Python Programming(Second Edition)
- Micha? Jaworski Tarek Ziadé
- 658字
- 2021-07-16 10:58:07
Other syntax elements you may not know yet
There are some elements of the Python syntax that are not popular and rarely used. It is because they either provide very little gain or their usage is simply hard to memorize. Due to this, many Python programmers (even with years of experience) simply do not know about their existence. The most notable examples of such features are as follows:
- The
for … else
clause - Function annotations
The for … else … statement
Using the else
clause after the for
loop allows you to execute a code of block only if the loop ended "naturally" without terminating with the break
statement:
>>> for number in range(1): ... break ... else: ... print("no break") ... >>> >>> for number in range(1): ... pass ... else: ... print("break") ... break
This comes in handy in some situations because it helps to remove some "sentinel" variables that may be required if the user wants to store information if a break
occurred. This makes the code cleaner but can confuse programmers not familiar with such syntax. Some say that such meaning of the else
clause is counterintuitive, but here is an easy tip that helps you to remember how it works—memorize that else
clause after the for
loop simply means "no break".
Function annotations
Function annotation is one of the most unique features of Python 3. The official documentation states that annotations are completely optional metadata information about the types used by user-defined functions, but in fact, they are not restricted to type hinting, and also there is no single feature in Python and its standard library that leverages such annotations. This is why this feature is unique—it does not have any syntactic meaning. Annotations can simply be defined for a function and can be retrieved in runtime, but that is all. What to do with them is left to the developers.
A slightly modified example from the Python documentation shows best how to define and retrieve function annotations:
>>> def f(ham: str, eggs: str = 'eggs') -> str: ... pass ... >>> print(f.__annotations__) {'return': <class 'str'>, 'eggs': <class 'str'>, 'ham': <class 'str'>}
As presented, parameter annotations are defined by the expression evaluating to the value of the annotation preceded by a colon. Return annotations are defined by the expression between the colon denoting the end of the def
statement and literal ->
that follows the parameter list.
Once defined, annotations are available in the __annotations__
attribute of the function object as a dictionary and can be retrieved during application runtime.
The fact that any expression can be used as the annotation and it is located just near the default arguments allows to create some confusing function definitions as follows:
>>> def square(number: 0<=3 and 1=0) -> (\ ... +9000): return number**2 >>> square(10) 100
However, such usage of annotations serves no other purpose than obfuscation and even without them it is relatively easy to write code that is hard to read and maintain.
While annotations have a great potential, they are not widely used. An article explaining new features added to Python 3 (refer to https://docs.python.org/3/whatsnew/3.0.html) says that the intent of this feature was "to encourage experimentation through metaclasses, decorators, or frameworks". On the other hand, PEP 3107 that officially proposed function annotations lists the following set of possible use cases:
- Providing typing information
- Type checking
- Let IDEs show what types a function expects and returns
- Function overloading / generic functions
- Foreign-language bridges
- Adaptation
- Predicate logic functions
- Database query mapping
- RPC parameter marshaling
- Other information
- Documentation for parameters and return values
Although the function annotations are as old as Python 3, it is still very hard to find any popular and actively maintained package that uses them for something else than type checking. So function annotations are still mostly good only for experimentation and playing—the initial purpose why they were included in initial release of Python 3.