Ввод статических методов, возвращающих экземпляр класса [duplicate]

Можете ли вы попробовать

// Create a reference to the file you want to download
let starsRef = storageRef.child("images/stars.jpg")

// Fetch the download URL
starsRef.downloadURL { url, error in
  if let error = error {
    // Handle any errors
  } else {
    // Get the download URL for 'images/stars.jpg'
  }
}
97
задан 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, и он будет работать.

131
ответ дан Paulo Scardine 28 August 2018 в 19:52
поделиться

Название «Позиция» не может быть доступно в то время, когда само тело класса анализируется. Я не знаю, как вы используете объявления типа, но 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 28 August 2018 в 19:52
поделиться

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

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 28 August 2018 в 19:52
поделиться
Другие вопросы по тегам:

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