среда, 9 декабря 2015 г.

Создание и работа с модулем в Python

Общие представления


Python прекрасно поддерживает модульный стиль программирования или разбиение кода на модули. Сам по себе модуль, ничто иное, чем файл с набором классов(про это мы будем говорить в следующих темах), их методов и функций. Чрезвычайно удобно распределить код по разным файлам, каждый из которых будет отвечать за решение определенной области задач.
Как мы видим, распределение кода на какие-то составляющие структуры для облегчения сопровождения и разработки кода, пронизывает все идеологии большинства стилей проектирования и программирования. Первыми "контейнерами" для "упаковки" кода в отдельные блоки, с которыми мы познакомились, были функции. Мы говорили о том, что каждая функция должна решать конкретную задачу, а теперь мы говорим, что каждый модуль должен решать свою область задач, как это? Допустим, что у нас есть набор функций, которые решают сугубо математические задачи: (Корень с числа, взятие производной, вычисление синуса, косинуса). Каждая функция будет решать свою задачу, но мы имеем возможность все эти функции, собрать в одном модуле, который в целом будет решать задачу математических расчетов. Такой модуль уже есть в Python и называется он "math", но про него мы вспомним дальше. Если бы мы писали какое-то приложение, и оно должно было бы уметь работать с базой пользователей, удобным решением стало бы вынесение всего функционала, который отвечает за работу с базой в отдельный модуль - это может быть регистрация, авторизация, поиск пользователя в базе и Т.Д. Меню с которыми должен работать наш гипотетический пользователь в нашем гипотетическом приложении, тоже удобно было бы реализовать отдельным модулем. Функционал для работы с работы с заметками пользователя, опять таки удобно было бы вынести в отдельный модуль. Все эти модули, мы имеем возможность подключить в нашем основном файле и использовать их функционал в необходимых местах нашего кода.

Основная мощь Python, кроется в его модулях и библиотеках. А библиотека - это набор модулей. Библиотека, которая позволяет создавать "gui" - graphic user interface (графический пользовательский интерфейс) - это привычные для нас кнопочки, флажки, переключатели... Будет состять из модулей: модуль отвечающий за работу с текстовыми полями ввода, модуль отвечающий за работу с разнообразными кнопками, модуль отвечающий за отображение картинок/фотографий и Т.Д., а в  соответствующих модулях располагается необходимый функционал для работы с этими структурами. Только базовых, входящих в стандартный комплект Python, модулей и библиотек около 300 и еще больше сторонних, которые мы можем брать в интернете и устанавливать для Python. Они помогают решать тысячи разнообразных задач: Создание графических интерфейсов, работа с базами данных, совершение математических расчетов, работа с аудио/видео, создание игр, создание сайтов, обработка текстов, создание серверов и клиентов, дополнительные возможности работы с операционной системой, работа с файловой таблицей(файлами и папками), работа с E-Mail протоколами, работа с разными API для доступа к некоторым функциям сторонних приложений, работа с датой и временем и сотнями других различных направлений. В рамках нашего обучения, мы не сможем охватить и пяти процентов существующих модулей и библиотек, но обычно это и не требуется. Со временем, вы овладеете "штатным" набором модулей, их будет не так и много, а в процессе работы, вы будете изучать необходимые именно вам модули и библиотеки. Также не всегда есть необходимость знать весь возможный функционал модуля или библиотеки, иногда требуется знание только какой-то определенной части функционала. Не стоит волноваться, что требуется изучить еще так много, на самом деле вам нужно будет изучить гораздо больше, чем вы можете представить. Названия и описание модулей и библиотек входящих в базовую поставку Python, вы можете найти на официальном сайте или на его аналогах. Сторонние модули и библиотеки, вам помогут найти, как поисковик так и ресурсы, посвященные программированию вобщем и Python вчастности.

Модуль и оператор import


Для начала, мы создадим свой маленький модуль, что даст более глубокое понимание происходящих при их использовании процессов.
Создадим файл с названием "geteven.py" и объявим в нем 3 функции:
#-*- coding:utf-8 -*-
def even(l):
    """ Функция возвращает количество четных чисел переданного списка """
    p = 0
    for i in l:
        if i%2 == 0:
            p += 1
    return p

def uneven(l):
    """ Функция возвращает количество нечетных чисел переданных в списке """
    p = 0
    for i in l:
        if i%2 != 0:
            p += 1
    return p

def iseven(n):
    """ Функция возвращает True, если переданное число четное, или False, если нечетное """
    if n%2 == 0:
        return True
    return False

Сохраним этот файл и откроем рядом еще один с произвольным названием.

