Python ООП, проверка типа для аргументов внутри методов / классов - PullRequest
0 голосов
/ 10 октября 2018

Я пытаюсь понять ООП в Python, и у меня есть проблема "непитонического мышления".Я хочу метод для моего класса, который проверяет тип аргумента и вызывает исключение, если он не соответствующего типа (например, ValueError).Самое близкое к моим желаниям это:

class Tee(object):
    def __init__(self):
        self.x = 0
    def copy(self, Q : '__main__.Tee'):
        self.x = Q.x
    def __str__(self):
        return str(self.x)

a = Tee()
b = Tee()
print(type(a))  # <class '__main__.Tee'>
print(isinstance(a, Tee))  # True
b.x = 255
a.copy(b)
print(a)        # 255
a.copy('abc')   # Traceback (most recent call last): [...]
                # AttributeError: 'str' object has no attribute 'x'

Итак, даже когда я пытался обеспечить тип аргумента Q в моем методе copy того же класса,интерпретатор просто проходит через него и вызывает AttributeError, когда пытается получить x член из строки.

Я понимаю, что мог бы сделать что-то вроде этого:

[...]
    def copy(self, Q):
        if isinstance(Q, Tee):
            self.x = Q.x
        else:
            raise ValueError("Trying to copy from a non-Tee object")
[...]
a = Tee()
a.copy('abc')   # Traceback (most recent call last): [...]
                # ValueError: Trying to copy from a non-Tee object

Это звучит как большая работа для реализации повсюду вокруг классов, даже если я создаю отдельную функцию, метод или декоратор.Итак, мой вопрос: есть ли более "питонический" подход к этому ?

Кстати, я использую Python 3.6.5.

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

Итак, мой вопрос: есть ли более «питонический» подход к этому?

Да: четко задокументируйте, какой API ожидается от объекта Q (в данном случае: он должен иметь атрибут x int) и называть его днем.

Дело в том, что независимо от того, «проверяете» ли вы тип аргумента или нет, ошибка произойдет во время выполнения, так что практическая проверка типов POV или нет не будет иметь большого значения - но это предотвратит передачу«совместимый» объект без уважительной причины.

Кроме того, поскольку Tee.x является общедоступным, его можно установить на что угодно в любой точке кода, и этот на самом деле гораздо более важен, так как может сломаться при совершенно не связаннойместами, что делает ошибку намного более трудной для отслеживания и устранения, поэтому, если вы действительно настаиваете на том, чтобы быть оборонительным (что может или не имеет смысла в зависимости от контекста), это то, на чем вы должны сосредоточиться.

class Tee(object):
    def __init__(self):
        self.x = 0

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        # this will raise if value cannot be 
        # used to build an int
        self._x = int(value)


    def copy(self, Q : '__main__.Tee'):
        # we don't care what `Q` is as long
        # as it has an `x` attribute that can
        # be used for our purpose
        self.x = Q.x

    def __str__(self):
        return str(self.x)

Это 1 / предотвратит невозможность использования Tee.x и 2 / остановит точную точку, где передается недопустимое значение, что делает ошибку очевидной и легко исправляемой путем проверки трассировки.

Обратите внимание, что здесь важно сказать, что проверка типов полностью и определенно бесполезна, но (по крайней мере, в Python) вы должны использовать ее только тогда и там, где это действительно имеет смысл для контекста.Я знаю, что это может показаться странным, когда вы купили идею о том, что «статическая типизация хороша, потому что она предотвращает ошибки» (уже здесь, сделали это ...), но на самом деле ошибки типа довольно редки (по сравнению с логическими ошибками) и чаще всего быстропятнистый.Правда о статической типизации заключается в том, что здесь не для того, чтобы помочь разработчику написать лучший код, а чтобы помочь компилятору оптимизировать код - что является ценной целью, но совершенно другой.

0 голосов
/ 10 октября 2018

Тип аннотации не применяются во время выполнения.Период.В настоящее время они используются только в средах разработки или статических анализаторах, таких как mypy , или в любом написанном вами коде, который анализирует эти аннотации.Но так как Python в значительной степени основан на утке, набирающей , среда выполнения не будет и фактически не применяет типы.

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

assert isinstance(Q, Tee), f'Expected instance of Tee, got {type(Q)}'

Но они также в основном для отладки, поскольку утверждения можно отключить.Чтобы иметь строгие утверждения типа , вам нужно быть явным:

if not isinstance(Q, Tee):
    raise TypeError(f'Expected instance of Tee, got {type(Q)}')

Но, опять же, это предотвращает утку, что не всегда желательно.

Кстати,ваш тип аннотации должен быть просто def copy(self, Q: 'Tee'), не включать '__main__';также см. https://docs.python.org/3/whatsnew/3.7.html#whatsnew37-pep563.

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