Использовать дескриптор (EDIT: не один декоратор) для нескольких атрибутов? - PullRequest
0 голосов
/ 10 ноября 2009

Python 2.5.4. Совершенно новый для Python, совершенно новый для декораторов с прошлой ночи. Если у меня есть класс с несколькими логическими атрибутами:

class Foo(object):
    _bool1 = True
    _bool2 = True
    _bool3 = True
    #et cetera

    def __init__():
        self._bool1 = True
        self._bool2 = False
        self._bool3 = True
        #et cetera

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

Другими словами, в отличие от чего-то подобного для каждого атрибута?

def bool1():
    def get_boo1():
        return self._bool1
    def set_bool1(self,value):
        if value <> True and value <> False:
            print "bool1 not a boolean value. exiting"
            exit()
        self._bool1=value
    return locals()
bool1 = property(**bool1())

#same thing for bool2, bool3, etc...

Я пытался написать это примерно так:

def stuff(obj):
    def boolx():
        def fget(self):
            return obj
        def fset(self, value):
            if value <> True and value <> False:
                print "Non-bool value" #name of object???
                exit()
            obj = value
        return locals()
    return property(**boolx())

bool1 = stuff(_bool1)
bool2 = stuff(_bool2)
bool3 = stuff(_bool3)

что дает мне:

File "C:/PQL/PythonCode_TestCode/Tutorials/Decorators.py", line 28, in stuff
    return property(**boolx())
TypeError: 'obj' is an invalid keyword argument for this function

Есть ли какие-нибудь указатели о том, как это сделать правильно?

Спасибо

Пол

Ответы [ 2 ]

3 голосов
/ 10 ноября 2009

Вы можете попробовать использовать дескриптор :

class BooleanDescriptor(object):
    def __init__(self, attr):
        self.attr = attr

    def __get__(self, instance, owner):
      return getattr(instance, self.attr)

    def __set__(self, instance, value):
      if value in (True, False):
        return setattr(instance, self.attr, value)
      else:
        raise TypeError


class Foo(object):
    _bar = False
    bar = BooleanDescriptor('_bar')

EDIT:

Как отметил С.Лотт, python предпочитает Duck Typing над проверкой типов.

2 голосов
/ 10 ноября 2009

Две важные вещи.

Во-первых, атрибуты уровня класса являются общими для всех экземпляров класса. Как static в Java. Из вашего вопроса не ясно, действительно ли вы говорите об атрибутах уровня класса.

Как правило, большая часть ОО-программирования выполняется с переменными экземпляра, как это.

class Foo(object):
    def __init__():
        self._bool1 = True
        self._bool2 = False
        self._bool3 = True
        #et cetera

Второй пункт. Мы не тратим много времени на проверку типов аргументов.

Если таинственный «кто-то» предоставит данные неверного типа, наш класс потерпит крах, и это в значительной степени лучший результат.

Суета с проверкой типа и домена - это большая работа, чтобы ваш класс потерпел крах в другом месте. В конечном счете, исключение (TypeError) остается тем же, поэтому дополнительная проверка имеет мало практического значения.

Действительно, дополнительная проверка домена может (и часто делает) иметь неприятные последствия, когда кто-то создает альтернативную реализацию bool, и ваш класс отклоняет этот совершенно допустимый класс, который имеет все те же функции, что и встроенный bool.

Не путайте проверку диапазона ввода человеком с проверкой типа Python. Вводимый человеком (или материал, который вы читаете из файлов или URI) должен быть проверен диапазон , но не проверен тип. Часть приложения, которая выполняет чтение внешних данных , определяет тип. Не нужно проверять тип. Таких загадок не будет.

Сценарий «что если я использую неправильный тип и моя программа работает, но не работает», на самом деле не имеет никакого смысла. Во-первых, найдите два типа, которые ведут себя одинаково, но дают слегка разных результатов. Единственный пример - int против float, и единственное время, которое действительно имеет значение, касается деления, и об этом позаботятся два оператора деления.

Если вы «случайно» используете строку, где требовалось число, ваша программа умрет. Достоверно. Последовательно.

...