Первый способ импортирования модуля в свою программу с которым мы познакомимся, будет производиться при помощи оператора import. Для импорта нашего модуля "geteven.py", нам необходимо загрузить его следующим образом:
import geteven
Обратите внимание, что расширение ".py", мы не указываем. "import", загружает модуль в свое пространство имен. Далее, мы рассмотрим пространства имен, которые появляются при загрузке модулей через "import". Просто запустите ваше приложение на исполнение и выполните функцию "dir()".
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'geteven']
>>>

Как видно, у нас появился некий "geteven". Вы помните, что в Python, все связи - это: (Имя, ссылка, объект). Упростив, мы можем утверждать, что была создана переменная "geteven", которая ссылается на объект загруженного модуля. То, что это именно модуль, мы можем проверить при помощи функции "type()".
>>> type(geteven)

>>>

Если вы просто обратитесь к имени этого модуля, вы получите более подробную информацию, что и откуда было подгружено:
>>> geteven

>>>

В данном случае, отобразился путь к моему файлу с модулем. Чтобы увидеть функции и аттрибуты модуля, мы просто воспользуемся функцией "dir()", передав ему имя модуля:
>>> dir(geteven)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'even', 'isev
en', 'uneven']
>>>

Вот теперь мы можем наблюдать наши функции. Такие аттрибуты как: "__name__, __file__", Отвечают за название и путь к файлу модуля:
>>> geteven.__name__
'geteven'
>>> geteven.__file__
'D:\\development\\Backup_notepad++\\geteven.py'
>>>

Для обращения к чему-то в пространстве имен модуля, мы используем уже привычную нам синтаксическую конструкцию, вида "имя пространства.имя к которому обращаемся". Далее станет очевидно, что одно пространство имен, может быть вложено во-второе пространство имен, а уже в нем будут располагаться необходимые функции/аттрибуты. Я надеюсь, что теперь очевидно, как мы можем воспользоваться нашими функциями:
>>> geteven.iseven(3)
False
>>> geteven.even([2,5,3,1,6,5,3])
2
>>> geteven.uneven([2,5,3,1,6,5,3])
5
>>>

Как видно, наши функции отлично работают после загрузки файла в качестве модуля, где они были описаны.

import/as


Синтаксическая конструкция  с оператором "import", имеет еще одну форму, которая носит исключительно декоративный, хотя иногда удобный, характер. Допустим, что имя модуля длинное, или просто неудобное для набора, чтобы облегчить себе жизнь, мы можем применить следующий вид импортирования модуля:
import geteven as ge
Все, что позволяет сделать такой вид импортирования, это поместить содержимое модуля не в пространство имен "geteven", а в пространство того имени, которое мы указали после "as", Т.Е. "ge". Очевидно, что теперь для обращения к функциям нашего модуля, мы должны обращаться к имени "ge".

Пространство имен модулей


Мы уже знаем, что оператор "import", подгружает модуль в его собственное пространство имен и, чтобы обратиться к содержимому импортированного модуля, мы обращаемся вначале к имени этого пространства, а затем к содержимому пространства. Тут мы можем провести аналогию с объектами и их методами. Объектов одного типа данных может быть несколько, методы у них одинаковые, но они не перемешиваются именно потому, что находятся в своих пространствах имен. Также и модуль попадает в свое пространство. Путаница может возникнуть только при попытке понять, что у каждого модуля глобальная область видимости своя и она никак не пересекается с глобальной областью видимости других модулей, или с глобальной областью видимости пространства, где модуль будет импортирован. Единственная, в действительности глобальная область - это область "__builtins__", Где находятся все встроенные функции Python. Давайте разберем это подробнее. Определим в начале нашего файла модуля "even.py" переменную list_even, присвоив ей пустой список:
list_even = []"/b>
И сразу опишем две функции, назначение которых мы разберем далее:
def set_even(l):
    global list_even
    list_even = l

def get_even():
    return list_even

Для полноты картины, отредактируем функции "even" и "uneven", чтобы они по умолчанию, принимали аргумент со значением "None" и, если при вызове в функцию явно не передается список, бралось значение переменной "list_even":
def even(l=None):
    """ Функция возвращает количество четных чисел переданного списка """
    if not l:
        l = list_even
    p = 0
    for i in l:
        if i%2 == 0:
            p += 1
    return p

def uneven(l=None):
    """ Функция возвращает количество нечетных чисел переданных в списке """
    if not l:
        l = list_even
    p = 0
    for i in l:
        if i%2 != 0:
            p += 1
    return p

После всех этих манипуляций, содержимое нашего файла, должно выглядеть следующим образом:
#-*- coding:utf-8 -*-

list_even = []

def even(l=None):
    """ Функция возвращает количество четных чисел переданного списка """
    if not l:
        l = list_even
    p = 0
    for i in l:
        if i%2 == 0:
            p += 1
    return p

def uneven(l=None):
    """ Функция возвращает количество нечетных чисел переданных в списке """
    if not l:
        l = list_even
    p = 0
    for i in l:
        if i%2 != 0:
            p += 1
    return p

def iseven(n):
    """ Функция возвращает True, если переданное число четное, или False, если нечетное """
    if n%2 == 0:
        return True
    return False

def set_even(l):
    global list_even
    list_even = l

def get_even():
    return list_even

Собственно, мы пытаемся сымитировать поведение, скорее объекта, чем модуля, но тем не менее - это вполне корректный пример. Теперь запустим наш основной файл, где импортируется модуль "geteven" на исполнение.
Давайте проанализируем, какие изменения мы внесли в модуль.
Во-первых, мы создали переменную "list_even" в глобальной области видимости модуля;
во-вторых, мы создали две функции "get_even" и "set_even".
Те, кто знают такие языки, как Java или c#, найдут то, что этими функциями я попытался сымитировать, так называемые Аксессоры(accessors) или, как их еще называют, гетеро-сетеры. Явно таких понятий в Python нету, но общие рекомендации и стиль использования остается такой же. Сейчас мы еще вернемся к понятию и использованию гетеро-сетеров.
Также мы изменили наши функции "even" и "uneven" таким образом, чтобы они имели аргумент со значением по умолчанию в "None" и, в случае, если пользователь не передал список явно при вызове, обращались к содержимому переменной "list_even".
Теперь посмотрим, что изменилось в наборе функций и аттрибутов нашего модуля:
>>> dir(geteven)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'even', 'get_
even', 'iseven', 'list_even', 'set_even', 'uneven']
>>>

Тут появился аттрибут "list_even" - переменная которую мы создали в модуле, также добавились 2 новых метода "get_even" и "set_even". Начнем с "list_even". Эта переменная была объявлена в глобальном пространстве имен модуля "geteven", но она не стала глобальной для всех пространств имен. Это можно легко проверить, попытавшись вызвать ее в нашем текущем пространстве:
>>> list_even
Traceback (most recent call last):
  File "", line 1, in
NameError: name 'list_even' is not defined
>>>

Приэтом, мы спокойно можем обратиться к этой переменной в области видимости нашего модуля:
>>> geteven.list_even
[]
>>>

Такая механика, позволяет не смешивать области видимости подгружаемых модулей, что помогает избежать конфликта одинаковых имен и аттрибутов в модулях. Допустим, что в каком-то подгружаемом модуле, есть функция "open()" и она была бы загружена в нашу текущую область видимости, что произойдет? Очень просто, мы потеряем возможность использовать наш классический "open()" для открытия файлов. Но если функция "open()" будет загружена в пространстве имен подгружаемого модуля, она никак не повлияет на работу функции в текущей области видимости.
Для чего мы создавали две дополнительные функции в модуле? Дело в том, что есть общепринятое соглашение, что напрямую к каким либо аттрибутам модулей, а в дальнейшем и создаваемых классов/объектов, обращаться - это не самая лучшая тактика. Для этого, создаются функции/методы(для объектов), которые позволяют читать и передавать какие-то значения для аттрибута. Более глубоко мы затронем эту тему, когда перейдем к объектно-ориентированному программированию в Python. Именно эти функции/методы, называются акссессоры или гетеро-сетеры.
Функция "set_even", позволяет присвоить переменной "list_even" какое-то значение, а функция "get_even", получить это значение. Еще раз напомню, что мы можем обращаться к этой переменной в области видимости нашего модуля и напрямую:
>>> geteven.list_even = [1,2,3,4,5]
>>> geteven.list_even
[1, 2, 3, 4, 5]
>>>

Но лучше это делать через наши функции:
>>> geteven.set_even([1,3,5,7,9])
>>> geteven.get_even()
[1, 3, 5, 7, 9]
>>>

Если вы внимательно посмотрите на код этих функций, вы убедитесь, что они работают с переменной "list_even", как с глобальной переменной, но происходит это в глобальной области видимости нашего модуля.
Теперь мы установили некое значение для аттрибута "list_even" и можем воспользоваться функциями "even" и "uneven", не передавая им никаких аргументов, тогда значение будет взято из аттрибута "list_even":
>>> geteven.even()
0
>>> geteven.uneven()
5
>>>


from/import


Помимо оператора "import", существует еще одна синтаксическая конструкция, которая позволяет загружать модули в наш код. Она выглядит следующим образом:
from имя модуля import "*(для загрузки всего содержимого модуля)" | "конкретные функции/аттрибуты"
Сейчас мы рассмотрим это более подробно, но для начала уточним разницу между "import" и "from/import". Последний вариант, загружает содержимое модуля в текущее пространство имен. Помните пример с функцией "open()?" Так вот, В этом случае, эта функция была бы загружена в текущую область видимости и заменила бы собой встроенную функцию "open()".
Давайте загрузим все содержимое модуля "geteven", для этого в файле, где мы импортируем модуль, напишем следующее:
from geteven import *
Запустим код на выполнение и посмотрим, что мы получили в текущей области видимости:
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'even', 'get_even', 'iseven', 'list_even', 'set_even', 'uneven']
>>>

Все содержимое модуля, было импортировано непосредственно в текущую область видимости. Это никак не сказывается на работе функций, они попрежнему будут выполнять свою роль. Но есть одна деталь. Переменная list_even, не будет изменяться при использовании "set_even", а остальные функции, будут обращаться не к "list_even", которая находится в текущем пространстве, а к "list_even", которая находится в области видимости модуля. Давайте проследим за следующим:
>>> set_even([1,2,3,4,5])
>>> list_even
[]
>>> get_even()
[1, 2, 3, 4, 5]
>>> import geteven # Подгружаем модуль через import
>>> list_even
[]
>>> get_even()
[1, 2, 3, 4, 5]
>>> geteven.list_even
[1, 2, 3, 4, 5]
>>> list_even = None
>>> get_even()
[1, 2, 3, 4, 5]
>>> set_even([9,8,7,6,5,4,3,2,1])
>>> list_even
>>> geteven.list_even
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>>

Если внимательно проследить за этими изменениями, можно сделать вывод, что для функций, даже если они попали в текущую область видимости, глобальной областью видимости, остается модуль с которого они были импортированы.

Также мы можем подгрузить только необходимые функции модуля, вместо символа "*", указав их имена через запятую:
from geteven import even, uneven
А теперь запустим код на выполнение и посмотрим:
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'even', 'uneven']
>>>


Результат"/h3>
Тема получилась не такой и маленькой, причем мы не затронули определенные аспекты модульной иерархии в Python. Кпримеру объединение модулей в библиотеку, но эти знания, необходимы в большинстве, разработчикам подобного инструментария и выходят за рамки данного курса. В следующих темах, мы рассмотрим несколько модулей и библиотек из стандартного набора Python, но основную массу вы будете изучать по соответствующим книгам, документациям, гайдам и туториалам.

1 комментарий:

  1. ВСЕ ПРОЧИТАЙТЕ НАСТОЯЩЕЕ ОТЗЫВ О том, КАК Я ПОЛУЧИЛ СВОЙ КРЕДИТ ОТ КОМПАНИИ LEGIT И ДОВЕРЕННОЙ КРЕДИТНОЙ СРЕДИ Меня зовут Kjerstin Lis, я искал кредит для погашения своих долгов, все, кого я встречал, мошенничали и брали свои деньги, пока я наконец не встретил мистера Бенджамина Брейл Ли Он смог дать мне кредит в размере 450 000 рублей. Он также помог другим моим коллегам. Я говорю как самый счастливый человек во всем мире сегодня, и я сказал себе, что любой кредитор, который спасает мою семью от нашей бедной ситуации, я скажу имя всему миру, и я так счастлив сказать, что моя семья вернулся навсегда, потому что я нуждался в кредите, чтобы начать свою жизнь заново, потому что я одинокая мама с 3 детьми, и весь мир, казалось, висел на мне, пока я не имел в виду, что БОГ послал кредитора, который изменил мою жизнь и член моей семьи, БОЖИЙ кредитор, мистер Бенджамин, он был Спасителем БОГом, посланным для спасения моей семьи, и сначала я подумал, что это будет невозможно, пока я не получу кредит, я пригласил его к себе в семью -все вечеринка, от которой он не отказался, и я посоветую всем, кто действительно нуждается в кредите, связаться с г-ном Бенджамином Брейлом Ли по электронной почте (lfdsloans@outlook.com), потому что он самый понимающий и добрый кредитор. когда-либо встречал с заботливым сердцем. Он не знает, что я делаю это, распространяя свою добрую волю ко мне, но я чувствую, что должен поделиться этим со всеми вами, чтобы освободить себя от мошенников, пожалуйста, остерегайтесь подделок и свяжитесь с правильной кредитной компанией. com или whatsapp + 1-989-394-3740. ,

    ОтветитьУдалить