Ответы
Q: Что такое лямбда-выражение в C ++ 11?
A: Под капотом это объект автогенерированного класса с оператором перегрузки () Уст. Такой объект называется замыканием и создается компилятором. Эта концепция «закрытия» находится рядом с концепцией связывания с C ++ 11. Но lambdas обычно генерируют лучший код.
В: Когда я буду использовать один?
A: Определить «простую и малую логику» и спросить компилятор выполнить генерацию из предыдущего вопроса. Вы даете компилятору некоторые выражения, которые вы хотите быть внутри оператора ().
Q: Какой класс проблемы они решают, что было невозможно до их введения?
A: Это какой-то синтаксис сахара, как перегрузка операторов вместо функций для пользовательских операций add, subrtact ... Но он сохраняет больше строк ненужного кода, чтобы обернуть 1-3 строки реальной логики для некоторых классов и т. д.! Некоторые инженеры считают, что если число строк меньше, то вероятность ошибок в нем меньше (я тоже так думаю)
Пример использования
auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);
Дополнительные сведения о лямбдах, не затрагиваемые вопросом. Игнорируйте этот раздел, если вы не заинтересованы
1. Захваченные значения. Что вы можете захватить
1.1. Вы можете ссылаться на переменную со статической продолжительностью хранения в lambdas. Все они захвачены.
1.2. Вы можете использовать лямбда для значений захвата «по значению». В этом случае захваченные вары будут скопированы в объект функции (замыкание).
[captureVar1,captureVar2](int arg1){}
1.3. Вы можете захватить ссылку. & Амп; - в этом контексте означают ссылки, а не указатели.
[&captureVar1,&captureVar2](int arg1){}
1.4. Он содержит обозначения для захвата всех нестатических варов по значению или по ссылке
[=](int arg1){} // capture all not-static vars by value
[&](int arg1){} // capture all not-static vars by reference
1.5. Он содержит обозначения для захвата всех нестатических варов по значению или путем ссылки и указания smth. Больше. Примеры: Захват всех нестатических варов по значению, но путем захвата ссылки Param2
[=,&Param2](int arg1){}
Захват всех нестатических варов по ссылке, но с помощью захвата значения Param2
[&,Param2](int arg1){}
2. Вывод типа возврата
2.1. Возвращаемый тип Lambda может быть выведен, если лямбда - это одно выражение. Или вы можете явно указать его.
[=](int arg1)->trailing_return_type{return trailing_return_type();}
Если lambda имеет более одного выражения, тогда возвращаемый тип должен быть указан с помощью возвращаемого типа возврата. Кроме того, подобный синтаксис может применяться к автофункциям и функциям-членам
3. Захваченные значения. Что вы не можете захватить
3.1. Вы можете захватывать только локальные вары, а не переменную-член объекта.
4. Свержения
4.1. lambda не является указателем на функцию и не является анонимной функцией, но может быть неявно преобразован в указатель на функцию.
p.s.
auto toFloat = [](int value) { return float(value);};
auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
Если вы попытаетесь запустить этот код, вы получите:
NameError: name 'Position' is not defined
Это связано с тем, что Position
необходимо определить, прежде чем вы сможете использовать его в аннотации. «Благословенный путь» и торговля; заключается в использовании строки, если вы используете Python & lt; 3.7 или импортируете модуль аннотаций из future
, если вы уже используете Python 3.7 (запущен в июне 2018 года), но я также рассмотрю обходные пути, предложенные для подобных вопросов.
from __future__ import annotations
Python 3.7 вводит PEP 563: отложенная оценка аннотаций . Модуль, который использует будущий оператор from __future__ import annotations
, автоматически сохранит аннотации в виде строк:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
В Python 4.0 это станет стандартным. Поскольку Python по-прежнему является динамически типизированным языком, поэтому во время выполнения проверка типа не выполняется, написание аннотаций не должно влиять на производительность, верно? Неправильно! До того, как в python 3.7 был выбран модуль ввода , один из самых медленных модулей python в ядре , поэтому, если вы import typing
, вы увидите до 7-кратного увеличения производительности при обновлении к 3.7.
Согласно PEP 484 , вы должны использовать строку вместо самого класса:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Если вы используете фреймворк Django, это может быть знакомо, поскольку модели Django также используют строки для прямых ссылок (определения внешних ключей, в которых внешняя модель self
или еще не объявлена). Это должно работать с Pycharm и другими инструментами.
Соответствующие части PEP 484 и PEP 563 , чтобы сэкономить вы путешествуете:
Прямые ссылки
Если подсказка типа содержит имена, которые еще не определены, это определение может быть выражено как строковый литерал, который будет разрешен позже .
Ситуация, в которой это происходит, обычно является определением класса контейнера, где определяемый класс встречается в сигнатуре некоторых из методов. Например, следующий код (начало простой реализации двоичного дерева) не работает:
class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right
Чтобы решить эту проблему, напишем:
class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = right
Строковый литерал должен содержать допустимое выражение Python (т. е. компиляция (lit, '', 'eval') должна быть допустимым объектом кода), и она должна оцениваться без ошибок после того, как модуль была полностью загружена. Локальное и глобальное пространство имен, в котором он оценивается, должны быть теми же пространствами имен, в которых будут оцениваться аргументы по умолчанию для одной и той же функции.
и PEP 563:
В Python 4.0 аннотации функций и переменных больше не будут оцениваться во время определения. Вместо этого строковая форма будет сохранена в соответствующем словаре
__annotations__
. Тестеры статического типа не будут видеть различий в поведении, тогда как инструменты, использующие аннотации во время выполнения, должны будут выполнить отложенную оценку....
Функцию, описанную выше, можно включить, начиная с Python 3.7, используя следующий специальный импорт:
from __future__ import annotations
Вещи, которые могут возникнуть у вас, вместо этого
A. Определите манекен
Position
Перед определением класса поместите фиктивное определение:
class Position(object): pass class Position(object): ...
Это избавится от
NameError
и даже может выглядеть нормально:>>> Position.__add__.__annotations__ {'other': __main__.Position, 'return': __main__.Position}
Но это?
>>> for k, v in Position.__add__.__annotations__.items(): ... print(k, 'is Position:', v is Position) return is Position: False other is Position: False
B. Monkey-patch для добавления аннотаций:
Возможно, вы захотите попробовать магию программирования метафайлов Python и написать декоратор, чтобы обезопасить определение класса, чтобы добавить аннотации:
class Position: ... def __add__(self, other): return self.__class__(self.x + other.x, self.y + other.y)
Декоратор должен отвечать за эквивалент этого:
Position.__add__.__annotations__['return'] = Position Position.__add__.__annotations__['other'] = Position
По крайней мере, кажется правильным:
>>> for k, v in Position.__add__.__annotations__.items(): ... print(k, 'is Position:', v is Position) return is Position: True other is Position: True
Вероятно, слишком много проблем.
Заключение
Если вы используете 3.6 или ниже, используйте строковый литерал, содержащий имя класса, в 3.7 используйте
from __future__ import annotations
, и он будет работать.
Название «Позиция» не может быть доступно в то время, когда само тело класса анализируется. Я не знаю, как вы используете объявления типа, но PEP 484 Python - это то, что большинство режимов должно использовать, если использовать эти подсказки для печати, скажем, что вы можете просто поместить имя в виде строки в этот момент:
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
Проверить https://www.python.org/dev/peps/pep-0484/#forward-references - инструменты, соответствующие этому, будут знать, чтобы развернуть имя класса оттуда и (всегда важно иметь в виду, что сам язык Python ничего не делает из этих аннотаций - они обычно предназначены для анализа статического кода или могут иметь библиотеку / фреймворк для проверки типов во время выполнения - но вы должны явно установить это)
Указание типа как строки в порядке, но всегда немного меня решает, что мы в основном обходим парсер. Поэтому вам лучше не пропустить ни одну из этих литералов:
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
Небольшое отклонение заключается в использовании связанного типаvar, по крайней мере, тогда вы должны написать строку только один раз при объявлении typevar:
from typing import TypeVar
T = TypeVar('T', bound='Position')
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: T) -> T:
return Position(self.x + other.x, self.y + other.y)