Несколько путаниц о декораторах python при просмотре примеров онлайн [duplicate]

Пример Отлично работает, НО нужно следующее, иначе вы получите err 5 (Незаконная процедура)

Const ForReading = 1, ForWriting = 2, ForAppending = 8
700
задан Martijn Pieters 16 May 2017 в 09:04
поделиться

10 ответов

Функция property() возвращает специальный объект дескриптора :

>>> property()
<property object at 0x10ff07940>

Этот метод имеет дополнительные методы:

]
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

Они также действуют как декораторы . Они возвращают новый объект свойства:

>>> property().getter(None)
<property object at 0x10ff079f0>

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

Помните, что синтаксис @decorator просто синтаксический сахар; синтаксис:

@property
def foo(self): return self._foo

действительно означает то же, что и

def foo(self): return self._foo
foo = property(foo)

, поэтому foo функция заменяется на property(foo), что мы видели выше, это специальный объект , Затем, когда вы используете @foo.setter(), то, что вы делаете, вызывает метод property().setter, который я показал вам выше, который возвращает новую копию свойства, но на этот раз с заменой функции setter на декорированный метод.

Следующая последовательность также создает свойство full-on, используя эти методы декоратора.

Сначала мы создаем некоторые функции и объект property только с помощью getter:

>>> def getter(self): print 'Get!'
... 
>>> def setter(self, value): print 'Set to {!r}!'.format(value)
... 
>>> def deleter(self): print 'Delete!'
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Далее мы используем метод .setter() для добавления сеттера:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

В последний раз мы добавили дебетер с методом .deleter():

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Последнее, но не более того, объект property действует как объект дескриптора , поэтому он имеет .__get__() , .__set__() и .__delete__() , чтобы подключиться к атрибуту получения, установки и удаления атрибута экземпляра:

>>> class Foo(object): pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

В дескрипторе Howto реализована реализация образца чистого python образца типа property() :

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = 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 getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)
745
ответ дан Martijn Pieters 15 August 2018 в 21:15
поделиться
  • 1
    Отлично. Вы могли бы добавить тот факт, что после Foo.prop = prop вы можете сделать Foo().prop = 5; pront Foo().prop; del Foo().prop с желаемым результатом. – glglgl 27 June 2013 в 08:25
  • 2
    Объекты метода создаются «на лету», а can используют одно и то же место памяти, если оно доступно. – Martijn Pieters♦ 12 June 2014 в 20:15
  • 3
    @MarkusMeskanen: Я предпочитаю использовать type(), поскольку доступ к атрибутам dunder и методам предназначен для использования в качестве точек расширения стандартными функциями и операторами. – Martijn Pieters♦ 10 March 2015 в 22:46
  • 4
    @MarkusMeskanen: потому что объект неизменен, и если вы его мутировали, вы не могли бы специализировать его в подклассе. – Martijn Pieters♦ 13 June 2015 в 14:25
  • 5
    @MarkusMeskanen: см. Python, переопределяющий геттер без setter ; если @human.name.getter изменил объект property на месте вместо того, чтобы возвращать новое, атрибут human.name будет изменен, изменив поведение этого суперкласса. – Martijn Pieters♦ 13 June 2015 в 14:33

Вот еще один пример:

##
## Python Properties Example
##
class GetterSetterExample( object ):
    ## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
    __x = None


##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
    ## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
    self.x = 1234

    return None


##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
    ## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
    _value = ( self.__x, _default )[ self.__x == None ]

    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Get x = ' + str( _value ) )

    ## Return the value - we are a getter afterall...
    return _value


##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Set x = ' + str( _value ) )

    ## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
    if ( _value > 0 ):
        self.__x = -_value
    else:
        self.__x = _value


##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
    ## Unload the assignment / data for x
    if ( self.__x != None ):
        del self.__x


##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
    ## Output the x property data...
    print( '[ x ] ' + str( self.x ) )


    ## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
    return '\n'

##
##
##
_test = GetterSetterExample( )
print( _test )

## For some reason the deleter isn't being called...
del _test.x

В принципе, то же, что и пример C (object), за исключением того, что я использую x вместо ... Я также не инициализирую __init - .. ну, но это можно удалить, потому что __x определен как часть класса ....

Выход:

[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234

, и если я комментирую out self.x = 1234 в init, тогда выход будет:

[ Test Class ] Get x = None
[ x ] None

, и если я установил _default = None в _default = 0 в функции getter (поскольку все получатели должны иметь значение по умолчанию, но он не передается значениями свойств из того, что я видел, поэтому вы можете определить его здесь, и это на самом деле не плохо, потому что вы можете определить значение по умолчанию один раз и использовать его везде) ie: def x (self, _default = 0):

[ Test Class ] Get x = 0
[ x ] 0

Примечание. Логика-получатель просто должна управлять этим значением, чтобы гарантировать, что она управляется им - то же самое для операторов печати ...

Примечание. Я привык к Lua и могу динамически создавать 10+ помощников, когда я вызываю одну функцию, и я сделал что-то подобное для Python без использования свойств и работает до некоторой степени, но, несмотря на то, что функции создаются до их использования, по-прежнему возникают проблемы с тем, что они вызываются до их создания, что является странным, поскольку оно не кодируется таким образом. .. Я предпочитаю гибкость мета-таблиц Lua и факт, что я могу использовать фактические сеттеры / получатели вместо того, чтобы напрямую обращаться к переменной ... Мне нравится, как быстро некоторые вещи могут быть созданы с помощью Python, хотя, например, gui-программ. хотя я проектирую, может быть невозможен без большого количества дополнительных библиотек - если я его закодирую в AutoHotkey, я могу напрямую обращаться к нужным вызовам DLL, и то же самое можно сделать в Java, C #, C ++ и т. д. - возможно, я пока не нашли правильной вещи, но для этого проекта я могу переключиться с Python ..

Примечание: выход кода на этом форуме нарушен - мне пришлось добавить пробелы в первую часть кода для он работает - когда копирование / вставка гарантируют, что вы конвертируете все пробелы в вкладки .... Я использую вкладки для Python, потому что в файле, который составляет 10 000 строк, размер файла может быть от 512 КБ до 1 МБ с пробелами и от 100 до 200 КБ с вкладками, которые приравниваются к огромная разница в размере файла и сокращение времени обработки ...

Вкладки также могут быть скорректированы для каждого пользователя - поэтому, если вы предпочитаете ширину 2 пробела, 4, 8 или все, что вы можете сделать, это означает, что это вдумчивый для разработчиков с недостатками зрения.

Примечание. Все функции, определенные в классе, не имеют отступов должным образом из-за ошибки на форуме oftware - убедитесь, что вы вставляете его, если вы копируете / вставляете

0
ответ дан Acecool 15 August 2018 в 21:15
поделиться

Ниже приведен минимальный пример реализации @property:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

В противном случае word остается вместо свойства.

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'
50
ответ дан AlexG 15 August 2018 в 21:15
поделиться
  • 1
    Как будет выглядеть этот пример, если функция / свойство word () необходимо определить в init ? – J.J 9 April 2017 в 09:51
  • 2
    Может кто-нибудь объяснить, почему я создам декоратор свойств здесь, вместо того, чтобы просто иметь self.word = my_word - который тогда работал бы таким же образом print( Thing('ok').word ) = 'ok' – SilverSlash 9 June 2017 в 04:41
  • 3
    @SilverSlash Это просто простой пример, реальный случай использования будет включать более сложный метод – AlexG 9 June 2017 в 16:22
  • 4
    Отлично. Читая другие ответы и проходя через ваш пример, я, наконец, понимаю это. Спасибо за это ! – Arun Das 12 June 2017 в 23:40
  • 5
    Лучшая иллюстрация, которую я когда-либо читал. – JawSaw 5 November 2017 в 05:43

Это следующее:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

То же, что и:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

То же, что и:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

То же, что и :

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

Это то же самое, что:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x
25
ответ дан Bill Moore 15 August 2018 в 21:15
поделиться

Первая часть проста:

@property
def x(self): ...

совпадает с

def x(self): ...
x = property(x)
  • , которая, в свою очередь, является упрощенным синтаксисом для создания property с помощью только геттера.

Следующим шагом будет расширение этого свойства с помощью setter и deleter. И это происходит с помощью соответствующих методов:

@x.setter
def x(self, value): ...

возвращает новое свойство, которое наследует все, начиная с старого x и заданного установщика.

x.deleter работает одинаково .

62
ответ дан glglgl 15 August 2018 в 21:15
поделиться

Я прочитал все сообщения здесь и понял, что нам может понадобиться пример реальной жизни. Почему на самом деле у нас есть @property? Итак, рассмотрите приложение Flask, в котором вы используете систему аутентификации. Вы объявляете модель User в models.py:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))

    ...

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

В этом коде у нас есть «скрытый» атрибут password с помощью @property, который запускает утверждение AttributeError при попытке доступа к нему напрямую, в то время как мы использовали @ property.setter для установки фактической переменной экземпляра password_hash.

Теперь в auth/views.py мы можем создать экземпляр пользователя с помощью:

...
@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                    username=form.username.data,
                    password=form.password.data)
        db.session.add(user)
        db.session.commit()
...

Атрибут уведомления password, который поступает из регистрационной формы, когда пользователь заполняет форму. [3]

Надеюсь, что этот пример будет полезен

5
ответ дан Leo Skhrnkv 15 August 2018 в 21:15
поделиться

Свойство может быть объявлено двумя способами.

  • Создание методов getter, setter для атрибута, а затем передача их как аргумент функции свойства
  • Использование @ Свойство decorator.

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

0
ответ дан nvd 15 August 2018 в 21:15
поделиться

Документация говорит, что это просто ярлык для создания свойств readonly. Таким образом,

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

эквивалентно

def getx(self):
    return self._x
x = property(getx)
103
ответ дан λuser 15 August 2018 в 21:15
поделиться
2
ответ дан Cleb 29 October 2018 в 04:19
поделиться
1
ответ дан Divyanshu Rawat 29 October 2018 в 04:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: