Оценивает ли Python подсказку типа прямой ссылки?

Обратите внимание, что есть случаи, когда вы определили свой собственный пользовательский класс и хотите сохранить атрибуты, тогда вы должны использовать copy.copy() или copy.deepcopy(), а не альтернативы, например, в Python 3:

import copy

class MyList(list):
    pass

lst = MyList([1,2,3])

lst.name = 'custom list'

d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}


for k,v in d.items():
    print('lst: {}'.format(k), end=', ')
    try:
        name = v.name
    except AttributeError:
        name = 'NA'
    print('name: {}'.format(name))

Выходы:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
0
задан pasta_sauce 24 March 2019 в 03:30
поделиться

1 ответ

Рассмотрим следующий код:

class Foo:
    def bar(self) -> Foo:
        return Foo()

Эта программа фактически завершится сбоем во время выполнения, если вы попытаетесь запустить ее с Python: когда интерпретатор увидит определение bar, определение Foo будет еще не закончен. Итак, поскольку Foo еще не добавлено в глобальное пространство имен, мы пока не можем использовать его в качестве подсказки типа.

Точно так же рассмотрим эту программу:

class Foo:
    def bar(self) -> Bar:
        return Bar()

class Bar:
    def foo(self) -> Foo:
        return Foo()

Это взаимозависимое определение страдает от той же проблемы: в то время как мы оцениваем Foo, Bar еще не была оценена, поэтому интерпретатор бросает исключение.


Есть три решения этой проблемы. Во-первых, вы можете создать некоторые из ваших строк подсказок типа, эффективно «объявив их вперед»:

class Foo:
    def bar(self) -> "Foo":
        return Foo()

Это удовлетворяет интерпретатору Python и не нарушает сторонние инструменты, такие как mypy: они могут просто удалить кавычки перед разбором типа. Основным недостатком является то, что этот синтаксис выглядит некрасиво и неуклюже.

Второе решение заключается в использовании синтаксиса комментариев типа:

class Foo:
    def bar(self):
        # type: () -> Foo
        return Foo()

Это имеет те же преимущества и недостатки, что и первое решение: оно удовлетворяет интерпретатору и инструментам, но выглядит хакерским и уродливым. Он также имеет дополнительное преимущество, заключающееся в том, что он поддерживает ваш код обратно совместимым с Python 2.7.

Третье решение - только Python 3.7+ - используйте директиву from __future__ import annotations:

from __future__ import annotations 

class Foo:
    def bar(self) -> Foo:
        return Foo()

Это автоматически сделает все аннотации представленными в виде строк. Таким образом, мы получаем преимущество первого решения, но без уродства.

Это поведение в конечном итоге станет стандартным в будущих версиях Python.

Также оказывается, что автоматическое создание всех строк аннотаций может привести к некоторым улучшениям производительности. Создание типов, таких как List[Dict[str, int]], может быть удивительно дорогим: они просто регулярные выражения во время выполнения и оцениваются так, как если бы они были написаны как List.__getitem__(Dict.__getitem__((str, int)).

Оценить это выражение довольно дорого: в итоге мы выполняем два вызова метода, создаем кортеж и создаем два объекта. Конечно, это не считая дополнительной работы, которая происходит в самих методах __getitem__ - и работа, которая происходит в этих методах, оказывается нетривиальной из-за необходимости.

(Короче говоря, им нужно создавать специальные объекты, которые гарантируют, что типы, подобные List[int], не могут быть использованы ненадлежащим образом во время выполнения - например, в isinstance проверках и т. П.)

0
ответ дан Michael0x2a 24 March 2019 в 03:30
поделиться
Другие вопросы по тегам:

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