Проверка нежелательного изменения типа в Python

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

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

Тем не менее, я все еще боюсь пропустить общую картину. В частности, как вы можете проверить на ошибку, когда тип переменной был случайно изменен?

Давайте сделаем очень простой пример в Python:

#! /usr/bin/env python

userid = 3
defaultname = "foo"

username = raw_input("Enter your name: ")
if username == defaultname:
    # Bug: here we meant userid...
    username = 2

# Here username can be either an int or a string
# depending on the branch taken.
import re
match_string = re.compile("oo")
if (match_string.match(username)):
        print "Match!"

Pylint, pychecker и pyflakes не предупреждают об этой проблеме.

Как Pythonic способ справиться с такого рода ошибками?

Должен ли код быть завернут в try/catch?

1 ответ

Это не даст вам проверки во время компиляции, но, как вы предложили использовать try / catch, я предполагаю, что проверки во время выполнения также будут полезны.

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

import datetime

# ------------------------------------------------------------------------------
# TypedObject
# ------------------------------------------------------------------------------
class TypedObject(object):     
    attr_types = {'id'         : int,
                  'start_time' : datetime.time,
                  'duration'   : float}

    __slots__ = attr_types.keys()

    # --------------------------------------------------------------------------
    # __setattr__
    # --------------------------------------------------------------------------
    def __setattr__(self, name, value):
        if name not in self.__slots__:
            raise AttributeError(
                "'%s' object has no attribute '%s'" 
                % (self.__class__.__name__, name))
        if type(value) is not self.attr_types[name]:
                raise TypeError(
                    "'%s' object attribute '%s' must be of type '%s'" 
                    % (self.__class__.__name__, name, 
                       self.attr_types[name].__name__))
        # call __setattr__ on parent class
        super(MyTypedObject, self).__setattr__(name, value)

Это приведет к:

>>> my_typed_object            = TypedObject()

>>> my_typed_object.id         = "XYZ"      # ERROR
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 28, in __setattr__
TypeError: 'MyTypedObject' object attribute 'id' must be of type 'int'

>>> my_typed_object.id         = 123        # OK

Вы могли бы пойти и сделать TypedObject выше, более общий, чтобы ваши классы могли наследовать от него.

Другим (возможно, лучшим) решением (указанным здесь) может быть использование черт Entought

Другие вопросы по тегам