& Ldquo; Уплотненный & Rdquo; статический тип подсказки в классе Python [дубликат]

Ответы

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.

  1. Подробнее о лямбда-грамматической информации можно найти в рабочем проекте для языка программирования C ++ # 337, 2012-01-16, 5.1.2. Лямбда-выражения, стр.88
  2. В C ++ 14 добавлена ​​дополнительная функция, названная «захват init». Он позволяет выполнять произвольное объявление членов данных закрытия:
    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
    
96
задан Daenyth 31 December 2015 в 21:11
поделиться

3 ответа

Если вы попытаетесь запустить этот код, вы получите:

NameError: name 'Position' is not defined

Это связано с тем, что Position необходимо определить, прежде чем вы сможете использовать его в аннотации. «Благословенный путь» и торговля; заключается в использовании строки, если вы используете Python & lt; 3.7 или импортируете модуль аннотаций из future, если вы уже используете Python 3.7 (запущен в июне 2018 года), но я также рассмотрю обходные пути, предложенные для подобных вопросов.

Python 3.7+: 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.

Python & lt; 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, и он будет работать.

126
ответ дан Paulo Scardine 15 August 2018 в 19:36
поделиться
  • 1
    Правильно, это меньше проблема PyCharm и проблема Python 3.5 PEP 484. Я подозреваю, что вы получите такое же предупреждение, если запустите его через инструмент типа mypy. – Paul Everitt 5 November 2015 в 14:36

Название «Позиция» не может быть доступно в то время, когда само тело класса анализируется. Я не знаю, как вы используете объявления типа, но 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 ничего не делает из этих аннотаций - они обычно предназначены для анализа статического кода или могут иметь библиотеку / фреймворк для проверки типов во время выполнения - но вы должны явно установить это)

9
ответ дан jsbueno 15 August 2018 в 19:36
поделиться

Указание типа как строки в порядке, но всегда немного меня решает, что мы в основном обходим парсер. Поэтому вам лучше не пропустить ни одну из этих литералов:

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)
1
ответ дан vbraun 15 August 2018 в 19:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: