Что такое stati c тип личности? - PullRequest
0 голосов
/ 10 июля 2020

Я хочу, чтобы параметр метода имел тот же тип, что и класс, для которого он вызывается (см. В конце для примера). Пытаясь сделать это, я столкнулся с таким поведением, которое я изо всех сил пытаюсь понять.

Следующее не вводит проверку

class A:
    def foo(self) -> None:
        pass

A.foo(1)

с

ошибка: аргумент 1 для «foo» из «A» имеет несовместимый тип «int»; ожидал "A"

, как я и ожидал, поскольку я думал, что A.foo должен принимать только A. Однако, если я добавляю собственный тип

from typing import TypeVar

Self = TypeVar("Self")

class A:
    def foo(self: Self) -> None:
        pass

A.foo(1)

, он выполняет проверку типа. Я ожидал, что это не удастся, сказав мне, что мне нужно сдать A, а не int. Это наводит на мысль, что средство проверки типов обычно выводит тип A для self, а добавление типа Self переопределяет это, я полагаю, на object. Это соответствует ошибке

from typing import TypeVar

Self = TypeVar("Self")

class A:
    def bar(self) -> int:
        return 0

    def foo(self: Self) -> None:
        self.bar()

error: «Self» не имеет атрибута «bar»

, который я могу исправить, если привязать как Self = TypeVar("Self", bound='A')

Я прав, что это означает, что self не ограничен, например, так же, как я ожидал бы, что this будет ограничено в Scala?

Я думаю, это только влияет если я укажу тип self как что угодно, кроме класса, для которого он определен намеренно или иначе. Мне также интересно узнать, какое влияние оказывает переопределение self как другого типа, и действительно ли это имеет смысл с тем, как Python разрешает и вызывает методы.

Контекст

Я хочу делать такие вещи, как

class A:
    def foo(self: Self, bar: List[Self]) -> Self:
        ...

, но ожидал, что Self будет ограничено A, и был удивлен, что это не так.

Ответы [ 2 ]

1 голос
/ 14 июля 2020

Если вы опустите подсказку типа для self, средство проверки типов автоматически предположит, что он имеет любой тип содержащего класса.

Это означает, что:

class A:
    def foo(self) -> None: pass

... эквивалентно выполнению:

class A:
    def foo(self: A) -> None: pass

Если вы хотите, чтобы self был чем-то другим, вы должны установить подсказку пользовательского типа.

Относительно этого фрагмента кода:

from typing import TypeVar

Self = TypeVar("Self")

class A:
    def foo(self: Self) -> None:
        pass

A.foo(1)

Использование TypeVar только один раз в сигнатуре функции либо искажено, либо избыточно, в зависимости от вашей точки зрения.

Но это не имеет отношения к основная направленность вашего вопроса. Мы можем исправить ваш фрагмент кода, вместо этого выполнив:

from typing import TypeVar

Self = TypeVar("Self")

class A:
    def foo(self: Self) -> Self:
        return self

A.foo(1)

... который демонстрирует то же поведение, которое вы заметили.

Но независимо от того, на какой из двух фрагментов кода мы смотрим, я полагаем, что средство проверки типов действительно предположит, что self имеет тот же тип, что и верхняя граница Self, при проверке типа тела foo. В этом случае верхняя граница object, как вы и подозревали.

Мы получаем такое поведение вне зависимости от того, делаем мы что-нибудь с собой или нет. Например, мы бы получили точно такое же поведение, просто выполнив:

def foo(x: Self) -> Self:
    return x

... и так далее. С точки зрения средства проверки типов в параметре self нет ничего особенного, за исключением того, что мы устанавливаем для него тип по умолчанию, если ему не хватает подсказки типа, вместо того, чтобы просто использовать Any.

ошибка: «Self» не имеет атрибута «bar»

, который я могу исправить, если привязать как Self = TypeVar("Self", bound='A')

Я прав, что это означает, что self является не ограничен, например, так же, как я ожидал бы, что this будет ограничен в Scala?

Я не знаю, как this ограничен в Scala, но это действительно, если вы решили переопределить тип по умолчанию self, вы несете ответственность за установку своих собственных ограничений и границ в зависимости от ситуации.

Другими словами, как только TypeVar определен, его значение не изменится, когда вы попытаетесь использовать его в определении функции. Это правило для TypeVars / функций в целом. И поскольку в основном self нет ничего особенного, то же правило применяется и там.

(Хотя средства проверки типов, такие как mypy, также попытаются выполнить некоторые базовые c проверки работоспособности любых ограничений, которые вы в конечном итоге выберете убедитесь, что вы не получите метод, который невозможно вызвать или что-то еще. Например, он пожалуется, если вы попытаетесь установить границу от Self до int.)

Обратите внимание, что выполнение таких действий, как:

from typing import TypeVar, List

Self = TypeVar('Self', bound='A')

class A:
    def foo(self: Self, bar: List[Self]) -> Self:
        ...

class B(A): pass

x = A().foo([A(), A()])
y = B().foo([B(), B()])

reveal_type(x)  # Revealed type is 'A'
reveal_type(y)  # Revealed type is 'B'

..., явно поддерживается PEP 484 . В документации mypy также есть несколько примеров .

1 голос
/ 10 июля 2020

Две вещи:

self только наполовину маг c.

Аргумент self имеет магическое свойство, которое, если вы вызываете атрибут объекта как функция, и эта функция имеет self в качестве первого аргумента, тогда сам объект будет добавлен к явным аргументам как self.

Я думаю, любой хороший анализатор stati c будет воспринимать как подразумевается, что self имеет рассматриваемый класс в качестве своего типа, что вы видите в своем первом примере.

TypeVar - для полиморфизма.

И я думаю, что это то, что ты пытаешься сделать? В третьем примере Self может быть любого типа, в зависимости от контекста . В контексте A.foo(1), Self равно int, поэтому self.bar() завершается ошибкой.

Может быть возможно написать метод экземпляра , который может вызываться как stati c метод против нечленов класса с ограничениями типа c параметров, но, вероятно, это не лучшая идея для любого приложения в дикой природе. Просто назовите переменную как-нибудь иначе и объявите метод как stati c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...