{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# __init__.py\n", "Before Python3.3, every package must contain a `__init__.py`. \n", "It's run automatically the first time a Python program imports this package. \n", "\n", "Although after Python3.3, `__init__.py` is no longer a must, using `__init__.py` still provides a performance advantage. \n", "\n", "\n", "## What does __init__.py do?\n", "\n", "### 1. Package initialization\n", "All the code in the directory's `__init__.py` is run automatically. \n", "Can be used to initialize the state \n", "e.g. initialize file to create required data files, open connections to databases\n", "\n", "### 2. Module usability declarations\n", "Declare that a directory is a Python Package \n", "In fact, `__init__.py` are often empty in practice\n", "\n", "\n", "## from * statament behavior\n", "You can use `__all__` list in `__init__.py` to define what is exported when this directory is imported using `from *` statement\n", "\n", "```python\n", "# c.py\n", "\n", "x = 1\n", "y = 2\n", "__all__ = [x]\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n" ] }, { "ename": "NameError", "evalue": "name 'y' is not defined", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# not imported since it's not in __all__\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mNameError\u001b[0m: name 'y' is not defined" ] } ], "source": [ "from c import *\n", "\n", "print(x)\n", "print(y) # not imported since it's not in __all__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Package Relative Imports\n", "\n", "How it works is version dependent\n", "- Python2 implicitly searches package directories first on imports\n", "- Python3 requires explicit relative import syntax to making same-package imports\n", " - This change enhances code readability by making same-package imports more obvious\n", " \n", "The directory used relative import can no longer be used as executable program." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Chagnes in Python3\n", "- Absoulte Import\n", " - Module searching path skips the package's own directory by default. \n", " - Imports only check **sys.path**\n", "- Relatvie Import\n", " - Extend **from** to allow imports searching the package's directory only\n", " \n", "## Relatvie Import Basics\n", "- Import with dots: **from . import mod**\n", " - Imports should be relatvie-only to the containing package\n", " - Won't look for same-named modules elsewhere including **sys.path**\n", "- Import without dots: **import m**, **from m import x**\n", " - Python2: relative-then-absolute\n", " - Search the packages's own directoy first and than **sys.path**\n", " - Python3: absolute-only\n", " - Skip the containing package and look up **sys.path**\n", "- Relatvie imports apply to **from** only, not **import**\n", "```python\n", "from . import mod1\n", "from .mod1 import name\n", "from .. import mod2\n", "```" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "ename": "SystemError", "evalue": "Parent module '' not loaded, cannot perform relative import", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mSystemError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[1;33m.\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0ma\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mSystemError\u001b[0m: Parent module '' not loaded, cannot perform relative import" ] } ], "source": [ "from . import a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Examples (Python2 vs Python3)\n", "\n", "#### Example 1\n", "- Code\n", "\n", "```python\n", "# code/pkg/spam.py\n", "import string\n", "print(string)\n", "\n", "# code/pkg/string.py\n", "print('Ni' * 8)\n", "```\n", "\n", "- Result\n", "\n", "```\n", "$ python3\n", ">>> import pkg.spam\n", "\n", "\n", "$ python2\n", ">>> import pkg.spam\n", "NiNiNiNiNiNiNiNi\n", "\n", "```\n", "\n", "#### Example 2\n", "- Code\n", "\n", "```python\n", "# code/string.py\n", "print('string' * 8)\n", "\n", "# code/pkg/string.py\n", "print('Ni' * 8)\n", "\n", "# code/pkg/spam.py\n", "import string\n", "print(string)\n", "```\n", "\n", "- Result\n", "\n", "```\n", "$ python3\n", ">>> import pkg.spam\n", "stringstringstringstringstringstringstringstring\n", "\n", "\n", "$ python2\n", ">>> import pkg.spam\n", "NiNiNiNiNiNiNiNi\n", "\n", "```\n", " \n", "### Absolute import in Python2\n", "```python\n", "# Use absoulte import in Python2\n", "from __future__ import absolute_import \n", "```\n", "\n", "\n", "## The Scope of Relative Imports\n", "- Applys to imports within packages only\n", " - *Normal imports in files not used as part of a package still search the directory containing the top-level script first*\n", " - Modules referenced by relative imports must exist in the package\n", " - Package relatvie import syntax is not even allowed in a file not being used as a package\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pitfalls of Package Relatvie Imports\n", "Must choose a single mode - package(relative imports) or program(simple imports)\n", "\n", "```python\n", "from . import mod # Not allowed in nonpackage in both Python2 and Python3\n", "import mode # Does not search file's own directory in package in Python3\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Namespace Package (Python3.3)\n", "An extension to the import model. \n", "When Python finds directories without finding `__init__.py`, it creates a namespace package that is the virtual concatenation to all found directories having the requested module name." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is mod.py\n" ] } ], "source": [ "# ns/mod.py (without __init__.py)\n", "\n", "import ns.mod" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ns" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "_NamespacePath(['C:\\\\Users\\\\smszw\\\\Desktop\\\\zw-public\\\\code\\\\Learning_Python\\\\Part 5 - Modules and Packages\\\\ns'])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ns.__path__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Cannot contain an **`__init__.py`** (Since it stops the following algorithm) \n", "- May span multiple directories that are collected at import time \n", "- Relative imports work in namespace packages too \n", "\n", "## The Import Algorithm\n", "1. If **`directory/module/__init__.py`** is found, a regular package is imported\n", "2. If **`directory/module.{py, pyc ...}`** is found, a simple module is imported\n", "3. If **`directory/module`** is found and is a directory, it's record and the scan continues with the next directory in the search path\n", "4. If none of the above was found, the scan continues with the next directory in the search path.\n", "\n", "If the search path scan completes without returning a module or package by steps 1 or 2, and at least one directory was recorded by step 3, than a namespace package is created. \n", "\n", "Once a namespace package is created, there is no difference between it and a regular package. \n", "\n", "## Optional __init__.py ?\n", "Many packages require no initialization code. \n", "Thus, they are no longer required after Python3.3 \n", "\n", "However, there is performance advantage to have one. \n", "With namespace packages, all entries in the path must be scanned. \n", "Compare to it, regular packages stop the algorithm at step 1. " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.0" } }, "nbformat": 4, "nbformat_minor": 1 }