Call python: Call Protocol — Python 3.9.5 documentation
Операция вызова: __call__ в Python
Метод __call__ вызывается при обращении к экземпляру как к функции. Это не повторяющееся определение — если метод __call__ присутствует, интерпретатор будет вызвать его, когда экземпляр вызывается как функция, передавая ему любые позиционные и именованные аргументы:
>>> class CallMe:
def __call__(self, *args, **kwargs): # Реализует вызов экземпляра
print(‘Called:’, args, kwargs) # Принимает любые аргументы
…
…
>>> C = CallMe()
>>> C(4,5,6)
Called: (4,5,6) {}
>>> c(5,6,7, x = 10, y = 20)
Called: (5,6,7) {‘x’ : 10, ‘y’: 20 }
| >>> class CallMe: def __call__(self, *args, **kwargs): # Реализует вызов экземпляра print(‘Called:’, args, kwargs) # Принимает любые аргументы … … >>> C = CallMe() >>> C(4,5,6) Called: (4,5,6) {}
>>> c(5,6,7, x = 10, y = 20) Called: (5,6,7) {‘x’ : 10, ‘y’: 20 } |
Выражаясь более формальным языком, метод __call__ поддерживает все схемы передачи аргументов. Все что передается экземпляру, передается этому методу наряду с обычным аргументом self, в котором передается сам экземпляр. Например, следующие определения метода:
class C:
def __call__(self, a, b, x=50, y=60): # Обычные и со значениями по умолчанию
pass
class C:
def __call__(self, *args, **kwargs): # Произвольные аргументы
pass
class C:
def __call__(self, *args, x = 100, **kwargs): # Аргументы которые могут
pass # передаваться только по имени
| class C: def __call__(self, a, b, x=50, y=60): # Обычные и со значениями по умолчанию pass
class C: def __call__(self, *args, **kwargs): # Произвольные аргументы pass
class C: def __call__(self, *args, x = 100, **kwargs): # Аргументы которые могут pass # передаваться только по имени |
соответствует следующим вызовам экземпляра:
X = C()
X(1, 2) # Аргументы со значениями по умолчанию опущены
X(1, 2, 3, 4) # Позиционные
X(a = 10, b = 20, c = 30) # Именованные
X(*[1, 2], **dict(c=3, d=4)) # Распаковывание произвольных аргументов
X(1, *(2,), c=3, **dict(d=4)) # Смешанные режимы
| X = C() X(1, 2) # Аргументы со значениями по умолчанию опущены X(1, 2, 3, 4) # Позиционные X(a = 10, b = 20, c = 30) # Именованные X(*[1, 2], **dict(c=3, d=4)) # Распаковывание произвольных аргументов X(1, *(2,), c=3, **dict(d=4)) # Смешанные режимы |
Суть состоит в том, что классы и экземпляры, имеющие метод __call__, поддерживают тот же синтаксис и семантику передачи аргументов, что и обычные функции и методы.
Реализация операции вызова, как в данном примере, позволяет экземплярам классов имитировать поведение функций, а также сохранять информацию о состоянии между вызовами.
>>> class Prod:
def __init__(self, value): # Принимает единственный аргумент
self.value = value
def __call__(self, other):
return self.value * other
…
…
>>> X = Prod(3) # Запоминает 3 в своей области видимости
>>> X(3) # 3 (передано) * 3 (сохраненное значение)
9
>>> X(4)
12
| >>> class Prod: def __init__(self, value): # Принимает единственный аргумент self.value = value def __call__(self, other): return self.value * other … … >>> X = Prod(3) # Запоминает 3 в своей области видимости >>> X(3) # 3 (передано) * 3 (сохраненное значение) 9 >>> X(4) 12 |
В следующим примере реализация метода __call__ может показаться ненужной. То же самое поведение можно реализовать с помощью простого метода:
>>> class Prod:
def __init__(self, value): # Принимает единственный аргумент
self.value = value
def myFunc(self, other):
return self.value * other
…
…
>>> X = Prod(3) # Запоминает 3 в своей области видимости
>>> X.myFunc(3) # 3 (передано) * 3 (сохраненное значение)
9
>>> X.myFunc(4)
12
| >>> class Prod: def __init__(self, value): # Принимает единственный аргумент self.value = value def myFunc(self, other): return self.value * other … … >>> X = Prod(3) # Запоминает 3 в своей области видимости >>> X.myFunc(3) # 3 (передано) * 3 (сохраненное значение) 9 >>> X.myFunc(4) 12 |
Однако метод __call__ может оказаться удобнее при взаимодействии с прикладными интерфейсами, где ожидается функция — это позволяет создавать объекты, совместимые с интерфейсами, ожидающими получить функцию, которые к тому же способны сохранять информацию о своем состоянии между вызовами.
Фактически этот метод занимает третье место среди наиболее часто используемых методов перезагрузки операторов — после конструктора __init__ и методов форматирования __str__ и __repr__.
Где выгодно купить просмотры ВК на записи и видео? И сколько они стоят? Когда высокая цена оправдана и обоснована? Почему без денег не всегда хорошо? Сайт https://pricesmm.com/ предлагает результаты анализа платных способов и цен на просмотры Вконтакте.
Для наших читателей хочу показать хороший пример где наглядно показывается удобность метода __call__. В следующем примере мы создали класс который определяет факториал использую кэш а также рекурсивность.
class Factorial:
def __init__(self): # Конструктор не принимает аргументов
self.cache = {} # Создаем пустой словарь для кэша
def __call__(self, n):
if n not in self.cache:
if n == 0:
self.cache[n] = 1
else:
self.cache[n] = n * self.__call__(n-1) # Рекурсия!
return self.cache[n]
fact = Factorial()
| class Factorial: def __init__(self): # Конструктор не принимает аргументов self.cache = {} # Создаем пустой словарь для кэша def __call__(self, n): if n not in self.cache: if n == 0: self.cache[n] = 1 else: self.cache[n] = n * self.__call__(n-1) # Рекурсия! return self.cache[n]
fact = Factorial() |
Проверяем работоспособность нашего класса:
for i in range(10):
print(«{}! = {}».format(i, fact(i))) # fact(i) вызывается __call__
# вывод
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
| for i in range(10): print(«{}! = {}».format(i, fact(i))) # fact(i) вызывается __call__
# вывод 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 |
Модуль subprocess | Python 3 для начинающих и чайников
Модуль subprocess отвечает за выполнение следующих действий: порождение новых процессов, соединение c потоками стандартного ввода, стандартного вывода, стандартного вывода сообщений об ошибках и получение кодов возврата от этих процессов.
Рекомендуемым подходом к работе с подпроцессами является использование следующих вспомогательных функций для всех случаев, где они могут справиться. Для более сложных случаев может быть использован непосредственно интерфейс Popen.
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None) — выполняет команду, описанную args. Ожидает завершения команды, а затем возвращает код возврата.
Аргументы, приведенные выше, являются лишь наиболее распространенными из них. Полная сигнатура функция в значительной степени такая же, как конструктор Popen. Аргумент timeout передается Popen.wait(). Если тайм-аут истекает, дочерний процесс будет убит, а затем будет поднято исключение TimeoutExpired.
>>> subprocess.call(["ls", "-l"]) 0 >>> subprocess.call("exit 1", shell=True) 1
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None) — выполняет команду, описанную args. Ожидает завершения команды, а затем завершается, если код возврата 0, или поднимает исключение CalledProcessError, объект которого возвращает код завершения атрибутом returncode.
>>> subprocess.check_call(["ls", "-l"]) 0 >>> subprocess.check_call("exit 1", shell=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
subprocess.check_output(args, *, input=None, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None) — выполняет команду и возвращает её вывод. Поднимает исключение CalledProcessError, если код возврата ненулевой.
>>> subprocess.check_output(["echo", "Hello World!"]) b'Hello World!\n' >>> subprocess.check_output(["echo", "Hello World!"], universal_newlines=True) 'Hello World!\n' >>> subprocess.check_output(["sed", "-e", "s/foo/bar/"], ... input=b"when in the course of fooman events\n") b'when in the course of barman events\n' >>> subprocess.check_output("exit 1", shell=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
>>> subprocess.check_output( ... "ls non_existent_file; exit 0", ... stderr=subprocess.STDOUT, ... shell=True) ... 'ls: non_existent_file: No such file or directory\n'
Создание новых процессов и управление ими в данном модуле обрабатывается классом Popen. Он предлагает большую гибкость, так что разработчики могут справиться с менее распространенными случаями, не охваченными удобными функциями.
subprocess.DEVNULL — значение, которое может использоваться в качестве аргумента stdin, stdout или stderr. Означает, что будет использован специальный файл devnull.
subprocess.PIPE — значение, которое может использоваться в качестве аргумента stdin, stdout или stderr. Означает, что для дочернего процесса будет создан пайп.
subprocess.STDOUT — значение, которое может использоваться в качестве аргумента stderr. Означает, что поток ошибок будет перенаправлен в поток вывода.
Класс subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=()) — Выполняет программу в новом процессе. args – строка или последовательность аргументов программы. Обычно первым указывают исполняемую программу, а затем аргументы, но также ее можно указать в параметре executable.
Также Popen поддерживает менеджеры контекста:
with Popen(["ifconfig"], stdout=PIPE) as proc: log.write(proc.stdout.read())
Методы класса Popen:
Popen.poll() — если процесс завершил работу — вернёт код возврата, в ином случае None.
Popen.wait(timeout=None) — ожидает завершения работы процесса и возвращает код возврата. Если в течение timeout процесс не завершился, поднимется исключение TimeoutExpired (которое можно перехватить, после чего сделать ещё раз wait).
Этот метод может вызвать блокировку (зависание), если установлено stdout=PIPE или stderr=PIPE, и дочерний процесс генерирует большое количество данных в stdout и stderr. Использование communicate() позволит избежать этого.
Popen.communicate(input=None, timeout=None) — взаимодействовует с процессом: посылает данные, содержащиеся в input в stdin процесса, ожидает завершения работы процесса, возвращает кортеж данных потока вывода и ошибок. При этом в Popen необходимо задать значение PIPE для stdin (если вы хотите посылать в stdin), stdout, stderr (если вы хотите прочитать вывод дочернего процесса).
Если в течение timeout процесс не завершился, поднимется исключение TimeoutExpired (которое можно перехватить, после чего сделать ещё раз communicate, либо убить дочерний процесс).
Прочитанные данные буферизируются в память, поэтому не стоит применять этот метод в случае огромных выходных данных.
Popen.send_signal(signal) — посылает сигнал signal.
Popen.terminate() — останавливает дочерний процесс.
Popen.kill() — убивает дочерний процесс.
Продвинутый Python, часть 3: классы и метаклассы
Это завершающая статья цикла «Продвинутый Python», в которой пойдёт речь о классах и метаклассах. В первой части мы познакомились с итераторами, генераторами и модулем itertools, а во второй говорили о замыканиях, декораторах и модуле functools.
Классы как объекты
Классы в Python — это объекты, как и функции. Сразу после объявления класса Python создаёт объект класса и присваивает его переменной с именем класса. Классы — это объекты с типом type
. Из этого правила есть исключения, о которых пойдёт речь ниже.
Объекты класса можно вызывать, то есть в них есть метод __call__
. При вызове создаётся объект соответствующего класса. С классами можно обращаться как с другими объектами. Например, можно определять атрибуты, присваивать классы переменным, использовать их там, где требуется вызываемая сущность, например, в map
. Когда вы пишете map(str, [1, 2, 3])
, список чисел конвертируется в список строк, так как str
— это класс.
Посмотрите пример кода, чтобы ближе познакомиться с описанными особенностями.
>>> class C:
... def __init__(self, s):
... print(s)
...
>>> MyClass = C
>>> type(C)
<class 'type'>
>>> type(MyClass)
<class 'type'>
>>> MyClass(2)
2
<__main__.C object at 0x7fae87e0d208>
>>> list(map(MyClass, [1,2,3]))
1
2
3
[<__main__.C object at 0x7fae87e0d2e8>, <__main__.C object at 0x7fae87e0d358>, <__main__.C object at 0x7fae87e0d390>]
>>> list(map(C, [1,2,3]))
1
2
3
[<__main__.C object at 0x7fae87e0d3c8>, <__main__.C object at 0x7fae87e0d400>, <__main__.C object at 0x7fae87e0d438>]
>>> C.test_attribute = True
>>> MyClass.test_attribute
True
В некоторых языках, таких как C++, классы можно объявлять только на верхнем уровне модулей. В Python class
можно использовать внутри функции. Этот подход можно использовать, чтобы создавать классы на лету. Ниже пример:
>>> def make_class(class_name):
... class C:
... def print_class_name(self):
... print(class_name)
... return C
...
>>> C1, C2 = map(make_class, ["C1", "C2"])
>>> c1, c2 = C1(), C2()
>>> c1.print_class_name()
C1
>>> c2.print_class_name()
C2
>>> type(c1)
<class '__main__.make_class.<locals>.C'>
>>> type(c2)
<class '__main__.make_class.<locals>.C'>
>>> c1.print_class_name.__closure__
(<cell at 0x7fae89666558: str object at 0x7fae87e0d340>,)
В этом примере классы, созданные с помощью make_class
, это разные объекты. Поэтому объекты, созданные этими классами, имеют разный тип. В данном случае устанавливаем имя класса вручную после создания класса. Это похоже на работу с декораторами. Также заметьте, что метод print_class_name
созданного класса захватывает его замыкание, в котором есть class_name
. Если вы не очень уверенно работаете с замыканиями, самое время перечитать вторую статью из цикла «Продвинутый Python», в которой рассматривались замыкания и декораторы.
Если классы — это объекты, которые создают объекты, то как называются объекты, которые создают классы? Поверьте, это не загадка «яйцо или курица». Здесь есть чёткий ответ: такие объекты называются метаклассами. Самым простым метаклассом можно считать type
. Когда type
получает на вход один параметр, он возвращает тип объекта, переданного в качестве параметра. В данном случае он не работает как метакласс. Когда type
получает на вход три параметра, он работает как метакласс и создаёт класс на основе переданных параметров. В качестве параметров должны передаваться имя класса, родители (классы, от которых происходит наследование), словарь атрибутов. Последние два параметра могут быть пустыми. Вот пример кода:
>>> MyClass = type('MyClass', (object,), {'my_attribute': 0})
>>> type(MyClass)
<class 'type'>
>>> o = MyClass()
>>> o.my_attribute
0
Обратите внимание, второй параметр должен быть кортежем, поэтому синтаксис может выглядеть странно. Если вам нужны методы, создавайте функции и передавайте их как атрибуты. Вот пример:
>>> def my_class_init(self, attr_value):
... self.my_attribute = attr_value
...
>>> MyClass = type('MyClass', (object,), {'__init__': my_class_init})
>>> o = MyClass('test')
>>> o.my_attribute
'test'
Можно создавать свои метаклассы: сгодится любой вызываемый (callable) объект, который способен принять три параметра и вернуть объект класса. Такие метаклассы можно применять к классу. Метакласс можно указать при объявлении класса. Давайте рассмотрим этот приём на примере, который заодно продемонстрирует возможности метаклассов:
>>> def my_metaclass(name, parents, attributes):
... return 'Hello'
...
>>> class C(metaclass=my_metaclass):
... pass
...
>>> C
'Hello'
>>> type(C)
<class 'str'>
В примере выше C
оказывается переменной, которая указывает на строку 'Hello'
. Конечно, вряд ли кто-то в здравом уме будет писать такой код. Мы просто хотим посмотреть, как работают метаклассы. Теперь давайте создадим что-то более практичное. В предыдущей статье серии мы видели, как с помощью декоратора можно логировать каждый метод в классе. Давайте сделаем то же самое с помощью метакласса. Позаимствуем декоратор logged
из поста о декораторах и итераторах.
def log_everything_metaclass(class_name, parents, attributes):
print('Creating class', class_name)
myattributes = {}
for name, attr in attributes.items():
myattributes[name] = attr
if hasattr(attr, '__call__'):
myattributes[name] = logged(
"%b %d %Y - %H:%M:%S", class_name + "."
)(attr)
return type(class_name, parents, myattributes)
class C(metaclass=log_everything_metaclass):
def __init__(self, x):
self.x = x
def print_x(self):
print(self.x)
# Usage:
print('Starting object creation')
c = C('Test')
c.print_x()
# Output:
Creating class C
- Running 'C.__init__' on Nov 21 2019 - 12:56:59
- Finished 'C.__init__', execution time = 0.000s
- Running 'C.print_x' on Nov 21 2019 - 12:57:06
Test
- Finished 'C.print_x', execution time = 0.000s
Как видите, у декораторов и метаклассов есть много общего. Фактически, метаклассы умеют всё, что можно сделать с помощью декоратора класса. Синтаксис декораторов более простой и читабельный, поэтому по возможности следует использовать именно их. Метаклассы умеют больше, так как они запускаются перед созданием класса, а не после, как декораторы. Чтобы убедиться в этом, давайте создадим декоратор и метакласс и посмотрим на порядок исполнения.
def my_metaclass(class_name, parents, attributes):
print('In metaclass, creating the class.')
return type(class_name, parents, attributes)
def my_class_decorator(class_):
print('In decorator, chance to modify the claqss.')
return class_
@my_class_decorator
class C(metaclass=my_metaclass):
def __init__(self):
print('Creating object.')
# Output:
In metaclass, creating the class.
In decorator, chance to modify the class.
Creating object.
Изучайте Python на Хекслете
Первые курсы в профессии «Python-программист» доступны бесплатно. Регистрируйтесь и начинайте учиться!
Рассмотрим более полезное приложение. Предположим, мы пишем набор классов для обработки ID3v2 тегов, которые используются, например, в MP3-файлах. Подробности можно узнать в «Википедии». Для реализации примера надо понимать, что теги состоят из фреймов. Каждый фрейм содержит четырёхбуквенный идентификатор. Например, TOPE
— фрейм имени артиста, TOAL
— фрейм названия альбома и так далее. Предположим, нам надо написать класс для каждого типа фреймов. Также нужно дать возможность пользователям библиотеки ID3v2 тегов добавлять собственные классы фреймов для поддержки новых или кастомных фреймов. С помощью метаклассов можно реализовать паттерн «фабрика классов». Это может выглядеть так:
frametype_class_dict = {}
class ID3v2FrameClassFactory(type):
def __new__(cls, class_name, parents, attributes):
print('Creating class', class_name)
# Here we could add some helper methods or attributes to c
c = type(class_name, parents, attributes)
if attributes['frame_identifier']:
frametype_class_dict[attributes['frame_identifier']] = c
return c
@staticmethod
def get_class_from_frame_identifier(frame_identifier):
return frametype_class_dict.get(frame_identifier)
class ID3v2Frame(metaclass=ID3v2FrameClassFactory):
frame_identifier = None
class ID3v2TitleFrame(ID3v2Frame, metaclass=ID3v2FrameClassFactory):
frame_identifier = 'TIT2'
class ID3v2CommentFrame(ID3v2Frame, metaclass=ID3v2FrameClassFactory):
frame_identifier = 'COMM'
title_class = ID3v2FrameClassFactory.get_class_from_frame_identifier('TIT2')
comment_class = ID3v2FrameClassFactory.get_class_from_frame_identifier('COMM')
print(title_class)
print(comment_class)
# Output:
Creating class ID3v2Frame
Creating class ID3v2TitleFrame
Creating class ID3v2CommentFrame
<class '__main__.ID3v2TitleFrame'>
<class '__main__.ID3v2CommentFrame'>
Конечно, задачу можно решить с помощью декораторов классов. Для сравнения посмотрите, как это может выглядеть.
frametype_class_dict = {}
class ID3v2FrameClass(object):
def __init__(self, frame_id):
self.frame_id = frame_id
def __call__(self, cls):
print('Decorating class', cls.__name__)
# Here we could add some helper methods or attributes to c
if self.frame_id:
frametype_class_dict[self.frame_id] = cls
return cls
@staticmethod
def get_class_from_frame_identifier(frame_identifier):
return frametype_class_dict.get(frame_identifier)
@ID3v2FrameClass(None)
class ID3v2Frame(object):
pass
@ID3v2FrameClass('TIT2')
class ID3v2TitleFrame(ID3v2Frame):
pass
@ID3v2FrameClass('COMM')
class ID3v2CommentFrame(ID3v2Frame):
pass
title_class = ID3v2FrameClass.get_class_from_frame_identifier('TIT2')
comment_class = ID3v2FrameClass.get_class_from_frame_identifier('COMM')
print(title_class)
print(comment_class)
Decorating class ID3v2Frame
Decorating class ID3v2TitleFrame
Decorating class ID3v2CommentFrame
<class '__main__.ID3v2TitleFrame'>
<class '__main__.ID3v2CommentFrame'>
Как видите, можно передавать параметры в декораторы, но не в метаклассы. Если нужно передать параметры в метаклассы, это нужно делать через атрибуты. Поэтому код с декораторами чище и проще в поддержке. Заметьте, что ко времени вызова декоратора класс уже создан. Это значит, что уже поздно менять его свойства, предназначенные только для чтения.
Как сказано выше, самый простой метакласс — это type
, и полученные из него классы имеют тип type
. Здесь возникает естественный вопрос: что представляет собой тип type
. Ответ простой: type
. Это значит, что type
представляет собой класс, и он выступает в качестве своего метакласса. Это экстраординарно, и это стало возможным на уровне интерпретатора Python. Вручную написать класс, который выступает в качестве своего метакласса, невозможно.
Понимая, что type
представляет собой класс, мы можем написать классы, которые наследуются от него. Эти классы можно использовать как метаклассы. Классы, которые их используют, будут иметь такой же тип, как метаклассы, полученные из type
. Вот пример:
>>> class meta(type):
... def __new__(cls, class_name, parents, attributes):
... print('meta.__new__')
... return super().__new__(cls, class_name, parents, attributes)
... def __call__(self, *args, **kwargs):
... print('meta.__call__')
... return super().__call__(*args, **kwargs)
...
>>> class C(metaclass=meta):
... pass
...
meta.__new__
>>> o = C()
meta.__call__
При вызове класса для создания нового объекта вызывается его функция __call__
. Она вызывает type.__call__
для создания объекта. В следующем разделе подытожим рассмотренное выше.
Подводим итоги
Предположим, что некий класс C
имеет метакласс my_metaclass
и декорирован с помощью my_class_decorator
. Далее предположим, что my_metaclass
представляет собой класс, полученный из type
. Соберём всё вместе, чтобы увидеть, как создаётся C
и как создаются объекты его типа. Вот как выглядит код:
class my_metaclass(type):
def __new__(cls, class_name, parents, attributes):
print('- my_metaclass.__new__ - Creating class instance of type', cls)
return super().__new__(cls, class_name, parents, attributes)
def __init__(self, class_name, parents, attributes):
print('- my_metaclass.__init__ - Initializing the class instance', self)
super().__init__(class_name, parents, attributes)
def __call__(self, *args, **kwargs):
print('- my_metaclass.__call__ - Creating object of type ', self)
return super().__call__(*args, **kwargs)
def my_class_decorator(cls):
print('- my_class_decorator - Chance to modify the class', cls)
return cls
@my_class_decorator
class C(metaclass=my_metaclass):
def __new__(cls):
print('- C.__new__ - Creating object.')
return super(C, cls).__new__(cls)
def __init__(self):
print('- C.__init__ - Initializing object.')
c = C()
print('Object c =', c)
На этом этапе вы можете потратить пару минут и попробовать определить порядок исполнения print
.
Посмотрим, как Python интерпретирует код выше. Затем посмотрим на вывод, чтобы подтвердить или опровергнуть наши предположения.
- Python читает определение класса и готовится передать три параметра в метакласс. Вот параметры:
class_name
,parents
иattributes
. - В нашем случае метакласс представляет собой класс, поэтому его вызов похож на создание нового класса. Это значит, что первый
my_metaclass.__new__
вызывается с четырьмя параметрами. Так создаётся объект, который и станет классом с именемC
. У объекта вызывается__init__
, а затем в переменную C записывается ссылка на объект. - Затем Python смотрит на декораторы, которые можно применить к классу. В нашем случае есть только один декоратор. Python вызывает его, передаёт возвращённый из метакласса класс в качестве параметра. Класс заменяется объектом, который возвращается из декоратора.
- Тип класса будет таким же, как определено в метаклассе.
- Когда класс вызывается для создания нового объекта, Python ищет
__call__
в метаклассе, так как тип класса — метакласс. В нашем случаеmy.metaclass.__call__
просто вызываетtype.__call__
, который создаёт объект из переданного класса. - Затем
type.__call__
создаёт объект. Для этого он ищетC.__new__
и запускает его. - Возвращённый объект готов к использованию.
Основываясь на этой логике, можно ожидать, что my_metaclass.__new__
вызывается первым. Затем следует my_metaclass.__init__
, затем my_class_decorator
. В этот момент класс C
полностью готов к использованию. Когда мы вызываем C
для создания объекта, который вызывает my.metaclass.__call__
(каждый раз при вызове объекта Python пытается вызвать __call__
), затем type.__call__
вызывает C.__new__
, наконец, вызывается C.__init__
. Вот вывод:
- my_metaclass.__new__ - Creating class instance of type <class '__main__.my_metaclass'>
- my_metaclass.__init__ - Initializing the class instance <class '__main__.C'>
- my_class_decorator - Chance to modify the class <class '__main__.C'>
- my_metaclass.__call__ - Creating object of type <class '__main__.C'>
- C.__new__ - Creating object.
- C.__init__ - Initializing object.
Object c = <__main__.C object at 0x7fef85a8ecf8>
Метаклассы — мощный инструмент, хотя и скорее эзотерический. Но достойных применений метаклассов известно не так уж и много. Автор оригинальной публикации нашёл только два репозитория, в которых метаклассы применяются по-настоящему. Это ABCMeta
и djangoplugins
.
ABCMeta
— это метакласс, позволяющий создавать абстрактные базовые классы. Детали смотрите в официальной документации.
Идея djungoplugins
основана на статье, в которой описывается простой фреймворк плагинов для Python. Здесь метаклассы используются для создания системы расширений. Автор оригинальной публикации считает, что такой же фреймворк можно создать с помощью декораторов.
Финальный аккорд
Понимание метаклассов помогает досконально разобраться, как ведут себя объекты и классы в Python. Но применение самих метаклассов в реальности может быть сложным, как показано в предыдущем разделе. Практически всё, что можно сделать с помощью метаклассов, можно реализовать и с помощью декораторов. Поэтому прежде чем использовать метаклассы, остановитесь на минуту и подумайте, так ли они необходимы. Если можно обойтись без них, лучше пойти по этому пути. Результат будет более читабельным и простым для поддержки и отладки.
Над адаптированным переводом статьи A Study of Python’s More Advanced Features Part III: Classes and Metaclasses by Sahand Saba работали Алексей Пирогов и Дмитрий Дементий. Мнение автора оригинальной публикации может не совпадать с мнением администрации «Хекслета».
Подпроцесс
— Управление подпроцессами — Документация по Python 3.9.5
Выполнение дочерней программы в новом процессе. В POSIX класс использует
os.execvp ()
-подобное поведение для выполнения дочерней программы. В Windows
класс использует функцию Windows CreateProcess ()
. Аргументы в пользу
Popen
следующие.
args должен быть последовательностью аргументов программы или отдельной строкой
или объект, подобный пути.
По умолчанию программа для выполнения является первым элементом в args , если args
последовательность.Если args — строка, интерпретация будет
платформенно-зависимый и описан ниже. См. Оболочку и исполняемый файл
аргументы в пользу дополнительных отличий от поведения по умолчанию. Пока не
в противном случае рекомендуется передавать аргументов как последовательность.
Пример передачи некоторых аргументов внешней программе
в виде последовательности:
Popen (["/ usr / bin / git", "commit", "-m", "Исправляет ошибку."])
В POSIX, если args является строкой, строка интерпретируется как имя или
путь к исполняемой программе.Однако это можно сделать только в том случае, если
передача аргументов программе.
Примечание
Может быть неочевидно, как разбить команду оболочки на последовательность аргументов,
особенно в сложных случаях. shlex.split ()
может проиллюстрировать, как
определить правильную разметку для args :
>>> import shlex, subprocess >>> command_line = input () / bin / vikings -input egg.txt -output "spam spam.txt" -cmd "echo '$ MONEY'" >>> args = shlex.разделить (command_line) >>> печать (аргументы) ['/ bin / vikings', '-input', 'egg.txt', '-output', 'spam spam.txt', '-cmd', "echo '$ MONEY'"] >>> p = subprocess.Popen (args) # Успех!
Обратите внимание, в частности, что параметры (например, -вход ) и аргументы (например,
как egg.txt ), которые разделены пробелами в оболочке, идут в отдельные
элементы списка, а аргументы, требующие экранирования кавычек или обратной косой черты, когда
используется в оболочке (например, имена файлов, содержащие пробелы, или команда echo
показано выше) являются отдельными элементами списка.
В Windows, если args является последовательностью, она будет преобразована в строку в
способ, описанный в разделе «Преобразование последовательности аргументов в строку в Windows». Это потому что
базовый CreateProcess ()
работает со строками.
Изменено в версии 3.6: параметр args принимает объект, подобный пути, если оболочка
Ложь
и последовательность, содержащая объекты, похожие на пути, в POSIX.
Изменено в версии 3.8: параметр args принимает объект, подобный пути, если оболочка
Ложь
и последовательность, содержащая байты и объекты, похожие на пути
в Windows.
Аргумент оболочки (по умолчанию Ложь
) указывает, следует ли использовать
оболочка как программа для выполнения. Если оболочка — это Истинно
, то это
рекомендуется передавать args как строку, а не как последовательность.
В POSIX с оболочкой = True
оболочка по умолчанию / bin / sh
. Если
args — строка, строка определяет команду
выполнить через оболочку. Это означает, что строка должна быть
форматируется точно так же, как при вводе в командной строке.Этот
включает, например, кавычки или обратную косую черту, экранирующие имена файлов пробелами в
их. Если args является последовательностью, первый элемент определяет командную строку, а
любые дополнительные элементы будут рассматриваться как дополнительные аргументы для оболочки
сам. То есть Popen
эквивалентно:
Popen (['/ bin / sh', '-c', args [0], args [1], ...])
В Windows с оболочкой = True
, переменная среды COMSPEC
указывает оболочку по умолчанию.Единственный раз, когда нужно указать
shell = True
в Windows — это когда команда, которую вы хотите выполнить, построена
в оболочку (например, dir или copy ). Вам не нужно
shell = True
для запуска командного файла или консольного исполняемого файла.
bufsize будет предоставлен как соответствующий аргумент для
open ()
функция при создании канала stdin / stdout / stderr
файловых объектов:
0
означает небуферизованный (чтение и запись — одно
системный вызов и может вернуться коротко)1
означает строчную буферизацию
(можно использовать, только еслиuniversal_newlines = True
i.е., в текстовом режиме)любое другое положительное значение означает использование буфера примерно этого
размеротрицательный bufsize (по умолчанию) означает системное значение по умолчанию
io.DEFAULT_BUFFER_SIZE будет использоваться.
Изменено в версии 3.3.1: bufsize теперь по умолчанию -1, чтобы по умолчанию включить буферизацию в соответствии с
поведение, которого ожидает большая часть кода. В версиях до Python 3.2.4 и
3.3.1 он неправильно по умолчанию 0
, который не был буферизован
и разрешено короткое чтение.Это было непреднамеренно и не соответствовало
поведение Python 2 соответствует ожиданиям большинства кодов.
Исполняемый файл аргумент указывает программу замены, которую нужно выполнить. Это
очень редко требуется. Когда shell = False
, исполняемый файл заменяет
программа для выполнения, указанная в args . Однако исходный args
еще перешли в программу. Большинство программ обрабатывают указанную программу
на args в качестве имени команды, которое затем может отличаться от программы
фактически выполнен.В POSIX имя args
становится отображаемым именем исполняемого файла в таких утилитах, как
пс . Если shell = True
, в POSIX исполняемый файл аргумент
задает заменяющую оболочку для / bin / sh
по умолчанию.
Изменено в версии 3.6: исполняемый файл Параметр принимает объект, подобный пути, в POSIX.
Изменено в версии 3.8: исполняемый файл Параметр принимает байты и объект, подобный пути
в Windows.
stdin , stdout и stderr определяют стандартный ввод исполняемой программы,
стандартный вывод и стандартные дескрипторы файла ошибок соответственно. Допустимые значения
PIPE
, DEVNULL
, существующий дескриптор файла (положительный
целое число), существующий файловый объект и Нет
. ТРУБА
указывает, что должен быть создан новый канал для дочернего элемента. DEVNULL
указывает, что будет использоваться специальный файл os.devnull
.С
настройки по умолчанию Нет
, перенаправление не происходит; дело ребенка
дескрипторы будут унаследованы от родителя. Кроме того, stderr может быть
STDOUT
, что указывает на то, что данные stderr из приложений
должен быть записан в тот же дескриптор файла, что и для stdout.
Если preexec_fn установлен как вызываемый объект, этот объект будет вызываться в
дочерний процесс непосредственно перед выполнением дочернего процесса.
(Только POSIX)
Предупреждение
Параметр preexec_fn небезопасно использовать при наличии потоков
в вашем приложении.Дочерний процесс может зайти в тупик до того, как exec будет
называется.
Если вы должны его использовать, оставьте это тривиальным! Минимизируйте количество библиотек
вы звоните.
Примечание
Если вам нужно изменить среду для ребенка, используйте env
параметр вместо того, чтобы делать это в preexec_fn .
Параметр start_new_session может заменить предыдущий
обычное использование preexec_fn для вызова os.setsid () в дочернем элементе.
Изменено в версии 3.8: параметр preexec_fn больше не поддерживается в субинтерпретаторах.
Использование параметра в субинтерпретаторе повышает
Ошибка выполнения
. Новое ограничение может коснуться приложений, которые
развернуты в mod_wsgi, uWSGI и других встроенных средах.
Если close_fds истинно, все дескрипторы файлов, кроме 0
, 1
и
2
будет закрыто перед выполнением дочернего процесса. Иначе
когда close_fds ложно, файловые дескрипторы подчиняются своему наследуемому флагу
как описано в разделе «Наследование файловых дескрипторов».
В Windows, если close_fds истинно, то дескрипторы не будут унаследованы
дочерний процесс, если он явно не передан в элементе handle_list
из
STARTUPINFO.lpAttributeList
, или перенаправлением стандартного дескриптора.
Изменено в версии 3.2: значение по умолчанию для close_fds было изменено с False
на
что описано выше.
Изменено в версии 3.7: В Windows значение по умолчанию для close_fds было изменено с False
на
Истинно
при перенаправлении стандартных дескрипторов.Теперь можно
установите close_fds на True
при перенаправлении стандартных дескрипторов.
pass_fds — необязательная последовательность файловых дескрипторов, которые нужно держать открытыми.
между родителем и ребенком. Обеспечение pass_fds сил
close_fds будет True
. (Только POSIX)
Изменено в версии 3.2: добавлен параметр pass_fds .
Если cwd не Нет
, функция изменяет рабочий каталог на
cwd перед выполнением дочернего элемента. cwd может быть строкой, байтами или
объект в виде пути. В частности, функция
ищет исполняемый файл (или первый элемент в args ) относительно cwd
если путь к исполняемому файлу является относительным.
Изменено в версии 3.8: параметр cwd принимает байтовый объект в Windows.
Если restore_signals истинно (по умолчанию), все сигналы, которые Python установил на
SIG_IGN восстанавливаются до SIG_DFL в дочернем процессе перед exec.В настоящее время это включает сигналы SIGPIPE, SIGXFZ и SIGXFSZ.
(Только POSIX)
Изменено в версии 3.2: добавлен restore_signals .
Если start_new_session истинно, системный вызов setsid () будет выполнен в
дочерний процесс до выполнения подпроцесса. (Только POSIX)
Изменено в версии 3.2: добавлен start_new_session .
Если группа не Нет
, системный вызов setregid () будет выполнен в
дочерний процесс до выполнения подпроцесса.Если предоставленный
value — это строка, она будет найдена через grp.getgrnam ()
и
будет использовано значение gr_gid
. Если значение является целым числом, оно
будет передан дословно. (Только POSIX)
Доступность: POSIX
Если extra_groups не равно None
, системный вызов setgroups () будет
сделано в дочернем процессе до выполнения подпроцесса.
Строки, представленные в extra_groups , будут просматриваться через
гр.getgrnam ()
и значения в gr_gid
.
Целочисленные значения будут передаваться дословно. (Только POSIX)
Доступность: POSIX
Если пользователь не Нет
, системный вызов setreuid () будет выполнен в
дочерний процесс до выполнения подпроцесса. Если предоставленный
value — это строка, поиск по ней будет осуществляться через pwd.getpwnam ()
и
будет использовано значение pw_uid
. Если значение является целым числом, оно будет
передаваться дословно.(Только POSIX)
Доступность: POSIX
Если umask не отрицательное, системный вызов umask () будет выполнен в
дочерний процесс до выполнения подпроцесса.
Доступность: POSIX
Если env не None
, это должно быть сопоставление, определяющее среду
переменные для нового процесса; они используются вместо значений по умолчанию
поведение наследования среды текущего процесса.
Примечание
Если указано, env должен предоставлять все переменные, необходимые для того, чтобы программа
выполнять.В Windows, чтобы запустить параллельную сборку,
указанный env должен включать действительный SystemRoot
.
Если кодировка или ошибки указаны , или текст истинно, объекты файла
stdin , stdout и stderr открываются в текстовом режиме с указанным
кодирование и ошибки , как описано выше в разделе «Часто используемые аргументы».
Аргумент universal_newlines эквивалентен тексту и предоставляется
для обратной совместимости.По умолчанию файловые объекты открываются в двоичном режиме.
Новое в версии 3.6: добавлены ошибки кодирования и .
Новое в версии 3.7: текст был добавлен как более читаемый псевдоним для universal_newlines .
Если задано, startupinfo будет объектом STARTUPINFO
, который
передается в базовую функцию CreateProcess
.
creationflags , если задан, может быть одним или несколькими из следующих флагов:
Объекты
Popen поддерживаются как диспетчеры контекста через с оператором
:
при выходе стандартные файловые дескрипторы закрываются, и процесс ожидает завершения.
с Popen (["ifconfig"], stdout = PIPE) в качестве процедуры: log.write (proc.stdout.read ())
Popen и другие функции в этом модуле, которые его используют, вызывают
событие аудита подпроцесс. Открыть
с аргументами
исполняемый файл
, args
, cwd
и env
. Значение для аргументов
может быть одной строкой или списком строк, в зависимости от платформы.
Изменено в версии 3.2: Добавлена поддержка диспетчера контекста.
Изменено в версии 3.6: Деструктор Popen теперь выдает предупреждение ResourceWarning
, если дочерний элемент
процесс все еще продолжается.
Изменено в версии 3.8: Popen может использовать os.posix_spawn ()
в некоторых случаях для улучшения
представление. В подсистеме Windows для Linux и пользовательской эмуляции QEMU,
Конструктор Popen с использованием os.posix_spawn ()
больше не вызывает
исключение при ошибках, таких как отсутствующая программа, но дочерний процесс не работает
с ненулевым кодом возврата
.
функций Python
Функция — это блок кода, который выполняется только при вызове.
В функцию можно передавать данные, называемые параметрами.
В результате функция может возвращать данные.
Создание функции
В Python функция определяется с использованием def
ключевое слово:
Пример
def my_function ():
print («Привет от функции»)
Вызов функции
Чтобы вызвать функцию, используйте имя функции, за которым следует круглые скобки:
Пример
def my_function ():
print («Привет от функции»)
my_function ()
Попробуй сам »
Аргументы
Информация может передаваться в функции как аргументы.
Аргументы указываются после имени функции в круглых скобках.
Вы можете добавить сколько угодно аргументов, просто разделив их запятыми.
В следующем примере есть функция с одним аргументом (fname).
Когда функция вызывается, мы передаем имя,
который используется внутри функции для вывода полного имени:
Пример
def my_function ( fname ):
print (fname + «Refsnes»)
my_function ( «Emil» )
my_function ( «Tobias» )
my_function ( «Linus» )
Попробуй сам »
Аргументы часто сокращаются до args в документации Python.
Параметры или аргументы?
Термины параметр и аргумент могут использоваться для одного и того же: информации, которая передается в функцию.
С точки зрения функции:
Параметр — это переменная, указанная в скобках в определении функции.
Аргумент — это значение, которое отправляется функции при ее вызове.
Количество аргументов
По умолчанию функция должна вызываться с правильным количеством аргументов.Это означает, что если ваша функция ожидает 2 аргумента, вы должны вызвать функцию
с 2 аргументами, не больше и не меньше.
Пример
Эта функция ожидает 2 аргумента и получает 2 аргумента:
def my_function (fname, lname):
print (fname + «» + lname)
my_function («Emil», «Refsnes»)
Попробуй сам »
Если вы попытаетесь вызвать функцию с 1 или 3 аргументами, вы получите ошибку:
Пример
Эта функция ожидает 2 аргумента, но получает только 1:
def my_function (fname, lname):
print (fname + «» + lname)
my_function («Emil»)
Попробуй сам »
Произвольные аргументы, * args
Если вы не знаете, сколько аргументов будет передано вашей функции,
добавьте *
перед именем параметра в определении функции.
Таким образом, функция получит кортеж аргументов и сможет получить доступ к элементам соответственно:
Пример
Если количество аргументов неизвестно, добавьте *
перед именем параметра:
def my_function (* kids):
print («Самый младший ребенок
is «+ kids [2])
my_function (» Эмиль «,» Тобиас «,» Линус «)
Попробуй сам »
Произвольные аргументы часто сокращаются до * args в документации Python.
Аргументы ключевого слова
Вы также можете отправлять аргументы с синтаксисом ключ = значение .
Таким образом, порядок аргументов не имеет значения.
Пример
def my_function (child3, child2, child1):
print («Самый младший ребенок
is «+ child3»
my_function (child1 = «Emil», child2 = «Tobias», child3 = «Linus»)
Попробуй сам »
Фраза Аргументы ключевого слова часто сокращается до kwargs в документации Python.
Аргументы произвольных ключевых слов, ** kwargs
Если вы не знаете, сколько аргументов ключевого слова будет передано в вашу функцию,
добавьте две звездочки: **
перед именем параметра в определении функции.
Таким образом, функция получит словарь аргументов и сможет получить доступ к элементам соответственно:
Пример
Если количество аргументов ключевого слова неизвестно, добавьте двойной
**
перед именем параметра:
def my_function (** kid):
print («Его фамилия» + kid [«lname»])
my_function (fname = «Tobias», lname = «Refsnes»)
Попробуй сам »
Произвольные аргументы Kword часто сокращаются до ** kwargs в документации Python.
Значение параметра по умолчанию
В следующем примере показано, как использовать значение параметра по умолчанию.
Если мы вызываем функцию без аргументов, она использует значение по умолчанию:
Пример
def my_function ( country = «Норвегия» ):
print («Я из» +
страна)
my_function («Швеция»)
my_function («Индия»)
my_function ()
my_function («Бразилия»)
Попробуй сам »
Передача списка в качестве аргумента
Вы можете отправлять аргументы любых типов данных функции (строка, число, список, словарь и т. Д.).),
и это будет
обрабатываться как один и тот же тип данных внутри функции.
Например, если вы отправите список в качестве аргумента, он все равно будет списком, когда он
достигает функции:
Пример
def my_function (food):
для x в food:
print (x)
fruit = [«яблоко», «банан», «вишня»]
my_function (fruit)
Попробуй сам »
Возвращаемые значения
Чтобы функция возвращала значение, используйте return
выписка:
Пример
def my_function (x):
return 5 * x
print (my_function (3))
print (my_function (5))
печать (моя_функция (9))
Попробуй сам »
Пропуск Заявление
определение функции
не может быть пустым, но если
по какой-то причине у вас есть определение функции
без содержимого, введите оператор pass
, чтобы избежать ошибки.
Рекурсия
Python также принимает рекурсию функций, что означает, что определенная функция может вызывать сама себя.
Рекурсия — это общая математическая и программная концепция. Это означает, что функция вызывает сама себя. Это имеет то преимущество, что вы можете перебирать данные для достижения результата.
Разработчику следует быть очень осторожным с рекурсией, поскольку довольно легко ускользнуть от написания функции, которая никогда не завершается, или функции, которая использует избыточные объемы памяти или мощности процессора.Однако при правильном написании рекурсия может быть очень эффективным и математически элегантным подходом к программированию.
В этом примере tri_recursion () — это функция, которую мы определили для вызова самой себя («рекурсивная»). Мы используем переменную k в качестве данных, которая уменьшается на (-1) каждый раз, когда мы выполняем рекурсию. Рекурсия заканчивается, когда условие не больше 0 (т.е. когда оно равно 0).
Новому разработчику может потребоваться некоторое время, чтобы понять, как именно это работает, лучший способ выяснить это — протестировать и изменить его.
Пример
Пример рекурсии
def tri_recursion (k):
если (k> 0):
результат = k + tri_recursion (k — 1)
print (результат)
еще:
результат = 0
вернуть результат
print («\ n \ nРезультаты примера рекурсии»)
tri_recursion (6)
Попробуй сам »
Вызов, отступ, аргументы и возвращаемые значения
Что такое функция в Python?
Функция в Python — это фрагмент кода, который запускается при обращении к нему.Он используется для использования кода более чем в одном месте программы. Его также называют методом или процедурой. Python предоставляет множество встроенных функций, таких как print (), input (), compile (), exec () и т. Д., Но также дает свободу создавать свои собственные функции.
В этом руководстве мы узнаем
Как определить и вызвать функцию в Python
Функция в Python определяется оператором «def» , за которым следует имя функции и круглые скобки (())
Пример:
Давайте определим функцию с помощью команды «def func1 ():» и вызовем эту функцию.Результатом функции будет «Я изучаю функцию Python».
Функция print func1 () вызывает нашу def func1 (): и выводит команду « Я изучаю функцию Python None. »
В Python есть набор правил для определения функции.
- Любые аргументы или входные параметры должны быть помещены в эти круглые скобки
- Первый оператор функции может быть необязательным оператором — строкой документации или строкой документации функции
- Код внутри каждой функции начинается с двоеточия (:) и должен быть с отступом (пробел)
- Оператор return (выражение) завершает функцию, опционально передавая значение вызывающей стороне.Оператор return без аргументов аналогичен return None.
Значение отступа (пробела) в Python
Прежде чем мы познакомимся с функциями Python, важно понять правило отступа для объявления функций Python, и эти правила применимы к другим элементам Python, а также к объявлению условий, петли или переменные.
Python следует определенному стилю отступов для определения кода, поскольку функции Python не имеют явного начала или конца, например фигурных скобок, чтобы указать начало и конец функции, они должны полагаться на этот отступ .Вот простой пример с командой «печать». Когда мы пишем функцию «print» прямо под def func 1 (): она покажет «ошибку отступа : ожидается блок с отступом ».
Теперь, когда вы добавляете отступ (пробел) перед функцией «print», она должна печататься должным образом.
Для успешной работы кода достаточно хотя бы одного отступа. Но лучше всего оставить 3-4 отступа для вызова функции .
Также необходимо, чтобы при объявлении отступа вы сохранили тот же отступ для остальной части кода .Например, на снимке экрана ниже, когда мы вызываем другой оператор «все еще в func1» и когда он не объявлен сразу под первым оператором печати, он показывает ошибку отступа «Undent не соответствует никакому другому уровню отступа».
Теперь, когда мы применяем одинаковый отступ для обоих операторов и выравниваем их в одной строке, это дает ожидаемый результат.
Как функция возвращает значение?
Команда возврата в Python указывает, какое значение вернуть вызывающей функции.
Давайте разберемся с этим на следующем примере.
Шаг 1) Здесь — мы видим, когда функция не «возвращает». Например, нам нужен квадрат 4, и он должен дать ответ «16» при выполнении кода. Что он дает, когда мы просто используем код «print x * x», но когда вы вызываете функцию «print square», в качестве вывода выдает «None». Это связано с тем, что при вызове функции рекурсия не происходит и выпадает из конца функции. Python возвращает «Нет» в случае сбоя в конце функции.
Шаг 2) Для большей ясности мы заменяем команду печати командой присваивания. Проверим вывод.
Когда вы запускаете команду «print square (4)», она фактически возвращает значение объекта, поскольку у нас нет какой-либо конкретной функции для выполнения здесь, она возвращает «None».
Шаг 3) Теперь мы увидим, как получить вывод с помощью команды «return». Когда вы используете функцию «return» и выполняете код, он выдаст на выходе «16.»
Шаг 4) Функции в Python сами по себе являются объектом, и у объекта есть какое-то значение. Здесь мы увидим, как Python обрабатывает объект. Когда вы запускаете команду« print square », она возвращает значение объекта. Поскольку мы не передали никаких аргументов, у нас нет какой-либо конкретной функции, которую можно было бы запустить здесь, она возвращает значение по умолчанию (0x021B2D30), которое является местоположением объекта. В практической программе Python вам, вероятно, никогда не понадобится сделайте это ..
Аргументы в функциях
Аргумент — это значение, которое передается функции при ее вызове.
Другими словами, на вызывающей стороне это аргумент, а на стороне функции — параметр.
Давайте посмотрим, как работает Python Args —
Шаг 1) Аргументы объявлены в определении функции. При вызове функции вы можете передать значения для этих аргументов, как показано ниже.
Шаг 2) Чтобы объявить значение аргумента по умолчанию, присвойте ему значение при определении функции.
Пример: x не имеет значений по умолчанию. Значения по умолчанию y = 0.Когда мы предоставляем только один аргумент при вызове функции умножения, Python присваивает предоставленное значение x, сохраняя значение y = 0. Следовательно, умножение x * y = 0
Шаг 3) На этот раз мы изменим значение на y = 2 вместо значения по умолчанию y = 0, и он вернет результат как (4×2) = 8.
Шаг 4) Вы также можете изменить порядок, в котором аргументы могут быть переданы в Python. Здесь мы изменили порядок значений x и y на x = 4 и y = 2.
Шаг 5) Несколько аргументов также могут передаваться в виде массива. В этом примере мы вызываем несколько аргументов (1,2,3,4,5), вызывая функцию (* args).
Пример: мы объявили несколько аргументов как число (1,2,3,4,5), когда вызываем функцию (* args); вывод выводится как (1,2,3,4,5)
Tips :
- В Python 2.7. Перегрузка функции не поддерживается в Python. Перегрузка функций — это возможность создавать несколько методов с одним и тем же именем с разной реализацией.Перегрузка функций полностью поддерживается в Python 3
- Между методами и функциями существует большая путаница. Методы в Python связаны с экземплярами объектов, а функции — нет. Когда Python вызывает метод, он связывает первый параметр этого вызова с соответствующей ссылкой на объект. Проще говоря, автономная функция в Python — это «функция», тогда как функция, которая является атрибутом класса или экземпляра, является «методом».
Вот полный код Python 3
# определить функцию def func1 (): print («Я изучаю функцию Python») print ("все еще в функции") func1 () def квадрат (x): вернуть х * х печать (квадрат (4)) def multiply (x, y = 0): print ("значение x =", x) print ("значение y =", y) вернуть x * y print (умножить (y = 2, x = 4))
Вот полный код Python 2
# определить функцию def func1 (): print "Я изучаю функцию Python" напечатать "все еще в func1" func1 () def квадрат (x): вернуть х * х принт квадрат (4) def multiply (x, y = 0): print "значение x =", x print "значение y =", y вернуть x * y напечатать умножить (y = 2, x = 4)
Описание:
Функция в Python — это фрагмент повторно используемого кода, который используется для выполнения одного связанного действия.В этой статье мы увидим
- Функция, определенная оператором def
- Блок кода внутри каждой функции начинается с двоеточия (:) и должен иметь отступ (пробел)
- Любые аргументы или входные параметры должны быть помещены внутри этих скобок и т. д.
- После объявления функции перед кодом должен быть оставлен хотя бы один отступ
- Во всем коде в функции def должен быть сохранен один и тот же стиль отступа
- В соответствии с передовой практикой лучше всего использовать три или четыре отступа перед оператором
- Вы можете использовать команду «return», чтобы вернуть значения в вызов функции.
- Python напечатает случайное значение, например (0x021B2D30), если аргумент не передан вызывающей функции. Пример «функции печати».
- На вызывающей стороне это аргумент, а на стороне функции это параметр
- Значение по умолчанию в аргументе — когда мы предоставляем только один аргумент при вызове функции умножения или любой другой функции, Python назначает другой аргумент по умолчанию
- Python также позволяет изменить порядок аргументов.
Определение основных функций в Python — Real Python
Многие языки программирования имеют специальную функцию, которая автоматически выполняется, когда операционная система запускает программу.Эта функция обычно называется main ()
и должна иметь определенный тип возвращаемого значения и аргументы в соответствии со стандартом языка. С другой стороны, интерпретатор Python выполняет сценарии, начиная с верхней части файла, и нет никакой конкретной функции, которую Python выполняет автоматически.
Тем не менее, наличие определенной начальной точки для выполнения программы полезно для понимания того, как работает программа. Программисты Python придумали несколько соглашений, чтобы определить эту отправную точку.
Базовый Python main ()
В некоторых скриптах Python вы можете увидеть определение функции и условный оператор, которые выглядят как в примере ниже:
def main ():
print («Привет, мир!»)
если __name__ == "__main__":
основной()
В этом коде есть функция main ()
, которая печатает фразу Hello World!
, когда его выполняет интерпретатор Python. Также существует условный оператор (или , если
), который проверяет значение __name__
и сравнивает его со строкой «__main__»
.Когда оператор if
оценивается как True
, интерпретатор Python выполняет main ()
. Вы можете узнать больше об условных операторах в разделе «Условные операторы в Python».
Этот шаблон кода довольно часто встречается в файлах Python, которые вы хотите, чтобы выполнялся как сценарий и , импортированный в другой модуль . Чтобы понять, как будет выполняться этот код, вы должны сначала понять, как интерпретатор Python устанавливает __name__
в зависимости от того, как выполняется код.
Режимы выполнения в Python
Есть два основных способа указать интерпретатору Python выполнять или использовать код:
- Вы можете выполнить файл Python как сценарий , используя командную строку.
- Вы можете импортировать код из одного файла Python в другой файл или в интерактивный интерпретатор.
Вы можете больше узнать об этих подходах в статье Как запускать сценарии Python. Независимо от того, какой способ выполнения кода вы используете, Python определяет специальную переменную с именем __name__
, которая содержит строку, значение которой зависит от того, как используется код.
Мы будем использовать этот пример файла, сохраненный как execution_methods.py
, чтобы исследовать, как поведение кода изменяется в зависимости от контекста:
print («Это мой файл для тестирования методов выполнения Python.»)
print («Переменная __name__ сообщает мне, в каком контексте запущен этот файл.»)
print ("Значение __name__:", repr (__ name__))
В этом файле определены три вызова print ()
. Первые два печатают вводные фразы.Третья функция print ()
сначала напечатает фразу Значение __name__ равно
, а затем она распечатает представление переменной __name__
с использованием встроенного Python repr ()
.
В Python repr ()
отображает печатное представление объекта. В этом примере используется repr ()
, чтобы подчеркнуть, что значение __name__
является строкой. Вы можете узнать больше о repr ()
в документации Python.
В этой статье вы увидите слова файл , модуль и скрипт . Практически между ними нет большой разницы. Однако есть небольшие различия в значениях, которые подчеркивают назначение фрагмента кода:
Файл: Обычно файл Python — это любой файл, содержащий код. Большинство файлов Python имеют расширение
.py
.Сценарий: Сценарий Python — это файл, который вы собираетесь запустить из командной строки для выполнения задачи.
Модуль: Модуль Python — это файл, который вы собираетесь импортировать из другого модуля или сценария, или из интерактивного интерпретатора. Вы можете узнать больше о модулях в Python Modules and Packages — An Introduction.
Это различие также обсуждается в разделе Как запускать сценарии Python.
Выполнение из командной строки
При таком подходе вы хотите выполнить сценарий Python из командной строки.
При выполнении сценария вы не сможете интерактивно определить код, выполняемый интерпретатором Python. Подробности того, как вы можете запустить Python из командной строки, не так важны для целей этой статьи, но вы можете развернуть поле ниже, чтобы узнать больше о различиях между командной строкой в Windows, Linux и macOS.
Способ, которым вы указываете компьютеру выполнить код из командной строки, немного отличается в зависимости от вашей операционной системы.
В Linux и macOS командная строка обычно выглядит следующим образом:
eleanor @ realpython: ~ / Documents $
Часть перед знаком доллара (
$) может выглядеть по-разному в зависимости от вашего имени пользователя и имени вашего компьютера. Команды, которые вы вводите, будут идти после $
. В Linux или macOS имя исполняемого файла Python 3 — python3
, поэтому вам следует запускать сценарии Python, набрав python3 script_name.py
после $
.
В Windows командная строка обычно выглядит следующим образом:
C: \ Users \ Eleanor \ Documents>
Часть перед >
может выглядеть по-разному в зависимости от вашего имени пользователя. Команды, которые вы вводите, будут идти после >
. В Windows имя исполняемого файла Python 3 обычно python
, поэтому вам следует запускать сценарии Python, набрав python script_name.py
после >
.
Независимо от вашей операционной системы, выходные данные сценариев Python, которые вы используете в этой статье, будут одинаковыми, поэтому в этой статье показан только стиль ввода Linux и macOS, а строка ввода будет начинаться с $
. .
Теперь вы должны выполнить сценарий execution_methods.py
из командной строки, как показано ниже:
$ python3 Execution_methods.py
Это мой файл для тестирования методов выполнения Python.
Переменная __name__ сообщает мне, в каком контексте выполняется этот файл.Значение __name__: '__main__'
В этом примере вы можете видеть, что __name__
имеет значение '__main__'
, где символы кавычек ( '
) говорят вам, что значение имеет строковый тип.
Помните, что в Python нет разницы между строками, определенными в одинарных кавычках ( '
) и двойных кавычках ( "
). Вы можете узнать больше об определении строк в базовых типах данных в Python.
Вы получите идентичный результат, если включите в сценарий строку shebang и выполните ее напрямую (./execution_methods.py
) или используйте магию % run
в IPython или Jupyter Notebooks.
Вы также можете увидеть сценарии Python, выполняемые из пакетов, добавив к команде аргумент -m
. Чаще всего это рекомендуется при использовании pip
: python3 -m pip install имя_пакета
.
Добавление аргумента -m
запускает код в модуле __main__.py
пакета. Вы можете найти дополнительную информацию о __main__.py
в статье Как опубликовать пакет Python с открытым исходным кодом в PyPI.
Во всех трех случаях __name__
имеет одно и то же значение: строка '__main__'
.
Технические детали: Документация Python конкретно определяет, когда __name__
будет иметь значение '__main__'
:
__name__
модуля устанавливается равным'__main__'
при чтении из стандартного ввода, сценария или из интерактивной подсказки.(Источник)
__name__
хранится в глобальном пространстве имен модуля вместе с __doc__
, __package__
и другими атрибутами. Вы можете прочитать больше об этих атрибутах в документации модели данных Python и, особенно для модулей и пакетов, в документации по импорту Python.
Импорт в модуль или интерактивный интерпретатор
Теперь давайте посмотрим на второй способ, которым интерпретатор Python будет выполнять ваш код: импорт.Когда вы разрабатываете модуль или сценарий, вы, скорее всего, захотите воспользоваться преимуществами модулей, которые уже созданы кем-то другим, что вы можете сделать с помощью ключевого слова import
.
Во время процесса импорта Python выполняет операторы, определенные в указанном модуле (но только первый , когда вы импортируете модуль). Чтобы продемонстрировать результаты импорта файла execution_methods.py
, запустите интерактивный интерпретатор Python, а затем импортируйте свои execution_methods.py
файл:
>>>
>>> импорт методов_исполнения
Это мой файл для тестирования методов выполнения Python.
Переменная __name__ сообщает мне, в каком контексте выполняется этот файл.
Значение __name__: 'execution_methods'
В этом выводе кода вы можете видеть, что интерпретатор Python выполняет три вызова print ()
. Первые две строки вывода точно такие же, как при выполнении файла как сценария в командной строке, потому что ни в одной из первых двух строк нет переменных.Однако есть разница в выводе третьего print ()
.
Когда интерпретатор Python импортирует код, значение __name__
устанавливается таким же, как имя импортируемого модуля. Вы можете увидеть это в третьей строке вывода выше. __name__
имеет значение 'execution_methods'
, которое является именем файла .py
, из которого Python импортирует.
Обратите внимание, что если вы снова импортируете модуль
без выхода из Python, выход не будет.
Лучшие практики для основных функций Python
Теперь, когда вы видите различия в том, как Python обрабатывает различные режимы выполнения, вам будет полезно знать некоторые передовые практики. Они будут применяться всякий раз, когда вы захотите написать код, который можно будет запустить как сценарий импорта и в другом модуле или в интерактивном сеансе.
Вы узнаете о четырех передовых методах, чтобы убедиться, что ваш код может служить двойной цели:
- Поместите большую часть кода в функцию или класс.
- Используйте
__name__
для управления выполнением вашего кода. - Создайте функцию с именем
main ()
, содержащую код, который вы хотите запустить. - Вызов других функций из
main ()
.
Поместите большую часть кода в функцию или класс
Помните, что интерпретатор Python выполняет весь код в модуле, когда он импортирует модуль. Иногда у написанного вами кода будут побочные эффекты, которые вы хотите, чтобы пользователь контролировал, например:
- Выполнение вычисления, занимающего много времени
- Запись в файл на диске
- Печать информации, которая может загромождать пользовательский терминал
В этих случаях вы хотите, чтобы пользователь управлял запуском выполнения этого кода, а не позволял интерпретатору Python выполнять код при импорте вашего модуля.
Следовательно, лучше всего включать большую часть кода в функцию или класс . Это связано с тем, что, когда интерпретатор Python встречает ключевые слова def
или class
, он сохраняет эти определения только для последующего использования и фактически не выполняет их, пока вы ему не скажете.
Сохраните приведенный ниже код в файл с именем best_practices.py
, чтобы продемонстрировать эту идею:
1 из времени импорта сна
2
3print ("Это мой файл, демонстрирующий передовой опыт.")
4
5def process_data (данные):
6 print («Начало обработки данных ...»)
7 modified_data = data + "что было изменено"
8 спальных мест (3)
9 print («Обработка данных завершена.»)
10 вернуть Modified_data
В этом коде вы сначала импортируете sleep ()
из модуля time
.
sleep ()
приостанавливает интерпретатор на количество секунд, которое вы указываете в качестве аргумента, и создает функцию, выполнение которой для этого примера занимает много времени.Затем вы используете print ()
, чтобы напечатать предложение, описывающее цель этого кода.
Затем вы определяете функцию с именем process_data ()
, которая выполняет пять функций:
- Распечатывает некоторые выходные данные, чтобы сообщить пользователю, что обработка данных начинается
- Изменяет входные данные
- Приостанавливает выполнение на три секунды, используя
sleep ()
- Распечатывает некоторые выходные данные, чтобы сообщить пользователю, что обработка завершена
- Возвращает измененные данные
Выполнить файл рекомендаций в командной строке
Что произойдет, если вы выполните этот файл как сценарий в командной строке?
Интерпретатор Python выполнит строки from time import sleep
и print ()
, которые находятся за пределами определения функции, затем он создаст определение функции с именем process_data ()
.Затем сценарий завершится без каких-либо дополнительных действий, поскольку в сценарии нет кода, который выполняет process_data ()
.
В блоке кода ниже показан результат выполнения этого файла как сценария:
$ python3 best_practices.py
Это мой файл для демонстрации передового опыта.
Вывод, который мы здесь видим, является результатом первого print ()
. Обратите внимание, что импорт из , время
и определение process_data ()
не приводят к выходным данным.В частности, выходы вызовов print ()
, которые находятся внутри определения process_data ()
, не распечатываются!
Импорт файла передовых практик в другой модуль или интерактивный интерпретатор
Когда вы импортируете этот файл в интерактивном сеансе (или другом модуле), интерпретатор Python выполнит точно такие же шаги, как и при выполнении файла как сценария.
После того, как интерпретатор Python импортирует файл, вы можете использовать любые переменные, классы или функции, определенные в импортированном вами модуле.Чтобы продемонстрировать это, мы будем использовать интерактивный интерпретатор Python. Запустите интерактивный интерпретатор и введите import best_practices
:
>>>
>>> import best_practices
Это мой файл для демонстрации передового опыта.
Единственный результат импорта файла best_practices.py
— это первый вызов print ()
, определенный вне process_data ()
. Импорт из времени
и определение process_data ()
не приводят к выходным данным, как и при выполнении кода из командной строки.
Используйте
, если __name__ == "__main__"
, чтобы контролировать выполнение вашего кода
Что делать, если вы хотите, чтобы process_data ()
выполнялся при запуске сценария из командной строки, но не при импорте файла интерпретатором Python?
Вы можете использовать идиому if __name__ == "__main__"
для определения контекста выполнения и условно запустить process_data ()
только тогда, когда __name__
равно "__main__"
.Добавьте приведенный ниже код в конец своего файла best_practices.py
:
11if __name__ == "__main__":
12 data = "Мои данные прочитаны из Интернета"
13 печать (данные)
14 измененные_данные = данные_процесса (данные)
15 печать (измененные_данные)
В этом коде вы добавили условный оператор, который проверяет значение __name__
. Это условие будет оцениваться как True
, когда __name__
равно строке «__main__»
.Помните, что специальное значение «__main__»
для переменной __name__
означает, что интерпретатор Python выполняет ваш сценарий, а не импортирует его.
Внутри условного блока вы добавили четыре строки кода (строки 12, 13, 14 и 15):
- Строки 12 и 13: Вы создаете переменную
data
, в которой хранятся данные, полученные из Интернета, и распечатываете их. - Строка 14: Вы обрабатываете данные.
- Строка 15: Вы печатаете измененные данные.
Теперь запустите свой сценарий best_practices.py
из командной строки, чтобы увидеть, как изменится вывод:
$ python3 best_practices.py
Это мой файл для демонстрации передового опыта.
Мои данные читаются из Интернета
Начало обработки данных ...
Обработка данных завершена.
Мои данные, прочитанные из Интернета, которые были изменены
Во-первых, выходные данные показывают результат вызова print ()
за пределами process_data ()
.
После этого печатается значение данных
. Это произошло потому, что переменная __name__
имеет значение «__main__»
, когда интерпретатор Python выполняет файл как сценарий, поэтому условный оператор оценивается как True
.
Затем ваш сценарий вызвал process_data ()
и передал данных
для модификации. Когда выполняется process_data ()
, он выводит на выход некоторые сообщения о состоянии. Наконец, печатается значение modified_data
.
Теперь вы должны проверить, что происходит при импорте файла best_practices.py
из интерактивного интерпретатора (или другого модуля). Пример ниже демонстрирует эту ситуацию:
>>>
>>> import best_practices
Это мой файл для демонстрации передового опыта.
Обратите внимание, что вы получаете то же поведение, что и до добавления условного оператора в конец файла! Это связано с тем, что переменная __name__
имела значение "best_practices"
, поэтому Python не выполнил код внутри блока, включая process_data ()
, потому что условный оператор оценил False
.
Создайте функцию с именем main () для содержания кода, который вы хотите запустить
Теперь вы можете писать код Python, который можно запускать из командной строки как сценарий и импортировать без нежелательных побочных эффектов. Затем вы узнаете, как написать свой код, чтобы другим программистам Python было легко понять, что вы имеете в виду.
Многие языки, такие как C, C ++, Java и некоторые другие, определяют специальную функцию, которая должна называться main ()
, которую операционная система автоматически вызывает при выполнении скомпилированной программы.Эту функцию часто называют точкой входа , потому что именно здесь выполнение входит в программу.
Напротив, Python не имеет специальной функции, которая служит точкой входа в сценарий. Фактически вы можете дать функции точки входа в скрипте Python любое имя, какое захотите!
Хотя Python не придает никакого значения функции с именем main ()
, лучше всего в любом случае назвать функцию точки входа main ()
.Таким образом, любые другие программисты, читающие ваш сценарий, сразу узнают, что эта функция является отправной точкой кода, выполняющего основную задачу сценария.
Кроме того, main ()
должен содержать любой код, который вы хотите запустить, когда интерпретатор Python выполняет файл. Это лучше, чем помещать код непосредственно в условный блок, потому что пользователь может повторно использовать main ()
, если они импортируют ваш модуль.
Измените best_practices.py
, чтобы он выглядел как код ниже:
1 из времени импорта сна
2
3print («Это мой файл, демонстрирующий передовой опыт.»)
4
5def process_data (данные):
6 print («Начало обработки данных ...»)
7 modified_data = data + "что было изменено"
8 спальных мест (3)
9 print («Обработка данных завершена.»)
10 вернуть Modified_data
11
12def main ():
13 data = "Мои данные прочитаны из Интернета"
14 печать (данные)
15 modified_data = данные_процесса (данные)
16 печать (измененные_данные)
17
18if __name__ == "__main__":
19 основной ()
В этом примере вы добавили определение main ()
, которое включает код, который ранее находился внутри условного блока.Затем вы изменили условный блок так, чтобы он выполнял main ()
. Если вы запустите этот код как скрипт или импортируете его, вы получите тот же результат, что и в предыдущем разделе.
Вызов других функций из main ()
Другая распространенная практика в Python — это , чтобы main ()
выполнял другие функции , вместо того, чтобы включать код выполнения задачи в main ()
. Это особенно полезно, когда вы можете составить общую задачу из нескольких более мелких подзадач, которые могут выполняться независимо.
Например, у вас может быть сценарий, который выполняет следующие действия:
- Считывает файл данных из источника, которым может быть база данных, файл на диске или веб-API
- Обрабатывает данные
- Записывает обработанные данные в другое место
Если вы реализуете каждую из этих подзадач в отдельных функциях, то вам (или другому пользователю) легко повторно использовать несколько шагов и игнорировать те, которые вам не нужны. Затем вы можете создать рабочий процесс по умолчанию в main ()
, и у вас будет лучшее из обоих миров.
Применять ли эту практику к вашему коду — это ваше решение. Разделение работы на несколько функций упрощает повторное использование, но увеличивает трудность для кого-то, пытающегося интерпретировать ваш код, потому что он должен выполнить несколько переходов в потоке программы.
Измените файл best_practices.py
так, чтобы он выглядел, как показано ниже:
1 из времени импорта сна
2
3print ("Это мой файл, демонстрирующий передовой опыт.")
4
5def process_data (данные):
6 print («Начало обработки данных ...»)
7 modified_data = data + "что было изменено"
8 спальных мест (3)
9 print («Обработка данных завершена.»)
10 вернуть Modified_data
11
12def read_data_from_web ():
13 print («Чтение данных из Интернета»)
14 data = "Данные из Интернета"
15 возврат данных
16
17def write_data_to_database (данные):
18 print («Запись данных в базу данных»)
19 печать (данные)
20
21def main ():
22 данные = read_data_from_web ()
23 измененные_данные = данные_процесса (данные)
24 write_data_to_database (измененные_данные)
25
26if __name__ == "__main__":
27 основных ()
В этом примере кода первые 10 строк файла имеют то же содержимое, что и раньше.Второе определение функции в строке 12 создает и возвращает некоторые образцы данных, а третье определение функции в строке 17 моделирует запись измененных данных в базу данных.
В строке 21 определяется main ()
. В этом примере вы изменили main ()
, чтобы он по очереди вызывал функции чтения, обработки и записи данных.
Во-первых, данные
создаются из read_data_from_web ()
. Эти данных
передаются в process_data ()
, которая возвращает modified_data
.Наконец, modified_data
передается в write_data_to_database ()
.
Последние две строки сценария — это условный блок, который проверяет __name__
и запускает main ()
, если оператор if
равен True
.
Теперь вы можете запустить весь конвейер обработки из командной строки, как показано ниже:
$ python3 best_practices.py
Это мой файл для демонстрации передового опыта.
Чтение данных из Интернета
Начало обработки данных...
Обработка данных завершена.
Запись обработанных данных в базу данных
Данные из Интернета, которые были изменены
В выходных данных этого выполнения вы можете видеть, что интерпретатор Python выполнил main ()
, который выполнил read_data_from_web ()
, process_data ()
и write_data_to_database ()
. Однако вы также можете импортировать файл best_practices.py
и повторно использовать process_data ()
для другого источника входных данных, как показано ниже:
>>>
>>> импортировать best_practices как bp
Это мой файл для демонстрации передового опыта.>>> data = "Данные из файла"
>>> modified_data = bp.process_data (данные)
Начало обработки данных ...
Обработка данных завершена.
>>> bp.write_data_to_database (измененные_данные)
Запись обработанных данных в базу данных
Данные из файла, который был изменен
В этом примере вы импортировали best_practices
и сократили имя до bp
для этого кода.
В процессе импорта интерпретатор Python выполнил все строки кода из best_practices.py
, поэтому на выходе отображается строка, объясняющая назначение файла.
Затем вы сохранили данные из файла в data
вместо того, чтобы читать данные из Интернета. Затем вы повторно использовали process_data ()
и write_data_to_database ()
из файла best_practices.py
. В этом случае вы воспользовались преимуществом повторного использования своего кода вместо определения всей логики в main ()
.
Сводка передовых методов работы с основными функциями Python
Вот четыре ключевых передовых метода работы с main ()
в Python, которые вы только что видели:
Поместите код, который требует много времени для выполнения или оказывает другие эффекты на компьютере, в функцию или класс, чтобы вы могли точно контролировать, когда этот код выполняется.
Используйте различные значения
__name__
, чтобы определить контекст и изменить поведение вашего кода с помощью условного оператора.Вы должны назвать свою точку входа function
main ()
, чтобы передать назначение функции, даже если Python не придает особого значения функции с именемmain ()
.Если вы хотите повторно использовать функциональные возможности своего кода, определите логику в функциях вне
main ()
и вызовите эти функции вmain ()
.
Заключение
Поздравляем! Теперь вы знаете, как создавать функции Python main ()
.
Вы узнали следующее:
Знание значения переменной
__name__
важно для написания кода, который служит двойной цели исполняемого скрипта и импортируемого модуля.__name__
принимает разные значения в зависимости от того, как вы выполняли свой файл Python.__name__
будет равно:-
"__main__"
, когда файл выполняется из командной строки или сpython -m
(для выполнения пакета__main__.py
файл) - Имя модуля, если модуль импортируется
-
- Python разработали набор передовых методов, которые можно использовать при разработке повторно используемого кода.
Программисты
Теперь вы готовы написать отличный код функции Python main ()
!
Вызов Python из R
Вызов Python из R
Обзор
Пакет reticulate предоставляет интерфейс R для модулей, классов и функций Python.Например, этот код импортирует модуль Python os
и вызывает в нем некоторые функции:
библиотека (сетчатая)
os <- import ("os")
os $ listdir (".")
[1] «.git» «.gitignore» «.Rbuildignore» «.RData»
[5] «.Rhistory» «.Rproj.user» «.travis.yml» «appveyor.yml»
[9] «ОПИСАНИЕ» «документы» «внешние» «index.html»
[13] "index.Rmd" "inst" "выдает" "ЛИЦЕНЗИЮ"
[17] "человек" "NAMESPACE" "НОВОСТИ.md "" pkgdown "
[21] «R» «README.md» «reticulate.Rproj» «src»
[25] «тесты» «виньетки»
Доступ к функциям
и другим данным в модулях и классах Python можно получить с помощью оператора $
(аналогично тому, как вы взаимодействуете со списком R, средой или эталонным классом).
Пакет reticulate совместим со всеми версиями Python> = 2.7. Интеграция с NumPy не является обязательной и требует NumPy> = 1.6.
Версия Python
По умолчанию reticulate использует версию Python из PATH
(т.е. Sys.which («python»)
). Функция use_python ()
позволяет указать альтернативную версию, например:
библиотека (сетчатая)
use_python ("/ usr / local / bin / python")
Функции use_virtualenv ()
и use_condaenv ()
позволяют указать версии Python в виртуальных средах или средах conda, например:
библиотека (сетчатая)
use_virtualenv ("myenv")
Дополнительные сведения см. В статье «Конфигурация версии Python».
Пакеты Python
Вы можете установить любые необходимые пакеты Python с помощью стандартных инструментов оболочки, таких как pip
и conda
. В качестве альтернативы, reticulate включает в себя набор функций для управления и установки пакетов в средах virtualenvs и Conda. Дополнительные сведения см. В статье «Установка пакетов Python».
Преобразование типов
При вызове в Python типы данных R автоматически преобразуются в их эквивалентные типы Python.Когда значения возвращаются из Python в R, они преобразуются обратно в типы R. Типы преобразуются следующим образом:
Одноэлементный вектор | Скаляр | 1 , 1L , TRUE , "foo" |
Многоэлементный вектор | Список | c (1,0, 2,0, 3,0) , c (1 л, 2 л, 3 л) |
Список нескольких типов | Кортеж | список (1L, ИСТИНА, "foo") |
Именованный список | Dict | список (a = 1L, b = 2.0) , dict (x = x_data) |
Матрица / массив | NumPy ndarray | матрица (c (1,2,3,4), nrow = 2, ncol = 2) |
Фрейм данных | Панды DataFrame | data.frame (x = c (1,2,3), y = c («a», «b», «c»)) |
Функция | Функция Python | функция (x) x + 1 |
Необработанный | Python массив байтов | как.необработанный (c (1:10)) |
NULL, TRUE, FALSE | Нет, верно, неверно | NULL , TRUE , FALSE |
Если возвращается объект Python пользовательского класса, то возвращается ссылка R на этот объект. Вы можете вызывать методы и обращаться к свойствам объекта так же, как если бы он был экземпляром ссылочного класса R.
Импорт модулей
Функцию import ()
можно использовать для импорта любого модуля Python.Например:
difflib <- import ("difflib")
diffflib $ ndiff (фу, бар)
filecmp <- import ("filecmp")
filecmp $ cmp (каталог1, каталог2)
Функции import_main ()
и import_builtins ()
предоставляют вам доступ к основному модулю, в котором код выполняется по умолчанию, и к набору встроенных функций Python. Например:
основной <- import_main ()
встроенные <- import_builtins ()
встроенные $ print ('foo')
Основной модуль обычно полезен, если вы выполнили код Python из файла или строки и хотите получить доступ к его результатам (более подробную информацию см. В разделе ниже).
Сценарии поиска
Функция source_python ()
будет источником скрипта Python и сделает объекты, которые он создает, доступными в среде R (по умолчанию вызывающая среда). Например, рассмотрим следующий сценарий Python:
def add (x, y):
возврат x + y
Мы получаем его с помощью функции source_python ()
, а затем можем вызвать функцию add ()
непосредственно из R:
source_python ('доп.ру ')
добавить (5, 10)
[1] 15
Исполнительный код
Вы можете выполнить код Python в основном модуле с помощью функций py_run_file
и py_run_string
. Затем вы можете получить доступ к любым объектам, созданным с использованием объекта py
, экспортированного с помощью reticulate:
библиотека (сетчатая)
py_run_file ("script.py")
py_run_string ("x = 10")
# доступ к основному модулю python через объект 'py'
py $ x
Преобразование объекта
По умолчанию, когда объекты Python возвращаются в R, они преобразуются в их эквивалентные типы R.Однако, если вы предпочитаете явно выполнять преобразование из Python в R и работать с собственными объектами Python по умолчанию, вы можете передать convert = FALSE
в функцию import
. В этом случае преобразование Python в R будет отключено для модуля, возвращенного из import
. Например:
# import numpy и не указывать автоматическое преобразование Python в R
np <- import ("numpy", convert = FALSE)
# делаем некоторые манипуляции с массивами с помощью NumPy
a <- np $ массив (c (1: 4))
сумма <- $ cumsum ()
# преобразовать в R явно в конце
py_to_r (сумма)
Как показано выше, если вам нужен доступ к объекту R в конце ваших вычислений, вы можете явно вызвать функцию py_to_r ()
.
Получение помощи
Вы можете распечатать документацию на любом объекте Python с помощью функции py_help ()
. Например:
os <- import ("os")
py_help (ОС $ chdir)
Списки, кортежи и словари
Автоматическое преобразование типов R в типы Python работает хорошо в большинстве случаев, но иногда вам нужно будет более четко указать на стороне R, чтобы предоставить Python тип, который он ожидает.
Например, если API Python требует список, и вы передаете один элемент R-вектор, он будет преобразован в скаляр Python.Чтобы преодолеть это, просто используйте функцию R list
явно:
foo $ bar (индексы = список (42L))
Точно так же Python API может потребовать кортеж
, а не список. В этом случае вы можете использовать функцию tuple ()
:
Именованные списки
R преобразуются в словари Python, однако вы также можете явно создать словарь Python с помощью функции dict ()
:
dict (foo = "bar", index = 42L)
Это может быть полезно, если вам нужно передать словарь, который использует более сложный объект (в отличие от строки) в качестве своего ключа.
Числовые типы и индексы
R и Python имеют разные числовые типы по умолчанию. Если вы пишете 42
в R, это считается числом с плавающей запятой, тогда как 42
в Python считается целым числом.
Это означает, что когда API Python ожидает целое число, вам необходимо обязательно использовать суффикс L
в R. Например, если функция foo
требует целого числа в качестве аргумента index
, вы должны сделать следующее:
Коллекции
Python адресуются с помощью индексов на основе 0, а не индексов на основе 1, с которыми вы, возможно, знакомы из R.Итак, чтобы обратиться к первому элементу массива в R, вы должны написать:
Принимая во внимание, что если вы вызываете метод в Python через сетку, которая принимает индекс, вы должны написать это для адресации первого элемента:
Обратите внимание на использование индекса на основе 0, а также L
, чтобы указать, что значение является целым числом.
Массивы
Матрицы и массивы
R автоматически преобразуются в массивы NumPy и обратно.
При преобразовании из R в NumPy массив NumPy отображается непосредственно в базовую память массива R (копирование не производится).В этом случае массив NumPy использует структуру памяти на основе столбцов, совместимую с R (то есть стиль Fortran, а не стиль C). При преобразовании из NumPy в R, R получает упорядоченную по столбцам копию массива NumPy.
Вы также можете вручную преобразовать массивы R в NumPy, используя функцию np_array ()
. Например, вы можете сделать это, если вам нужно создать массив NumPy с C, а не в стиле Fortran в макете в памяти (для более высокой производительности в строковых вычислениях) или если вы хотите более явно контролировать тип данных массива NumPy. .Вот несколько примеров использования np_array ()
:
a <- np_array (c (1: 8), dtype = "float16")
a <- np_array (c (1: 8), order = "C")
Рассуждения о массивах, использующих различные порядки в памяти, могут быть непростыми. Дополнительные сведения приведены в статье «Массивы в R и Python».
Кроме того, всегда помните, что при вызове методов NumPy индексы массива основаны на 0, а не на 1 и требуют суффикса L
, чтобы указать, что они являются целыми числами.
Фреймы данных
Кадры данных
R могут быть автоматически преобразованы в Pandas DataFrames и обратно.По умолчанию столбцы преобразуются с использованием тех же правил, что и преобразование массива R array <-> NumPy, но предоставляется пара расширений:
Фактор | Категориальная переменная |
POSIXt | Массив NumPy с dtype = datetime64 [нс] |
Если фрейм данных R имеет имена строк, сгенерированный фрейм данных Pandas будет повторно проиндексирован с использованием этих имен строк (и наоборот). Специальная обработка также доступна для DatetimeIndex
, связанного с Pandas DataFrame; однако, поскольку R поддерживает только символьные векторы для имен строк, они сначала преобразуются в символы.
Разреженные матрицы
Разреженные матрицы, созданные пакетом Matrix R, могут быть преобразованы в матрицу Scipy CSC и наоборот. Это часто бывает полезно, когда вы хотите передать разреженные матрицы функциям Python, которые принимают матрицу Scipy CSC, чтобы воспользоваться преимуществами этого формата, такими как эффективное нарезание столбцов и быстрые матричные векторные произведения.
Например, мы сначала создаем разреженную матрицу, используя Matrix :: sparseMatrix ()
:
библиотека (матрица)
N <- 5
dgc_matrix <- sparseMatrix (
я = образец (N, N),
j = образец (N, N),
х = runif (N),
dims = c (N, N))
Разреженная матрица выглядит так:
> dgc_matrix
5 x 5 разреженная матрица класса "dgCMatrix"
[1,] 0.2264952. . . .
[2,]. . . . 0,32
[3,]. . . 0,08.
[4,]. . 0,01777771. .
[5,]. 0,05885743. . .
Давайте преобразуем его в матрицу Scipy CSC, используя r_to_py ()
:
> csc_matrix <- r_to_py (x)
> csc_matrix
(0, 0) 0,226495201467
(4, 1) 0,0588574311696
(3, 2) 0,0177777127828
(2, 3) 0.9215982
(1, 4) 0,3160601
Обратите внимание, что правая часть содержит ненулевые элементы матрицы, а левая часть представляет их расположение в матрице.
Мы также можем использовать py_to_r ()
для преобразования матрицы CSC обратно в представление Matrix :: dgCMatrix
, которым затем можно легко манипулировать в R, который аналогичен исходной разреженной матрице, которую мы создали ранее с помощью Matrix :: sparseMatrix ()
:
> py_to_r (csc_matrix)
5 x 5 разреженная матрица класса "dgCMatrix"
[1,] 0.2264952. . . .
[2,]. . . . 0,32
[3,]. . . 0,08.
[4,]. . 0,01777771. .
[5,]. 0,05885743. . .
с контекстами
R с универсальной функцией
может использоваться для взаимодействия с объектами диспетчера контекста Python (в Python вы используете ключевое слово с
, чтобы сделать то же самое). Например:
py <- import_builtins ()
с (py $ open ("output.txt "," w ")% как% file, {
file $ write ("Здравствуйте!")
})
В этом примере открывается файл и обеспечивается его автоматическое закрытие в конце блока with. Обратите внимание на использование оператора % в качестве оператора%
для псевдонима объекта, созданного диспетчером контекста.
Итераторы
Если API Python возвращает итератор или генератор, вы можете взаимодействовать с ним, используя функцию iterate ()
. Функцию iterate ()
можно использовать для применения функции R к каждому элементу, полученному итератором:
Если вы не передадите функцию в итерацию
, результаты будут собраны в вектор R:
Обратите внимание, что Iterators
будут очищены от своих значений с помощью iterate ()
:
a <- iterate (iter) # результаты не пустые
b <- iterate (iter) # результаты пусты, так как элементы уже опустошены
Итерация уровня элемента
Вы также можете выполнять итерацию поэлементно, используя функцию iter_next ()
.Например:
пока (ИСТИНА) {
элемент <- iter_next (iter)
если (is.null (элемент))
перерыв
}
По умолчанию iter_next ()
вернет NULL
по завершении итерации, но вы можете указать настраиваемое значение завершено
, оно будет возвращено вместо него. Например:
пока (ИСТИНА) {
item <- iter_next (iter, completed = NA)
если (is.na (элемент))
перерыв
}
Обратите внимание, что некоторые итераторы / генераторы в Python бесконечны.В этом случае вызывающей стороне потребуется настраиваемая логика, чтобы определить, когда завершить цикл.
Генераторы
Генераторы
Python - это функции, реализующие протокол итератора Python. Точно так же функция reticulate generator ()
позволяет вам создать итератор Python из функции R.
В Python генераторы выдают значения с помощью ключевого слова yield
. В R значения просто возвращаются из функции. Одним из преимуществ ключевого слова yield
является то, что оно позволяет последовательным итерациям использовать состояние предыдущих итераций.В R это можно сделать, вернув функцию, которая изменяет окружающую среду с помощью оператора << -. Например:
# определить функцию генератора
sequence_generator <-function (start) {
значение <- начало
function () {
значение << - значение + 1
значение
}
}
# преобразовываем функцию в итератор python
iter <- py_iterator (генератор_последовательностей (10))
Если вы хотите указать конец итерации, верните NULL
из функции:
sequence_generator <-function (start) {
значение <- начало
function () {
значение << - значение + 1
если (значение <100)
значение
еще
НОЛЬ
}
}
Обратите внимание, что вы можете изменить значение, указывающее на конец итерации, используя параметр completed
(например,грамм. py_iterator (func, completed = NA)
).
Функции
Подписи
По умолчанию функции R преобразуются в Python с общей сигнатурой ( function (...)
), где нет ни аргументов ключевого слова, ни значений по умолчанию для аргументов.
Например, ниже мы применяем r_to_py ()
к функции R, а затем используем модуль inspect Python, чтобы получить спецификацию аргумента преобразованной функции. Вы можете видеть, что подпись обернутой функции отличается от подписи исходной функции R.
> инспектировать <- import ("inspect")
> convert_func <- r_to_py (функция (a, b = 1.5) {})
> проверить $ getargspec (convert_func)
ArgSpec (args = [], varargs = 'args', keywords = 'kwargs', по умолчанию = None)
Это преобразование по умолчанию обычно работает нормально, однако в некоторых библиотеках Python предусмотрена строгая проверка сигнатур функций для обратных вызовов, предоставляемых пользователем. В этих случаях общая подпись функции (...)
не сможет выполнить эту проверку.
В этих случаях вы можете использовать py_func ()
, чтобы обернуть функцию R так, чтобы обернутая функция имела точно такую же сигнатуру, что и исходная функция R, т.е.грамм. один аргумент a
без значения по умолчанию и другой аргумент b
со значением по умолчанию 1.5.
> wrapped_func <- py_func (функция (a, b = 1.5) {})
> проверить $ getargspec (wrapped_func)
ArgSpec (args = ['a', 'b'], varargs = None, keywords = None, по умолчанию = (1.5,))
Обратите внимание, что сигнатура функции R не должна содержать эзотерических несовместимых с Python конструкций. Например, у нас не может быть функции R с сигнатурой, такой как функция (a = 1, b)
, поскольку функция Python требует, чтобы аргументы без значений по умолчанию появлялись перед аргументами со значениями по умолчанию.
Фоновые потоки
В некоторых случаях библиотеки Python вызывают обратные вызовы в фоновом потоке Python. Поскольку код R должен выполняться в основном потоке, это не будет работать по умолчанию, если вы передадите функцию R в качестве обратного вызова.
Чтобы обойти это, вы можете использовать py_main_thread_func ()
, который предоставит специальную оболочку для вашей функции R, которая гарантирует, что она будет вызываться только в основном потоке.
Продвинутый
Доступно несколько более сложных функций, которые в основном полезны при создании высокоуровневых интерфейсов R для библиотек Python.
Объекты Python
Обычно взаимодействие с объектами Python из R включает использование оператора $
для доступа к любым свойствам функций объекта, которые вам нужны. При использовании $
объекты Python автоматически конвертируются в их эквиваленты в R. Следующие функции позволяют вам взаимодействовать с объектами Python на более низком уровне (например, преобразование в R не выполняется, если вы явно не вызываете функцию py_to_r
):
py_has_attr () | Проверить, имеет ли объект указанный атрибут. |
py_get_attr () | Получить атрибут объекта Python. |
py_set_attr () | Установить атрибут объекта Python. |
py_list_attributes () | Список всех атрибутов объекта Python. |
py_len () | Длина объекта Python. |
py_call () | Вызов вызываемого объекта Python с указанными аргументами. |
py_to_r () | Преобразование объекта Python в его эквивалент в R |
r_to_py () | Преобразование объекта R в его эквивалент Python |
Рассол
Вы можете сохранять и загружать объекты Python (через pickle) с помощью функций py_save_object
и py_load_object
:
py_save_object () | Сохраните объект Python в файл с помощью pickle. |
py_load_object () | Загрузите ранее сохраненный объект Python из файла. |
Конфигурация
Следующие функции позволяют запрашивать информацию о конфигурации Python, доступной в текущей системе.
py_available () | Проверьте, доступен ли в этой системе интерфейс Python. |
py_numpy_available () | Проверьте, доступен ли интерфейс R для NumPy (требуется NumPy> = 1.6) |
py_module_available () | Проверьте, доступен ли модуль Python в этой системе. |
py_config () | Получите информацию о местонахождении и используемой версии Python. |
Управление выводом
Эти функции позволяют захватывать или подавлять вывод Python:
py_capture_output () | Захватить вывод Python для указанного выражения и вернуть его как вектор символов R. |
py_suppress_warnings () | Выполнить указанное выражение, подавив отображение предупреждений Python. |
Разное
Функции предоставляют разные другие возможности нижнего уровня:
py_set_seed () | Установить случайные начальные числа Python и NumPy. |
py_unicode () | Преобразует строку в объект Unicode Python. |
py_str () | Получить строковое представление объекта Python. |
py_id () | Получить уникальный идентификатор для объекта Python |
py_is_null_xptr () | Проверить, является ли объект Python пустым externalptr. |
py_validate_xptr () | Проверяет, является ли объект Python пустым externalptr, и выдает ошибку, если это так. |
Дополнительные сведения
В следующих статьях рассматриваются дополнительные аспекты использования сетки :
Python-функций (def): определение с примерами
Что такое функция в Python?
В Python функция - это группа связанных операторов, выполняющих определенную задачу.
Функции помогают разбить нашу программу на более мелкие и модульные части. По мере того, как наша программа становится все больше и больше, функции делают ее более организованной и управляемой.
Кроме того, он позволяет избежать повторения и позволяет многократно использовать код.
Синтаксис функции
def имя_функции (параметры): "" "строка документации" "" выписка (а)
Выше показано определение функции, состоящее из следующих компонентов.
- Ключевое слово
def
, которое отмечает начало заголовка функции. - Имя функции, однозначно идентифицирующее функцию. Именование функций следует тем же правилам написания идентификаторов в Python.
- Параметры (аргументы), через которые мы передаем значения функции. Они не обязательны.
- Двоеточие (:) для обозначения конца заголовка функции.
- Необязательная строка документации (docstring), описывающая, что делает функция.
- Один или несколько допустимых операторов Python, составляющих тело функции. Заявления должны иметь одинаковый уровень отступа (обычно 4 пробела).
- Необязательный оператор
return
для возврата значения из функции.
Пример функции
def greet (имя):
"" "
Эта функция приветствует
человек прошел как
параметр
"" "
print («Привет,» + имя + «. Доброе утро!»)
Как вызвать функцию в Python?
После того, как мы определили функцию, мы можем вызвать ее из другой функции, программы или даже из командной строки Python. Чтобы вызвать функцию, мы просто вводим имя функции с соответствующими параметрами.
>>> привет ('Павел')
Привет, Пол.Доброе утро!
Примечание: Попробуйте запустить приведенный выше код в программе Python с определением функции, чтобы увидеть результат.
def greet (имя):
"" "
Эта функция приветствует
человек прошел как
параметр
"" "
print ("Привет," + имя + ". Доброе утро!")
привет ('Пол')
Строки документации
Первая строка после заголовка функции называется строкой документации и является сокращением от строки документации.Он кратко используется для объяснения того, что делает функция.
Документация, хотя и не обязательна, является хорошей практикой программирования. Если вы не помните, что ели на ужин на прошлой неделе, всегда документируйте свой код.
В приведенном выше примере у нас есть строка документации непосредственно под заголовком функции. Обычно мы используем тройные кавычки, чтобы строка документа могла занимать несколько строк. Эта строка доступна нам как атрибут функции __doc__
.
Например, :
Попробуйте запустить в оболочке Python следующую команду, чтобы увидеть результат.
>>> печать (привет .__ doc__)
Эта функция приветствует
человек прошел как
параметр
Чтобы узнать больше о строках документации в Python, посетите веб-сайт Python Docstrings.
Заявление о возврате
Оператор return
используется для выхода из функции и возврата в то место, откуда она была вызвана.
Синтаксис возврата
вернуть [список_выражений]
Этот оператор может содержать выражение, которое вычисляется и возвращается значение.Если в операторе нет выражения или сам оператор return
отсутствует внутри функции, тогда функция вернет объект None
.
Например:
>>> print (привет («май»))
Привет, май. Доброе утро!
Нет
Здесь Нет
- это возвращаемое значение, поскольку greet ()
напрямую печатает имя, а оператор return
не используется.
Пример возврата
def absolute_value (число):
"" "Эта функция возвращает абсолютное
значение введенного числа "" "
если число> = 0:
вернуть номер
еще:
return -num
печать (абсолютное_значение (2))
печать (абсолютное_значение (-4))
Выход
2 4
Как функция работает в Python?
Работа функций в Python
Объем и время жизни переменных
Область действия переменной - это часть программы, в которой эта переменная распознается.Параметры и переменные, определенные внутри функции, не видны снаружи функции. Следовательно, они имеют локальный охват.
Время жизни переменной - это период, в течение которого переменная находится в памяти. Время жизни переменных внутри функции - до тех пор, пока функция выполняется.
Они уничтожаются, когда мы возвращаемся из функции. Следовательно, функция не запоминает значение переменной из своих предыдущих вызовов.
Вот пример, иллюстрирующий область действия переменной внутри функции.
def my_func ():
х = 10
print ("Значение внутри функции:", x)
х = 20
my_func ()
print ("Значение вне функции:", x)
Выход
Значение внутри функции: 10 Значение вне функции: 20
Здесь мы видим, что значение x изначально равно 20. Хотя функция my_func ()
изменила значение x на 10, это не повлияло на значение вне функции.
Это связано с тем, что переменная x внутри функции отличается (локальная для функции) от переменной снаружи.Хотя у них одинаковые имена, это две разные переменные с разными областями действия.
С другой стороны, переменные вне функции видны изнутри. У них глобальный размах.
Мы можем читать эти значения изнутри функции, но не можем их изменять (записывать). Чтобы изменить значение переменных вне функции, они должны быть объявлены как глобальные переменные с использованием ключевого слова global
.
Типы функций
В принципе, мы можем разделить функции на следующие два типа:
- Встроенные функции - Функции, встроенные в Python.
- Пользовательские функции - Функции, определяемые самими пользователями.
IDL в Python Bridge
IDL в Python Bridge
Класс PYTHON обеспечивает мост между IDL и Python. Класс содержит набор статических методов, которые позволяют инициализировать интерпретатор Python и взаимодействовать с ним. Кроме того, каждый объект, переданный обратно из Python, упакован в экземпляр класса PYTHON; затем вы можете вызывать методы базового объекта Python или устанавливать и извлекать атрибуты объекта.Класс PYTHON также использует перегрузку операторов, так что многие встроенные процедуры IDL и математические операции будут без проблем работать с обернутым объектом Python.
См. Инструкции по установке в Python Bridge.
Методы и дополнительная информация
См. Также Python Bridge: передача параметров и преобразование данных.
Примеры
Вызов методов Python непосредственно из кода IDL, как если бы это был объект IDL:
IDL> np = Python.Импорт ('numpy')
IDL> arr = np.random.rand (100)
IDL> печать, np.mean (arr)
IDL> печать, np.std (arr, dtype = 'float32')
Выполните те же команды непосредственно в интерпретаторе Python:
IDL> Python.Run ('импортировать numpy.random как запущено')
IDL> Python.Run ('arr = ran.rand (100)')
IDL> Python.Run ('печать (arr.mean ())')
IDL> Python.Выполнить ('print (arr.std (dtype = "float32"))')
В качестве ярлыка для Python.Run вы можете вводить три символа «>>>» перед каждым оператором:
IDL> >>> import numpy.random as run
IDL> >>> arr = ran.rand (100)
IDL> >>> print (arr.mean ())
IDL> >>> print (arr.std (dtype = 'float32'))
Вы также можете ввести три символа «>>>» и нажать клавишу Enter , чтобы войти в командный режим Python:
IDL> >>>
>>> import numpy.случайный при пробеге
>>> arr = ran.rand (100)
>>> печать (arr.mean ())
>>> печать (arr.std (dtype = 'float32'))
>>>
IDL>
В конце нажмите клавишу Enter , чтобы повторно войти в обычный командный режим IDL.
Синтаксис
Результат = PYTHON ([ PyID ])
Примечание: Вам не нужно напрямую вызывать функцию PYTHON.Вместо этого используйте Python.Import () или Python.Wrap () для возврата ссылок на объекты Python. Этот синтаксис предоставлен здесь только для полноты.
Возвращаемое значение
Возвращает ссылку на вновь созданный объект PYTHON.
Аргументы
PyID
Целое число, дающее идентификатор объекта в интерпретаторе Python. Если PyID не указан, то возвращается ссылка на объект Python __main__.
Ключевые слова
Нет
Статический метод Python :: Import импортирует модуль в интерпретатор Python. При этом также запускается интерпретатор Python, если он еще не запущен.
Синтаксис
Результат = Python.Import ( Модуль )
или
Python.Import, Модуль
Возвращаемое значение
Если Python.Импорт вызывается как функция, а результатом является объект PYTHON, который является оболочкой для модуля Python. Затем вы можете вызывать функции и получать или устанавливать атрибуты, используя обозначение «точки» для этого объекта. Это эквивалентно оператору Python import Module как результат .
Если Python.Import вызывается как процедура, то все функции и атрибуты в модуле импортируются в Python __main__. Это эквивалентно оператору Python из модуля Module import *.В этом случае все функции и атрибуты будут доступны из статического класса Python.
Примечание: В Python обычно считается плохой практикой использовать from module import *, поскольку это загрязняет пространство имен Python __main__ и может привести к конфликтам имен. Чтобы избежать этого, используйте метод функции Python.Import и получите доступ ко всем своим методам и атрибутам из возвращенного объекта PYTHON.
Аргументы
Модуль
Строка, дающая имя импортируемого модуля Python.Чтобы импортировать модуль в пакет модулей, вы можете использовать точки для разделения имен, например, module.submodule.subsubmodule.
Ключевые слова
Нет
Статический метод Python :: Run выполняет команды Python в интерпретаторе Python. При этом также запускается интерпретатор Python, если он еще не запущен.
Примеры
Выполнить произвольный код Python и распечатать результат:
IDL> ПЕЧАТЬ, Python.Выполнить ('[x * x for x in range (1,11)]')
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Создайте «генератор» Python, получите генератор и распечатайте значения с помощью foreach:
IDL> Python.Run, 'mygen = (x * x для x в диапазоне (1,11))'
IDL> mygen = Python.mygen
IDL> HELP, mygen
MYGEN PYTHON<класс 'генератор'>
IDL> foreach val, mygen do PRINT, val
1
4
...
100
Создайте новую функцию Python, используя многострочную инструкцию, затем используйте ее:
IDL> Python.Run, "def hello (x): \ n" + $
"'Печатает дружеское сообщение' \ n" +
$
"return 'Hello,' + x + '!'"
IDL> Python.Help (Python.hello)
Документация библиотеки Python: функция hello в module_main_
привет (x)
Печатает дружеское сообщение
IDL> ПЕЧАТЬ, Python.привет ("Мир")
Привет, мир!
Синтаксис
Результат = Python.Run ( Команда )
или
Python.Run, Команда
Возвращаемое значение
Если Python.Run вызывается как метод функции, то результатом является скалярная строка IDL или массив строк, содержащий выходные данные Python при выполнении команды. Если команда не выдала никаких результатов, возвращается пустая строка.
Если Python.Run вызывается как метод процедуры, то вывод Python перенаправляется на консоль IDL.
Аргументы
Команда
Строка IDL или массив строк, содержащий операторы Python для выполнения в интерпретаторе Python. Если Команда представляет собой массив строк, то IDL отправит каждый элемент в Python как отдельный оператор. Вы также можете отправить многострочный оператор (например, определение функции), используя одну строку IDL и разделяя строки escape-кодом "\ n".
Ключевые слова
Нет.
Статический метод Python :: Wrap преобразует переменную IDL в объект Python и возвращает завернутый объект PYTHON. При этом также запускается интерпретатор Python, если он еще не запущен.
Примеры
Оберните массив IDL с плавающей запятой и вызовите для него метод Python:
IDL> a = Python.Wrap (RANDOMU (семя, 10000))
IDL> HELP, a
PYTHON
IDL> ПЕЧАТЬ, среднее значение ()
0,497082
Синтаксис
Результат = Python.Wrap ( Значение )
Возвращаемое значение
Результатом является объект PYTHON, содержащий копию данных IDL. Затем вы можете вызывать функции и получать или устанавливать атрибуты, используя обозначение «точки» для этого объекта.
Примечание: Данные являются копией исходных данных, а не справочными.Например, изменение значений в массиве IDL не повлияет на обернутый массив.
Аргументы
Значение
IDL-переменная любого типа, кроме указателя.
Ключевые слова
Нет
Если у вас есть объект PYTHON (скажем, при вызове Python :: Import), вы можете вызывать методы Python для этого объекта, используя "точечную" нотацию. Аргументы и ключевые слова могут быть переданы методу, как при вызове метода IDL.Например:
label = ['Matlab', 'Python', 'IDL', 'Другое']
размеров = [20, 30, 40, 10]
цветов = ['желто-зеленый', 'золотой', 'светло-голубой', 'светло-коралловый']
разнести = [0, 0, 0.1, 0]
pyplot = Python.Import ('matplotlib.pyplot')
пирог = pyplot.пирог (размеры, разнесение = разнесение, $
этикеток = этикетки, цвета = цвета,
долларов США
autopct = '% 1.1f %%', / shadow, startangle = 90)
void = pyplot.axis ('равно')
void = pyplot.savefig ("myplot.png", dpi = 96)
void = pyplot.show ()
Это создает фигуру, показанную справа. Обратите внимание, что при вызове методов вы можете передавать переменные IDL, используя стандартный синтаксис для аргументов и ключевых слов.
Если у вас есть объект PYTHON (скажем, при вызове Python :: Import или Python :: Wrap), вы можете извлекать или устанавливать атрибуты для этого объекта, используя "точечную" нотацию. Например, многие объекты Python имеют атрибут __doc__, который содержит краткое описание класса. Мы можем получить к нему доступ, используя следующий код:
IDL> rand = Python.Import ('numpy.random')
IDL> rand .__ doc__
========================
Генерация случайных чисел
========================
...
Вы также можете установить значения атрибутов, используя "точечную" нотацию. Например, мы можем определить наш собственный класс Python, а затем получить и установить атрибуты для экземпляра этого класса:
Python.Run, "класс MyClass: \ n" +
долларов США
"myattr = 'world?' \ N" +
долларов
"def hello (self): \ n" +
долларов
"'Печатает дружеское сообщение' \ n" +
$
"return" Hello, "+ self.myattr
Python.Run, 'myvar = MyClass ()'
myvar = Python.myvar
ПЕЧАТЬ, myvar.myattr
myvar.myattr = 'Мир!'
ПЕЧАТЬ, myvar.hello ()
отпечатков IDL:
мир?
Привет, мир!
Вы можете вызвать встроенный в Python метод dir, чтобы получить список IDL, содержащий доступные методы и атрибуты.Например:
IDL> pyplot = Python.Import ('matplotlib.pyplot')
IDL> attr = Python.dir (pyplot)
IDL> attr.ToArray ()
Аннотация
Стрелка
Художник
...
илим
yscale
yticks
Имена переменных и методов IDL не чувствительны к регистру, а имена Python - к регистру.Когда вы вызываете метод Python, такой как метод savefig, в модуле matplotlib.pyplot, мост Python сначала ищет метод в нижнем регистре с этим именем. Если совпадение не найдено, мост использует внутренний словарь методов и атрибутов (__dict__), чтобы попытаться найти совпадение без учета регистра. Предполагается, что первое совпадение правильное.
Если в вашем модуле есть два метода или атрибута с одинаковыми именами, которые отличаются только регистром, вы можете использовать встроенный Python.getattr, чтобы получить правильный.
Python имеет ряд «встроенных» методов, таких как abs (), cmp (), dir (), map () или reduce (). Их можно вызывать как статические методы в классе Python. Например:
IDL> a = -5
IDL> Python.abs (a)
5
IDL> Python.dir ()
[
"_builtins_",
"_doc_",
"_name_",
"_package_",
«mygen»,
«мивар»
]
Вот неполный список встроенных функций, которые могут вам пригодиться:
Python.dir ([
объект ])
Без аргументов вернуть список имен в текущей локальной области. С помощью объекта Python верните список допустимых атрибутов для этого объекта.
Python.getattr (
объект , attr [, по умолчанию ])
Получить атрибут attr из объекта Python. attr должен быть строкой. Если атрибут не существует, то возвращается по умолчанию , если он указан, в противном случае выдается ошибка.
Python.hasattr (
объект , attr )
Вернуть true, если объект имеет атрибут attr , или false в противном случае.
Python.help (
объект )
Возвращает массив строк, содержащий справочную документацию для указанного объекта .
Python.id (
объект )
Возвращает целое число, дающее номер идентификатора Python.
Python.isinstance (
объект , classinfo )
Возвращает 1 (истина), если объект Python является экземпляром объекта Python classinfo , или 0 (ложь) в противном случае.
Python.len (
объект )
Возвращает целое число, дающее количество элементов в объекте.
Python.repr (
объект )
Возвращает строковое представление объекта.Это то же самое, что вернет подразумеваемая печать IDL.
Python.setattr (
объект , attr , значение )
Установите для атрибута attr объекта Python предоставленное значение . attr должен быть строкой.
Python.str (
объект )
Возвращает строковое представление объекта. Это то же самое, что и обычная печать IDL.
Python.vars (
объект )
Возвращает хеш IDL, содержащий атрибут __dict__ для объекта. Атрибут __dict__ содержит все атрибуты и методы объекта.
В IDL объекты PYTHON могут использоваться в большинстве математических, логических и побитовых выражений. Когда объект PYTHON используется в выражении, любые «нормальные» переменные IDL (такие как целые числа, числа с плавающей запятой или строки) сначала преобразуются в объекты PYTHON.Затем вызывается соответствующий «магический метод» Python и возвращается результат. Результат преобразуется обратно в обычную переменную IDL, если это возможно, в противном случае возвращается объект PYTHON.
В следующей таблице перечислены магические методы Python, которые вызываются для каждого оператора IDL:
Оператор | Синтаксис IDL | Звонок Python |
плюс | а + б | __добавить__ |
Минус | а - б | __sub__ |
Звездочка | а * б | __мул__ |
Слэш | а / б | __div__ |
Минус Обычный | –a | __neg__ |
Каретка | а ^ б | __pow__ |
Модуль упругости | а мод б | __mod__ |
Равно | a EQ b | __eq__ |
Не равно | a NE b | __ne__ |
Больше или равно | a GE b | __ge__ |
Меньше или равно | a LE b | __le__ |
Больше | a GT b | __gt__ |
Менее | a LT b | __lt__ |
Побитовое и | а И б | __и__ |
Побитовое или | a OR b | __или __ |
Побитовый xor | а XOR б | __xor__ |
Побитовое, а не | НЕ b | __invert__ |
Верно | , если (а) | булев () |
Логическое отрицание | ~ | ~ логическое значение (а) |
Во многих случаях вы можете получить доступ к элементам объекта PYTHON, используя стандартный синтаксис массива IDL, как если бы объект был "нормальным" массивом IDL.Когда вы обращаетесь к объекту Python и получаете элемент или набор элементов, IDL преобразует результат обратно в «нормальную» переменную IDL. Вот несколько примеров:
Использование списка Python или кортежа
IDL> var = Python.Wrap (Список (3.14, 1234, 'привет'))
IDL> HELP, var
VAR PYTHON
IDL> ПЕЧАТЬ, переменная [2]
привет
IDL> var [0] = 3.14159
IDL> ПЕЧАТЬ, вар
[3,1415901, 1234, "привет"]
Использование словаря Python
IDL> var = Python.Wrap (HASH ('Меркурий', 1, 'Венера', 2, 'Земля', 3))
IDL> HELP, var
VAR PYTHON
IDL> ПЕЧАТЬ, var ['Venus']
2
IDL> var ['Mars'] = 4
IDL> ПЕЧАТЬ, вар
{'Меркурий': 1, 'Земля': 3, 'Марс': 4, 'Венера': 2}
Использование массива Python
IDL> var = Python.Обертка (INDGEN (10))
IDL> HELP, var
VAR PYTHON
IDL> ПЕЧАТЬ, переменная [3: 5]
3 4 5
IDL> var [-2: *] = -1
IDL> ПЕЧАТЬ, вар
[0 1 2 3 4 5 6 7 -1 -1]
Вы можете использовать оператор IDL FOREACH для списка Python, кортежа, dict, ndarray или любого другого класса Python, реализующего протокол итератора.Например:
IDL> a = Python.Wrap (СПИСОК ('a', 2, 'c', 3.14))
IDL> foreach значение, a, index do print, value, index
a 0
2 1
c 2
3,14000 3
Для Python dict (или других классов с итератором) переменная FOREACH «index» (третий аргумент) будет установлена равной итератору Python. Избегайте изменения этой переменной.Например:
IDL> a = Python.Wrap (HASH ('Меркурий', 1, 'Венера', 2, 'Земля', 3))
IDL> HELP, a
ПИТОН
IDL> для каждой планеты, a, индекс do HELP, планета, индекс
СТРОКА ПЛАНЕТЫ = 'Венера'
ИНДЕКС PYTHON<класс 'dict_keyiterator'>
СТРОКА ПЛАНЕТЫ = 'Меркурий'
ИНДЕКС PYTHON<класс 'dict_keyiterator'>
СТРОКА ПЛАНЕТЫ = 'Земля'
ИНДЕКС PYTHON<класс 'dict_keyiterator'>
В большинстве случаев вы вызываете методы, используя нотацию «точка» в модуле или объекте Python.Например:
IDL> randomModule = Python.Import ("numpy.random")
IDL> x = randomModule.rand (50)
IDL> HELP, x
X DOUBLE = массив [50]
Однако в некоторых случаях вы можете получить ссылку на реальный объект метода Python. Например, если вы использовали Python.getattr для получения объекта метода по имени:
IDL> randMethod = Python.getattr (randomModule, 'ранд')
IDL> HELP, rand Метод
RANDMETHOD PYTHON<класс 'builtin_function_or_method'>
IDL> Python.callable (randMethod)
1
В этих случаях вы можете вызвать метод, используя круглые скобки:
IDL> x = randMethod (50)
IDL> HELP, x
X DOUBLE = массив [50]
Вы также можете использовать магический метод __call__ для вызова метода:
IDL> x = randMethod.__звоните __ (50)
Обычно вы передаете переменные IDL в вызовы методов Python в качестве входных аргументов или ключевых слов и получаете результат обратно как переменную IDL. Однако, если вы используете метод Python.Run, вы можете напрямую передавать переменные в Python или из него. Для этого вы можете использовать стандартную "точечную" нотацию IDL в статическом классе PYTHON. Например:
Установить переменную Python
IDL> Python.myvar = НАЙТИ (10)
IDL> Python.Беги ('myvar')
'массив ([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.], dtype = float32)'
Получить переменную Python
IDL> myvar2 = Python.myvar
IDL> СПРАВКА, myvar2
MYVAR2 FLOAT = массив [10]
Примечание: Всем переменным, передаваемым в Python, будут присвоены имена в нижнем регистре, независимо от исходного регистра. При извлечении переменных из Python в первую очередь будет использоваться версия в нижнем регистре.Если совпадение не найдено, то мост использует внутренний словарь атрибутов Python (__dict__), чтобы попытаться найти совпадение без учета регистра. Предполагается, что первое совпадение правильное. Если у вас есть две переменные с одинаковыми именами, которые отличаются только регистром, вы можете использовать встроенный Python.getattr для получения правильной. Например:
IDL> void = Python.Run ('var = 1')
.
IDL> void = Python.Run ('Var = 2')
IDL> ПЕЧАТЬ, Python.вар
1
IDL>
IDL> ПЕЧАТЬ, Python.getattr (Python (), 'Var')
2
Совет: Для получения дополнительной информации о передаче переменных см. Python Bridge: передача параметров и преобразование данных.
В большинстве случаев вы захотите взаимодействовать с Python, используя Python.Import для извлечения модуля, а затем вызывая методы для возвращенного объекта PYTHON.Однако вы также можете выполнять операторы Python непосредственно в интерпретаторе Python с помощью метода Python.Run. В качестве ярлыка для ввода Python.Run вы также можете ввести три символа «>>>»:
IDL> >>> импортировать matplotlib.pyplot как plt
IDL> >>> import numpy.random as run
IDL> >>> arr = ran.rand (100)
IDL> >>> p = plt.plot (arr)
IDL> >>> plt.показать ()
Обратите внимание, что вам нужно вводить три символа «>>>» перед каждым оператором Python.
В качестве альтернативы вы можете ввести три символа «>>>» и нажать клавишу Enter , чтобы войти в «Командный режим Python»:
IDL> >>>
>>> импортировать matplotlib.pyplot как plt
>>> import numpy.random as ran
>>> arr = run.ранд (100)
>>> p = plt.plot (arr)
>>> plt.show ()
>>>
В конце нажмите клавишу Enter , чтобы повторно войти в обычный командный режим IDL.
В командном режиме Python есть несколько ограничений:
- В отличие от обычной консоли Python, вы не можете вводить многострочную конструкцию в отдельные строки. Чтобы ввести конструкцию с несколькими операторами (например, определение функции), все операторы должны находиться в одной строке, разделенные точкой с запятой.Например:
>>> def привет (x): печать (x)
- Любые переменные или объекты, созданные с помощью Python.Run или командного режима Python, будут существовать только в интерпретаторе Python. Для работы с этими объектами в IDL вам потребуется получить ссылку. Например:
IDL> >>>
>>> импортировать matplotlib.pyplot как plt
>>> import numpy.случайный при пробеге
>>> arr = ran.rand (100)
>>>
IDL> myarr = Python.arr
N_ELEMENTS, РАЗМЕР, ДЛИНА, NDIM
Функция N_ELEMENTS и атрибут переменной LENGTH возвращают количество элементов в объекте Python. Для объектов Python ndarray атрибут shape используется для вычисления общего количества элементов. Для всех остальных объектов Python IDL вызывает Python.len () и возвращает результат.
Атрибут переменной DIM возвращает скаляр или массив, задающий размеры объекта. Для объектов Python ndarray DIM устанавливается равным атрибуту shape в обратном порядке (поскольку Python является "основной строкой"). Для всех других объектов Python IDL вызывает Python.len () для объекта и возвращает результат.
Атрибут переменной NDIM возвращает целое число, дающее количество измерений объекта. Для объектов Python ndarray NDIM устанавливается равным количеству элементов в атрибуте shape.Для всех других объектов Python IDL возвращает NDIM = 1 для объектов Python, содержащих несколько элементов (таких как списки, кортежи или словари), или 0 для скалярных объектов Python.
СПРАВКА
Процедура HELP предоставляет краткое описание объекта PYTHON:
IDL> np = Python.Import ('numpy')
.
IDL> HELP, NP
NP PYTHON<класс 'модуль'>
IDL> HELP, np.ndarray
<Выражение> PYTHON
Для получения более подробной информации вызовите Python.help () для своего объекта.
ISA
Функция IDL ISA использует следующие правила:
- ISA ( pyObj , 'OBJREF') всегда возвращает истину
- ISA ( pyObj , / ARRAY) возвращает истину, если pyObj считается «массивом», или ложь в противном случае.См. Правила N_ELEMENTS выше.
- ISA ( pyObj , / SCALAR) возвращает истину, если pyObj считается «скаляром», или ложь в противном случае. См. Правила N_ELEMENTS выше.
Все остальные ключевые слова ISA вернут false.
TYPECODE, TNAME, TYPENAME
Объекты
PYTHON всегда возвращают TYPECODE = 11, TNAME = 'OBJREF' и TYPENAME = 'PYTHON'.
ПЕЧАТЬ и подразумеваемая печать
Процедура PRINT вызовет Python.str () для объекта, при использовании Implied Print будет вызывать Python.repr (). Например:
IDL> arr = Python.Wrap (ИНДГЕН (5))
IDL> ПЕЧАТЬ, обр.
[0 1 2 3 4]
IDL> около
массив ([0, 1, 2, 3, 4], dtype = int16)
История версий
См. Также
! NULL, HASH, LIST, Python to IDL Bridge, передача параметров моста Python и преобразование данных
.