Как работает @property декоратор?

982

Я хотел бы понять, как работает встроенная функция property. Что меня смущает, так это то, что он propertyтакже может быть использован в качестве декоратора, но он принимает аргументы только тогда, когда используется как встроенная функция, а не как декоратор.

Этот пример из документации :

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

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

propertyаргументы «S являются getx, setx, delxи строка документации.

В приведенном ниже коде propertyиспользуется в качестве декоратора. Объектом этого является xфункция, но в приведенном выше коде нет места для объектной функции в аргументах.

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

И, как являются x.setterи x.deleterдекораторы создали? Я запутался.

как он
источник
13
Смотрите также: Как работают свойства Python?
Мартин Тома
3
propertyна самом деле это класс (не функция), хотя __init__(), конечно, он вызывает метод, когда вы создаете объект. Использование help(property)из терминала является проницательным. helpэто также класс по какой-то причине.
Brōtsyorfuzthrāx
Я думаю, что эта ссылка является хорошим примером: [property] ( journaldev.com/14893/python-property-decorator )
Sheng Bi
4
@Shule 2-летняя тема, но все же: все класс. Даже классы.
Артемида до сих пор не доверяет SE
2
Это смущало меня тоже. Я наконец нашел статью, которая смогла разобрать ее для меня. Я надеюсь, что это помогает кому-то еще. programiz.com/python-programming/property Я никак не связан с сайтом.
jjwdesign

Ответы:

1013

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объект с помощью всего лишь геттера:

>>> 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: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

Дескриптор Howto включает в себя чистый Python пример реализации этого property()типа:

class Property:
    "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__)
Мартейн Питерс
источник
11
Отлично. Вы можете добавить тот факт, что после Foo.prop = propвы можете сделать Foo().prop = 5; pront Foo().prop; del Foo().propс желаемым результатом.
glglgl
12
Объекты метода создаются на лету и могут повторно использовать ту же ячейку памяти, если она доступна.
Мартин Питерс
1
@MarkusMeskanen: я скорее использую type()для доступа к атрибутам dunder, а методы предназначены для использования в качестве точек расширения стандартными функциями и операторами.
Мартин Питерс
2
@MarkusMeskanen: потому что объект является неизменным, и если вы изменили его на месте, вы не сможете специализировать его в подклассе.
Мартин Питерс
5
@MarkusMeskanen: см. Получатель переопределения Python без установщика ; если @human.name.getterизменить propertyобъект на месте, а не вернуть новый, human.nameатрибут будет изменен, изменяя поведение этого суперкласса.
Мартин Питерс
202

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

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

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

def getx(self):
    return self._x
x = property(getx)
J0HN
источник
20
Полный контекст (ответ с наибольшим количеством голосов) хорош, но этот ответ был практически полезен для выяснения, почему кто-то еще использовал его @propertyв качестве декоратора в своем классе.
Иосиф
1
@property также можно использовать, когда вы хотите добавить атрибут в класс и вам необходимо поддерживать совместимость с ранее созданными объектами этого класса (например, они могут быть сохранены в файле выбора).
AndyP
112

Вот минимальный пример того, как @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'
AlexG
источник
1
Как будет выглядеть этот пример, если функцию / свойство word () необходимо определить в init ?
JJ
5
Может кто-нибудь объяснить, почему я создал бы здесь декоратор свойств, вместо того, чтобы просто self.word = my_word- который тогда работал бы так жеprint( Thing('ok').word ) = 'ok'
SilverSlash
1
@SilverSlash Это всего лишь простой пример, реальный вариант использования будет включать более сложный метод
AlexG
Можете ли вы объяснить мне, как печать Thing('ok').wordвызывает функцию внутри во время выполнения?
Vicrobot
83

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

@property
def x(self): ...

такой же как

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

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

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

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

x.deleter работает так же.

glglgl
источник
49

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

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
Билл Мур
источник
4
Первый и последний примеры кода одинаковы (дословно).
Adomas Baliuka
48

Ниже приведен еще один пример того, как @propertyможно помочь, когда нужно провести рефакторинг кода, который взят отсюда (я только кратко излагаю его ниже):

Представьте, что вы создали такой класс Money:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

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

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

Теперь предположим, что вы решили изменить свой Moneyкласс и избавиться от атрибутов dollarsand, centsно вместо этого решили отслеживать только общее количество центов:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

