Diferències
Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.
| Ambdós costats versió prèvia Revisió prèvia Següent revisió | Revisió prèvia | ||
| info:cursos:pue:python-pcpp1:m2:4.1 [14/12/2023 10:46] – mate | info:cursos:pue:python-pcpp1:m2:4.1 [19/12/2023 11:02] (actual) – [One-line docstrings] mate | ||
|---|---|---|---|
| Línia 64: | Línia 64: | ||
| In a nutshell, type hinting allows you to **statically indicate** the //type// information related to Python objects, which means that you can, for example, add //type// information to a function – indicate the type of an argument the function accepts, or the type of the value it will return. Look at the following examples: | In a nutshell, type hinting allows you to **statically indicate** the //type// information related to Python objects, which means that you can, for example, add //type// information to a function – indicate the type of an argument the function accepts, or the type of the value it will return. Look at the following examples: | ||
| + | <code python># No type information added: | ||
| + | def hello(name): | ||
| + | return " | ||
| + | |||
| + | |||
| + | # Type information added to a function: | ||
| + | def hello(name: str) -> str: | ||
| + | return " | ||
| + | |||
| + | Type hinting is **optional**, | ||
| + | |||
| + | In the second example, the '' | ||
| + | |||
| + | What does it all mean for you, and how can you take advantage of type hinting in Python? | ||
| + | |||
| + | * First of all, type hinting can help you **document your code**. Instead of leaving argument- and response-related information in docstrings, you can use the language itself to serve this purpose. This may be an elegant and useful way to highlight some of the more important code information, | ||
| + | * Type hinting allows you to n**otice certain kinds of errors more effectively** and write a more beautiful and, most of all, **cleaner code**. When using type hints, you more carefully think about types in your code, which helps to prevent or detect some of the errors that may result from the dynamic nature of Python. (However, we're not advocates for making Python require static typing.) | ||
| + | * You must remember that type hinting in Python is **not used at runtime**, which means all the //type// information you leave in the code in the form of annotations is erased when the program is executed. In other words, type hinting does not have any effect on the operation of your code. On the other hand, when used along with some type checking system or lint-like tools that you can plug in to your editor or IDE, it can support your code-writing by autocompleting your typing and spotting and highlighting errors before your code is executed. | ||
| + | * Since type hints have no effect on the source code, this means that they have no impact on performance times (characters are ignored by Python at runtime, which has no influence on interpretation/ | ||
| + | In this short section we've only touched on the most fundamental questions related to the subject of type hinting in Python. For more information about the topic, we encourage you to have a closer look at PEP 483 – The theory of type hints, PEP 484 – Type hints (information about the syntax for type annotations, | ||
| + | |||
| + | Now it's time to move on to docstrings. | ||
| + | |||
| + | == Docstrings – where and how? | ||
| + | We've already said that docstrings can be used in classes, modules, functions, and method definitions. Now we want to elaborate on this: there are cases where they not only //can// be included, but //should// be included. To be more precise – all public modules, functions, classes, and methods that are exported by a given module **should have docstrings**. | ||
| + | |||
| + | Non-public methods do not need to contain docstrings. However, it is recommended that you leave a comment right after the '' | ||
| + | |||
| + | As we've said before, docstrings are string literals that occur as **the first statement** in a module, function, class, or method. However, it is important (and fair) to add that string literals can also occur in many other places in Python code, and still serve as documentation. And even though they may no longer be accessible as runtime object attributes, they can still be extracted by some specific software tools (for more information about these, consult PEP 256, which provides information about // | ||
| + | |||
| + | And now, without going into too many details, it's enough to tell you that we distinguish two kinds of such "extra dosctrings" | ||
| + | |||
| + | * **attribute docstrings**, | ||
| + | * **additional dosctrings**, | ||
| + | |||
| + | == How to create docstrings | ||
| + | Docstrings should be surrounded by triple double quotes (""" | ||
| + | <code python> | ||
| + | """ | ||
| + | ...</ | ||
| + | If you need to use any backslashes in your docstrings, then you should follow the r""" | ||
| + | |||
| + | == One-line vs. multi-line docstrings | ||
| + | There are two forms of docstrings. And even though each of them serves the same purpose (i.e. is supposed to provide documentation), | ||
| + | |||
| + | * **one-line docstrings** – they are used for simple and short descriptions, | ||
| + | * **multi-line docstrings** – they are used for more difficult cases, and should consist of a summary line followed by one blank line and a more elaborate description. | ||
| + | Let's talk a bit more about each of them. | ||
| + | |||
| + | === One-line docstrings | ||
| + | **One-line docstrings** should be used for rather simple, obvious, and short descriptions. They should take up one line only, and be surrounded by triple double quotes (the closing quotes should be on the same line as the opening quotes as this helps to keep the code clean and elegant). | ||
| + | |||
| + | Important notes: | ||
| + | |||
| + | * a docstring should begin with an upper-case letter (unless an identifier begins the sentence) and end with a period; | ||
| + | * a docstring should prescribe the code segment' | ||
| + | <code python> | ||
| + | def greeting(name): | ||
| + | """ | ||
| + | return name * 2 | ||
| + | </ | ||
| + | * a docstring should not just simply repeat the function or method parameters. For example: | ||
| + | <code python ❌>def my_function(x, | ||
| + | """ | ||
| + | ...</ | ||
| + | <code python ✔> | ||
| + | def my_function(x, | ||
| + | """ | ||
| + | ...</ | ||
| + | * Do not use a blank line above or under a one-line docstring unless you're documenting a class, in which case you should put a blank line after all the docstrings that document it: | ||
| + | <code python ❌>def calculate_tax(x, | ||
| + | |||
| + | """ | ||
| + | | ||
| + | return (x+y) * 0.25 | ||
| + | </ | ||
| + | === Multi-line docstrings | ||
| + | Multi-line docstrings should be used for non-obvious cases and more detailed descriptions of code segments. They should have a summary line, similar to what a one-line docstring looks like, followed by a blank line and a more elaborate description. The summary line may be located on the same line as the open triple double quotes, or put on the next line. The end quotes should be put on a separate line. | ||
| + | |||
| + | Important notes: | ||
| + | |||
| + | * a multi-line docstring should be indented to the same level as the open quotes, for example: | ||
| + | <code python> | ||
| + | def king_creator(name=" | ||
| + | """ | ||
| + | | ||
| + | Keyword arguments: | ||
| + | :arg name: the king's name (default: Greg) | ||
| + | :type name: str | ||
| + | :arg ordinal: Roman ordinal number (default: I) | ||
| + | :type ordinal: str | ||
| + | :arg country: the country ruled (default: Neverland) | ||
| + | :type country: str | ||
| + | """ | ||
| + | if name == " | ||
| + | return " | ||
| + | ... | ||
| + | </ | ||
| + | * you should insert a blank line after all the multi-line docstrings that are documenting a class; | ||
| + | * script docstrings (in the sense of stand-alone programs/ | ||
| + | * module docstrings should list the classes, exceptions, and functions exported by the module; | ||
| + | * package docstrings (understood as the docstring of the package' | ||
| + | * docstrings for functions and class methods should summarize their behavior and provide information about the arguments (including optional arguments), values, exceptions, restrictions, | ||
| + | * class docstrings should also summarize its behavior as well as document the public methods and instance variables. For example: | ||
| + | <code python> | ||
| + | """ | ||
| + | | ||
| + | Attributes: | ||
| + | ----------- | ||
| + | vehicle_type: | ||
| + | The type of the vehicle, e.g. a car. | ||
| + | id_number: int | ||
| + | The vehicle identification number. | ||
| + | is_autonomous: | ||
| + | self-driving -> True, not self-driving -> False | ||
| + | |||
| + | | ||
| + | Methods: | ||
| + | -------- | ||
| + | report_location(lon=45.00, | ||
| + | Print the vehicle id number and its current location. | ||
| + | (default longitude=45.00, | ||
| + | """ | ||
| + | | ||
| + | def __init__(self, | ||
| + | """ | ||
| + | Parameters: | ||
| + | ----------- | ||
| + | vehicle_type: | ||
| + | The type of the vehicle, e.g. a car. | ||
| + | id_number: int | ||
| + | The vehicle identification number. | ||
| + | is_autonomous: | ||
| + | self-driving -> True (default), not self-driving -> False | ||
| + | """ | ||
| + | | ||
| + | self.vehicle_type = vehicle_type | ||
| + | self.id_number = id_number | ||
| + | self.is_autonomous = is_autonomous | ||
| + | | ||
| + | def report_location(self, | ||
| + | """ | ||
| + | Print the vehicle id number and its current location. | ||
| + | | ||
| + | Parameters: | ||
| + | ----------- | ||
| + | id_number: int | ||
| + | The vehicle identification number. | ||
| + | lon: float, optional | ||
| + | The vehicle' | ||
| + | lat: float, optional | ||
| + | The vehicle' | ||
| + | """ | ||
| + | |||
| + | ... | ||
| + | ... | ||
| + | ... | ||
| + | </ | ||
| + | === Docstring formatting types | ||
| + | You may have noticed that we have used two different docstring formats for documenting the '' | ||
| + | |||
| + | Both formatting types are good for the purposes of creating formal documentation, | ||
| + | |||
| + | Sphinx is a great tool for creating documentation for software development projects. It uses reStructuredText as its markup language, and has a lot of useful features, such as supporting the HTML output format, automatic testing of code snippets, extensive cross-references, | ||
| + | |||
| + | == How to document a project | ||
| + | When documenting a Python project, depending on the nature of the project (i.e. private, shared, public, open source/ | ||
| + | |||
| + | This means you can easily improve their experience by thinking about how they' | ||
| + | |||
| + | Generally, a project should contain the following documentation elements: | ||
| + | |||
| + | * a **readme**, which provides a brief summary of the project, its purpose, and possibly some installation guidelines; | ||
| + | * an **examples.py** file, which is a script that demonstrates a few examples of how to utilize the project; | ||
| + | * a **license** in the form of a txt file (particularly important for Open Source and Public Domain projects) | ||
| + | * a **how to contribute** file which provides information about the possible ways of contributing to the project (shared, open source, and public domain projects). | ||
| + | Because documenting your code can be a rather exhausting and time-consuming activity, you are definitely encouraged to use some of the tools that could help you auto-generate documentation in the desired format, and deal with documentation updates and versioning in an effective and efficient way. | ||
| + | |||
| + | There are many documentation tools and resources available, such as Sphinx, which we've already mentioned, or the highly popular pdoc, and many more. We encourage you to follow this path. | ||
| + | |||
| + | == Linters and fixers | ||
| + | How do you maintain the good quality of your code? Well, you already know that you can follow the style guides such as PEP 8 or PEP 257, and write your code in a readable and consistent way. You can (and possibly should) adopt the Zen of Python philosophy, with all its good advice for writing an elegant and maintainable code, and use the type hinting mechanism. You can observe how others write code and document it as part of their projects (Look at the Python Standard Library or the Requests library), and learn from them. Finally, you can use // | ||
| + | |||
| + | Right. But what is a **linter**? Well, it's a tool that helps you write your code, because it **analyzes it for any stylistic anomalies and programming errors against a set of pre-defined rules**. In other words, it's a program that analyzes your code and reports such issues as structural and syntax errors, consistency breakups, and a lack of compatibility with best practices or code style guidelines such as PEP 8. The most popular linters are: Flake8, Pylint, Pyflakes, Pychecker, Mypy, and Pycodestyle (formerly Pep8) – the official linter tool to check Python code against PEP 8 conventions. | ||
| + | |||
| + | A **fixer**, on the other hand, is a program that helps you fix these issues and format your code to be consistent with the adopted standards. The most popular fixers are: Black, YAPF, and autopep8. | ||
| + | |||
| + | Most editors and IDEs (e.g. PyCharm, Spyder, Atom, Sublime Text, Visual Studio, Eclipse + PyDev, VIM, or Thonny) support linters, which means you can run them in the background as you write code. This makes it possible to detect, highlight, and identify many problem areas in your code, such as typos, wrong tabbing and indentation issues, function calls with the wrong number of arguments, stylistic inconsistencies, | ||
| + | |||
| + | That being said, we encourage you to explore the territory of linters and fixers yourself, and start using them to maintain high-quality Python code, and simply make your life easier. | ||
| + | |||
| + | == How to access docstrings | ||
| + | We've nearly made it to the end of our journey with PEP 257 and docstrings. The last question that still remains to be fully answered is: how can we actually access docstrings? | ||
| + | |||
| + | We do it by using the Python __doc__ attribute – if any string literals are present after the definition of a function/ | ||
| + | |||
| + | Run the code in the editor to see what happens. Your output should be like this:< | ||
| + | |||
| + | A more elaborate description of the function. | ||
| + | |||
| + | Parameters: | ||
| + | a: int (description) | ||
| + | b: int (description) | ||
| + | |||
| + | Returns: | ||
| + | int: Description of the return value.</ | ||
| + | But there' | ||
| + | </ | ||
| + | Run the code and see what happens. What are your conclusions? | ||
| + | |||
| + | As you can see, the output is lengthier and more descriptive:< | ||
| + | |||
| + | my_fun(a, b) | ||
| + | The summary line goes here. | ||
| + | | ||
| + | A more elaborate description of the function. | ||
| + | | ||
| + | Parameters: | ||
| + | a: int (description) | ||
| + | b: int (description) | ||
| + | | ||
| + | Returns: | ||
| + | int: Description of the return value.</ | ||
| + | Now try to access the docstrings of any of the Python built-in functions (e.g. print()). Then import a module and access the module documentation. Experiment with the %%__doc__%% method and the '' | ||
| + | |||
| + | You've learned a lot. You can be proud of yourself! | ||
| + | |||
| + | <code python> | ||
| + | """ | ||
| + | |||
| + | A more elaborate description of the function. | ||
| + | |||
| + | Parameters: | ||
| + | a: int (description) | ||
| + | b: int (description) | ||
| + | |||
| + | Returns: | ||
| + | int: Description of the return value. | ||
| + | """ | ||
| + | return a*b | ||
| + | |||
| + | print(my_fun.__doc__) | ||
| + | </ | ||