Декораторы свойств в Python и функции set - PullRequest
3 голосов
/ 09 марта 2012

У меня есть несколько вопросов по поводу следующего кода:

  1 class Test(object):
  2     def __init__(self):
  3         print "Object instance created."
  4         self._x = raw_input("Initial value of x = ")
  5         print "Initial value of x set."
  6 
  7     def Property(func):
  8         return property(**func())
  9 
 10     @Property
 11     def x():
 12         def fget(self):
 13             print 'Getting x'
 14             return self._x
 15         def fset(self, val):
 16             print 'Setting x'
 17             self._x = val
 18         def fdel(self):
 19             print 'Deleting x'
 20             del self._x
 21         doc = "A test case"
 22         return locals()
  1. Почему необходима функция Property()?
  2. Почему я не могу просто return locals(), а затемиспользовать @property непосредственно в качестве декоратора?

Когда я это делаю, я получаю сообщение о том, что x не принимает аргументов, один дан (предположительно 'self').Я знаю, что в python есть опция @x.setter, однако я вынужден регулярно использовать 2.4, поэтому для меня это не вариант.Даже тогда @x.setter все еще кажется менее элегантным, чем определение всего этого в одном блоке.

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

Ответы [ 2 ]

2 голосов
/ 09 марта 2012

Вы можете делать все это в одном блоке: не используя @property, определяя и создавая экземпляр класса, который имеет методы __get__(), __set__() и __delete__().См. Реализация дескрипторов для получения более подробной информации:

class Test(object):
    def __init__(self):
        print "Object instance created."
        self._x = raw_input("Initial value of x = ")
        print "Initial value of x set."
    class x(object):
        def __get__(self, instance, owner):
            print 'Getting x'
            return instance._x
        def __set__(self, instance, value):
            print 'Setting x'
            instance._x = value
        def __delete__(self, instance):
            print 'Deleting x'
            del instance._x
        __doc__ = "A test case"
    x = x()

property() - это ярлык для написания вышеупомянутого, а метод Property() в вашем примере класса - это ярлык для необходимости писатьфункции отдельно и передать их на property();вместо этого вы пишете функцию, которая определяет функции, а затем возвращает их, где они передаются в property().

Причина, по которой вы не можете использовать @property, заключается в том, что декораторы украшают один объект.Таким образом, вам понадобится контейнер, такой как класс, и вы можете просто написать дескриптор непосредственно в этой точке.

2 голосов
/ 09 марта 2012

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

Если используется в качестве декоратора, property преобразует функцию в геттер; если используется как функция, вы можете передать в метод получения, установки, удаления и документ.

locals() возвращает всех местных жителей, поэтому у вас будет словарь с fget, fset, fdel, doc, Property и __init__ - вызывая property взорвать, потому что было передано слишком много аргументов.

Лично мне нравится стиль @x.setter и @x.deleter, так как я не получаю ненужных имен функций в пространстве имен классов.

Если вам приходится регулярно использовать 2.4, просто бросьте свой собственный (или украдите последнюю версию 2.6, как я;):

    class property(object):
        "2.6 properties for 2.5-"    
        def __init__(self, fget=None, fset=None, fdel=None, doc=None):
            self.fget = fget
            self.fset = fset
            self.fdel = fdel
            self.__doc__ = doc or fget.__doc__
        def __call__(self, func):
            self.fget = func
            if not self.__doc__:
                self.__doc__ = fget.__doc__
        def __get__(self, obj, objtype=None):
            if obj is None:
                return self         
            if self.fget is None:
                raise AttributeError("unreadable attribute")
            return self.fget(obj)
        def __set__(self, obj, value):
            if self.fset is None:
                raise AttributeError("can't set attribute")
            self.fset(obj, value)
        def __delete__(self, obj):
            if self.fdel is None:
                raise AttributeError("can't delete attribute")
            self.fdel(obj)
        def setter(self, func):
            self.fset = func
            return self
        def deleter(self, func):
            self.fdel = func
            return self
...