Если вышеупомянутый пользователь теперь пытается запустить свою библиотеку, как и раньше

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

это приведет к ошибке

AttributeError: у объекта «Деньги» нет атрибута «доллары»

Это означает, что теперь каждый, кто полагается на ваш оригинальный Moneyкласс, должен будет изменить все строки кода, где dollarsи centsиспользуются, что может быть очень болезненным ... Итак, как этого можно избежать? Используя @property!

Вот как:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100

    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100

    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

когда мы сейчас звоним из нашей библиотеки

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

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

Также setterотлично работает:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

Вы можете использовать @propertyтакже в абстрактных классах; Я даю минимальный пример здесь .

Cleb
источник
Ваше резюме очень хорошее, пример, который использует веб-сайт, немного странный .. Начинающий может спросить ... почему мы не можем просто придерживаться self.dollar = dollars? мы так много сделали с @property, но, похоже, функции извлечения не добавляются.
Шен Би
1
@ShengBi: не сосредотачивайте столько внимания на фактическом примере, а больше на основном принципе: если - по какой-либо причине - вам придется реорганизовать код, вы можете сделать это, не затрагивая чужой код.
Клеб
21

Я прочитал все посты здесь и понял, что нам может понадобиться реальный пример из жизни. Почему, собственно, у нас есть @property? Итак, рассмотрим приложение Flask, в котором вы используете систему аутентификации. Вы объявляете модель пользователя в 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который приходит из регистрационной формы, когда пользователь заполняет форму. Подтверждение пароля происходит в интерфейсе пользователя EqualTo('password', message='Passwords must match')(в случае, если вам интересно, но это другая тема, связанная с колбами).

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

Лео Скрнкв
источник
18

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

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

Вызов функции "get_config ()" будет работать следующим образом.

util = UtilityMixin()
print(util.get_config)

Если вы заметили, я не использовал скобки "()" для вызова функции. Это основная вещь, которую я искал для @property Decorator. Так что вы можете использовать свою функцию как переменную.

Девендра Бхат
источник
1
Очень полезный момент, который помогает сжать эту абстрактную концепцию.
Info5ek
18

Начнем с Python-декораторов.

Декоратор Python - это функция, которая помогает добавить некоторые дополнительные функции к уже определенной функции.

В Python все является объектом. Функции в Python являются объектами первого класса, что означает, что на них можно ссылаться с помощью переменной, добавлять в списки, передавать в качестве аргументов другой функции и т. Д.

Рассмотрим следующий фрагмент кода.

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye
#  Given function decorated

Здесь мы можем сказать, что функция decorator изменила нашу функцию say_hello и добавила в нее несколько дополнительных строк кода.

Синтаксис Python для декоратора

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

Завершим все, чем с кейсом, но перед этим поговорим о некоторых людях.

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

Эти методы, конечно, являются средством получения данных и средством изменения данных.

Согласно этому принципу атрибуты класса делаются закрытыми, чтобы скрыть их и защитить от другого кода.

Да, @property - это в основном питонический способ использования геттеров и сеттеров.

У Python есть отличная концепция, называемая свойством, которая значительно упрощает жизнь объектно-ориентированного программиста.

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

class Celsius:
def __init__(self, temperature = 0):
    self.set_temperature(temperature)

def to_fahrenheit(self):
    return (self.get_temperature() * 1.8) + 32

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

Перефразированный код, вот как мы могли бы добиться этого с помощью собственности.

В Python property () - это встроенная функция, которая создает и возвращает объект свойства.

У объекта свойства есть три метода: getter (), setter () и delete ().

class Celsius:
def __init__(self, temperature = 0):
    self.temperature = temperature

def to_fahrenheit(self):
    return (self.temperature * 1.8) + 32

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

Вот,

temperature = property(get_temperature,set_temperature)

мог быть разбит как,

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Указать на примечание:

  • get_tength остается свойством вместо метода.

Теперь вы можете получить доступ к значению температуры, написав.

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

Кроме того, мы можем продолжить и не определять имена get_teuration и set_tempera, поскольку они не нужны и загрязняют пространство имен класса.

Вещий способ справиться с указанной выше проблемы является использование @property .

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

Указывает на Примечание -

  1. Метод, который используется для получения значения, украшен символом "@property".
  2. Метод, который должен функционировать как установщик, украшен символом «@ temperature.setter». Если бы функция называлась «x», мы должны были бы украсить ее как «@ x.setter».
  3. Мы написали «два» метода с одинаковым именем и разным количеством параметров «def температура (self)» и «def температура (self, x)».

Как видите, код определенно менее элегантен.

Теперь давайте поговорим об одном реальном практическом сценарии.

Допустим, вы разработали класс следующим образом:

class OurClass:

    def __init__(self, a):
        self.x = a


y = OurClass(10)
print(y.x)

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

И в один роковой день к нам пришел доверенный клиент и предположил, что значение «х» должно быть в диапазоне от 0 до 1000, это действительно ужасный сценарий!

Благодаря свойствам это легко: мы создаем версию свойства "x".

class OurClass:

    def __init__(self,x):
        self.x = x

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

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

Это здорово, не правда ли: вы можете начать с самой простой реализации, какой только можно себе представить, и вы можете позже перейти на версию свойства без необходимости менять интерфейс! Так что свойства - это не просто замена геттерам и сеттерам!

Вы можете проверить эту реализацию здесь

Дивьяншу Рават
источник
2
Ваш класс по Цельсию будет бесконечно рекурсировать при настройке (что означает создание экземпляра).
Тед Петру
1
@ Тед Петру, я тебя не понял? Как это будет бесконечно повторяться при настройке?
Дивьяншу Рават,
Это на самом деле не ясно ... люди спрашивают, почему, но пример не убедителен ...
Шен Би
1
Это просто комментарий, мое личное мнение. Ваш ответ может быть действительно хорошим. так что оставь это.
Шен Би
1
по сравнению с популярными ответами, этот предназначен для людей; Спасибо.
Info5ek
6

propertyэто класс за @propertyдекоратором.

Вы всегда можете проверить это:

print(property) #<class 'property'>

Я переписал пример с, help(property)чтобы показать, что @propertyсинтаксис

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

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

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

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

c = C()
c.x="a"
print(c.x)

функционально идентичен property()синтаксису:

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

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

Нет никакой разницы в том, как мы используем собственность, как вы можете видеть.

Для ответа на вопрос @propertyдекоратор реализован через propertyкласс.


Итак, вопрос в том, propertyчтобы немного объяснить класс. Эта строка:

prop = property(g,s,d)

Была инициализация. Мы можем переписать это так:

prop = property(fget=g,fset=s,fdel=d)

Смысл fget, fsetи fdel:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

Следующее изображение показывает тройки, которые мы имеем, из класса property:

введите описание изображения здесь

__get__, __set__И __delete__там быть переопределены . Это реализация шаблона дескриптора в Python.

В общем случае дескриптор - это атрибут объекта с «поведением привязки», доступ к атрибуту которого был переопределен методами в протоколе дескриптора.

Мы также можем использовать свойство setter, getterи deleterметоды , чтобы связать функцию собственности. Проверьте следующий пример. Метод s2класса Cустановит свойство в два раза .

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

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"
прости
источник
1

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

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

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

ПНВ
источник
Можете ли вы обновить свой ответ, сказав, что свойство является классом, чтобы я мог проголосовать.
Прости
1

Лучшее объяснение можно найти здесь: Python @Property Explained - Как использовать и когда? (Полные примеры) Сельва Прабхакаран | Опубликовано 5 ноября 2018

Это помогло мне понять, ПОЧЕМУ не только КАК.

https://www.machinelearningplus.com/python/python-property/

Виктор Ван
источник
0

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

##
## 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

и если я закомментирую self.x = 1234 в init, то получится :

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

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

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

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

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

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

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

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

Acecool
источник
-3

Одно замечание: для меня, для Python 2.x, @propertyне работал как рекламируется, когда я не наследовал от object:

class A():
    pass

но работал когда:

class A(object):
    pass

для Python 3 работал всегда.

Дьюла Самуэль Карли
источник
5
Это связано с тем, что в Python 2 класс, который не наследуется, objectявляется классом старого стиля, а классы старого стиля не поддерживают протокол дескриптора (который propertyреализуется так, как он работает). В Python 3 классы старого стиля больше не существуют; все классы - это то, что мы называли классами нового стиля в Python 2.
chepner