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

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

16 ответов

Решение

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(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__)

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

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

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

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

Вот минимальный пример того, как @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'

Ниже приведен еще один пример того, как @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
def x(self): ...

такой же как

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

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

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

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

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

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

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

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

Декоратор 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

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

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

Я прочитал все посты здесь и понял, что нам может понадобиться реальный пример. Почему, собственно, у нас есть @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') (на случай, если вам интересно, но это другая тема, связанная с колбами).

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

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

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

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

util = UtilityMixin()
print(util.get_config)

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

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

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

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

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"

Декоратор - это функция, которая принимает функцию в качестве аргумента и возвращает закрытие. Замыкание - это набор внутренней функции и свободной переменной. Внутренняя функция закрывается над свободной переменной и поэтому называется закрытием. Свободная переменная - это переменная, которая находится вне внутренней функции и передается во внутреннюю через docorator.

Как видно из названия, декоратор украшает полученную функцию.

function decorator(undecorated_func):
    print("calling decorator func")
    inner():
       print("I am inside inner")
       return undecorated_func
    return inner

это простая функция декоратора. Он получил undecorated_func и передал его в inner() как свободную переменную, inner() напечатал "Я внутри внутреннего" и вернул undecorated_func. Когда мы звоним decorator(undecorated_func), он возвращает inner. Вот ключ, в декораторах мы называем внутреннюю функцию именем переданной функции.

   undecorated_function= decorator(undecorated_func) 

теперь внутренняя функция называется undecorated_func. Так как inner теперь называется undecorated_func, мы передали undecorated_func декоратору и вернули undecorated_func плюс напечатанное "Я внутри внутреннего". так что этот оператор печати украсил нашу "undecorated_func".

Теперь давайте определим класс с помощью декоратора свойств:

class Person:
    def __init__(self,name):
        self._name=name
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self.value):
        self._name=value

когда мы украсили name() с помощью @property(), произошло следующее:

name=property(name) # Person.__dict__ you ll see name 

первый аргумент property () - геттер. вот что произошло во втором украшении:

   name=name.setter(name) 

Как я упоминал выше, декоратор возвращает внутреннюю функцию, и мы называем внутреннюю функцию именем переданной функции.

Вот что важно знать. "имя" непреложно. в первом украшении получилось вот что:

  name=property(name)

во втором мы получили это

  name=name.setter(name)

Мы не изменяем имя obj. Во втором украшении python видит, что это объект свойства и у него уже есть геттер. Итак, python создает новый объект "name", добавляет "fget" из первого obj, а затем устанавливает "fset".

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

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

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

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

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

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

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

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

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

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

Далее я привел пример, поясняющий @property.

Рассмотрим класс с именемStudentс двумя переменными:nameи и вы хотите быть в диапазоне от 1 до 5 .

Теперь я объясню два неправильных решения и, наконец, правильное:


Код ниже неверен , потому что он не проверяет (находится в диапазоне от 1 до 5)

      class Student:
    def __init__(self, name, class_number):
        self.name = name
        self.class_number = class_number

Несмотря на проверку, это решение также неверно :

      def validate_class_number(number):
    if 1 <= number <= 5:
        return number
    else:
        raise Exception("class number should be in the range of 1 to 5")

class Student:
    def __init__(self, name, class_number):
        self.name = name
        self.class_number = validate_class_number(class_number)

Потому что валидация проверяется только в момент создания экземпляра класса и не проверяется после этого (можно изменитьclass_numberс номером вне диапазона от 1 до 5):

      student1 = Student("masoud",5)
student1.class_number = 7

Правильное решение :

      class Student:
    def __init__(self, name, class_number):
        self.name = name
        self.class_number = class_number
        
    @property
    def class_number(self):
        return self._class_number

    @class_number.setter
    def class_number(self, class_number):
        if not (1 <= class_number <= 5): raise Exception("class number should be in the range of 1 to 5")
        self._class_number = class_number

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

class A():
    pass

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

class A(object):
    pass

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

Другие вопросы по тегам