Як працює декоратор @property?


980

Я хотів би зрозуміти, як працює вбудована функція 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декоратори створили? Я збентежений.



3
propertyнасправді є класом (а не функцією), хоча, ймовірно, він викликає __init__()метод, коли ви створюєте об'єкт, звичайно. Використання help(property)з терміналу проникливе. helpтакож клас з певних причин.
Brōtsyorfuzthrāx

Я думаю, що це посилання дає хороший приклад: [властивість] ( journaldev.com/14893/python-property-decorator )
Sheng Bi

4
@Shule 2-річна нитка, але все-таки: Все - клас. Навіть заняття.
Артеміда досі не вірить SE

2
Це мене також бентежило. Нарешті я знайшов статтю, яка змогла розбити її для мене. Я сподіваюся, що це допомагає комусь іншому. programiz.com/python-programming/property Я жодним чином не пов’язаний із сайтом.
jjwdesign

Відповіді:


1008

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 замінена декорованим методом.

Наступна послідовність також створює властивість повного використання, використовуючи ці методи декоратора.

Спочатку ми створюємо деякі функції та 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__)

10
Дуже добре. Ви можете додати той факт, що після того, як Foo.prop = propви зможете зробити Foo().prop = 5; pront Foo().prop; del Foo().propбажаний результат.
glglgl

12
Об'єкти методу створюються з льоту і можуть використовувати повторно те саме місце пам'яті, якщо є.
Martijn Pieters

1
@MarkusMeskanen: Я скоріше використовую type()в якості доступу до атрибутів і методів, які використовуються як стандартні функції та оператори.
Martijn Pieters

2
@MarkusMeskanen: оскільки об’єкт незмінний, і якщо ви його мутували на місці, ви не могли спеціалізувати його на підкласі.
Мартійн Пітерс

5
@MarkusMeskanen: див. Переважний геттер Python без сеттера ; якщо @human.name.getterзмінити propertyоб'єкт на місці, а не повернути новий, human.nameатрибут буде змінено, змінивши поведінку цього суперкласу.
Martijn Pieters

201

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

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

еквівалентно

def getx(self):
    return self._x
x = property(getx)

19
Повний контекст (відповідь, що найбільше відповідає) - це добре, але ця відповідь була практично корисною для з'ясування того, чому хтось інший використовував @propertyяк декоратор у своєму класі.
ійосеф

1
@property можна також використовувати, коли ви хочете додати атрибут до класу, і вам потрібно підтримувати сумісність з раніше створеними об'єктами цього класу (наприклад, які можуть бути збережені у файлі підбору).
AndyP

111

Ось мінімальний приклад того, як @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'

1
Як виглядає цей приклад, якщо слово /) функцію / властивість потрібно визначити в 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)
  • що, в свою чергу, є спрощеним синтаксисом для створення a propertyз просто getter.

Наступним кроком буде розширення цієї властивості за допомогою сетера та делетера. І це відбувається за допомогою відповідних методів:

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

повертає нову властивість, яка успадковує все від старого xплюс заданий сеттер.

x.deleter працює так само.


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
Перший і останній приклади коду однакові (дослівно).
Адомас Балюка

47

Нижче - ще один приклад того, як @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клас і позбутися від dollarsі 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і в абстрактних класах; Я наводжу тут мінімальний приклад .


ваше резюме дуже добре, приклад, який приймає веб-сайт, є дещо дивним. Початківець запитає ... чому ми не можемо просто дотримуватися 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. Так що ви можете використовувати свою функцію так само, як змінну.


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

Тут ми можемо сказати, що функція декоратора змінила нашу функцію 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()

Давайте зробимо висновок про все, ніж зі сценарієм випадку, але перед цим поговоримо про деякі принципи ой.

Геттери і сетери використовуються в багатьох об'єктно-орієнтованих мовах програмування для забезпечення принципу інкапсуляції даних (розглядається як поєднання даних із методами, що працюють на цих даних.)

Ці методи, звичайно, є геттером для отримання даних і сеттером для зміни даних.

Відповідно до цього принципу, атрибути класу робляться приватними, щоб приховати та захистити їх від іншого коду.

Так, @ власність - це в основному пітонічний спосіб використання геттерів та сеттерів.

У 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 властивість () - це вбудована функція, яка створює та повертає об’єкт властивості.

Об'єкт властивості має три методи: 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_temperature залишається властивістю замість методу.

Тепер ви можете отримати доступ до значення температури, написавши.

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

Ми можемо продовжувати і не визначати імена get_temperature та set_temperature, оскільки вони непотрібні та забруднюють простір імен класів.

Віщий спосіб впоратися з вказаною вище проблеми є використання @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 temperature (self)" та "def temperature (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
@Ted Petrou Я тебе не зрозумів? Як це буде нескінченно повторюватися при встановленні?
Divyanshu Rawat

Це насправді не зрозуміло ... люди запитують чому, але приклад не переконливий ...
Шен Бі

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

Майно може бути оголошено двома способами.

  • Створюючи getter, сеттер методи для атрибута, а потім передаючи їх як аргумент функції властивості
  • Використання декоратора @property .

Ви можете подивитися кілька прикладів, які я писав про властивості в python .


Ви можете оновити свою відповідь, сказавши, що властивість - це клас, і я можу подати заяву.
prosti


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 (об'єкт), за винятком того, що я використовую 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 у функції getter (так як всі getters повинні мати значення за замовчуванням, але воно не передається значеннями властивостей від того, що я бачив, ви можете визначити його тут, і насправді це непогано, тому що ви можете визначити типовий параметр один раз і використовувати його всюди), тобто: def x (self, _default = 0):

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

Примітка. Логіка геттера є лише для того, щоб значення маніпулювало нею, щоб переконатися, що воно маніпулює нею - те ж саме для операторів друку ...

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

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

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

Примітка: Усі функції, визначені в класі, не мають відступів належним чином через помилку в програмному забезпеченні форуму - переконайтеся, що ви відступаєте від нього, якщо копіюєте / вставляєте


-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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.