@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의 인수는 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.setterx.deleter장식 만들어? 혼란 스러워요.



3
property__init__()물론 객체를 만들 때 메서드를 호출하지만 실제로는 함수가 아닌 클래스 입니다. help(property)터미널에서 사용 하는 것은 통찰력이 있습니다. help어떤 이유로 수업입니다.
Brōtsyorfuzthrāx

이 링크가 좋은 예라고 생각합니다. [property] ( journaldev.com/14893/python-property-decorator )
Sheng Bi

4
@Shule 2 살짜리 실이지만 여전히 : 모든 것이 클래스입니다. 심지어 수업.
Artemis는 여전히

2
이것은 나에게도 혼란 스러웠다. 나는 마침내 나를 위해 그것을 분해 할 수있는 기사를 발견했다. 나는 이것이 다른 누군가를 돕기를 바랍니다. programiz.com/python-programming/property 사이트와 관련이 없습니다.
jjwdesign 2012 년

답변:


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 함수가 장식 된 메소드로 대체되었습니다.

다음 순서는 또한 데코레이터 메소드를 사용하여 완전한 특성을 작성합니다.

먼저 propertygetter를 사용하여 함수와 객체를 만듭니다 .

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

Descriptor 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
Method 오브젝트는 즉석에서 생성되고 있다 가능하면 동일한 메모리 위치를 재사용.
Martijn Pieters

1
@ MarkusMeskanen : type()Dunder 속성에 액세스 하는 대신 차라리 표준 함수와 연산자에 의해 확장 점으로 사용됩니다.
Martijn Pieters

2
@MarkusMeskanen : 객체가 불변이기 때문에 그 객체를 변경하면 서브 클래스로 전문화 할 수 없습니다.
Martijn Pieters

5
@MarkusMeskanen : setter없이 파이썬이 getter를 재정의하는 것을보십시오 ; 경우 @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수업에서 데코레이터로 사용 된 이유를 파악하는 데 실제로 유용했습니다 .
ijoseph April

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
이 예제는 word () 함수 / 프로퍼티를 init에 정의해야한다면 어떻게 보일까요?
JJ

5
누군가가 내가 왜 대신 self.word = my_word같은 방식으로 작동하는 대신 여기에 장식 데코레이터를 print( Thing('ok').word ) = 'ok'
만드는지

1
@SilverSlash 이것은 간단한 예일뿐입니다. 실제 사용 사례에는 더 복잡한 방법이 필요합니다
AlexG

Thing('ok').word런타임에 인쇄 가 어떻게 함수를 내부적으로 호출 하는지 설명해 주 시겠습니까?
Vicrobot

83

첫 번째 부분은 간단합니다.

@property
def x(self): ...

와 같다

def x(self): ...
x = property(x)
  • 이는 property게터만으로 를 만드는 간단한 구문입니다 .

다음 단계는이 속성을 setter와 deleter로 확장하는 것입니다. 그리고 이것은 적절한 방법으로 발생합니다 :

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

x주어진 setter와 old의 모든 것을 상속받는 새로운 속성을 반환합니다 .

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
첫 번째 코드와 마지막 코드 예제는 동일합니다.
Adomas Baliuka

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클래스 를 변경 하고 dollarscents속성을 제거 하기로 결정하고 대신 총 센트 양만 추적하기로 결정한다고 가정 해 봅시다 .

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'속성이 없습니다.

지금 당신의 원본에 의존하는 모든 사람 즉, 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로 많은 작업을 수행했지만 추출 기능이 추가되지 않은 것 같습니다.
Sheng Bi

1
@ShengBi : 실제 예제에 그다지 초점을 두지 말고 기본 원칙에 더 초점을 맞추십시오. 어떤 이유로 든 코드를 리팩터링해야하는 경우 다른 사람의 코드에 영향을주지 않고 그렇게 할 수 있습니다.
Cleb

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)

이 코드에서는 @ property.setter를 사용하여 실제 인스턴스 변수를 설정하는 동안 직접 트리거 하려고 할 때 어떤 트리거 어설 션 password을 사용하여 "숨겨진"속성 을 사용 했습니다 .@propertyAttributeErrorpassword_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')(궁금한 점이 있지만 Flask 양식과 다른 주제 일 경우).

이 예제가 도움이 되길 바랍니다.


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 데코레이터는 이미 정의 된 함수에 일부 추가 기능을 추가하는 데 도움이되는 함수입니다.

파이썬에서는 모든 것이 객체입니다. 파이썬의 함수는 일류 객체이므로 변수로 참조 할 수 있고 목록에 추가되고 다른 함수 등에 인수로 전달됩니다.

다음 코드 스 니펫을 고려하십시오.

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 함수를 수정하고 추가 코드 줄을 추가했다고 말할 수 있습니다.

데코레이터를위한 파이썬 문법

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

사례 시나리오보다 모든 것을 마무리했지만 그 전에는 몇 가지 원칙에 대해 이야기 해 봅시다.

게터와 세터는 많은 객체 지향 프로그래밍 언어에서 데이터 캡슐화의 원칙을 보장하기 위해 사용됩니다 (이러한 데이터를 처리하는 메소드와 데이터를 묶는 것으로 나타납니다).

이 메소드는 물론 데이터를 검색하기위한 getter와 데이터를 변경하기위한 setter입니다.

이 원칙에 따라 클래스의 속성은 다른 코드로부터 숨기거나 보호하기 위해 비공개로 설정됩니다.

예, @property 는 기본적으로 게터와 세터를 사용 하는 pythonic 방법입니다.

파이썬은 속성이라는 훌륭한 개념을 가지고있어 객체 ​​지향 프로그래머의 삶을 훨씬 단순하게 만듭니다.

온도를 섭씨로 저장할 수있는 클래스를 만들기로 결정했다고 가정 해 봅시다.

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

리팩토링 된 코드, 다음은 속성으로이를 달성 할 수있는 방법입니다.

파이썬에서, 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_temperature는 메서드 대신 속성으로 유지됩니다.

이제 글로 온도 값에 액세스 할 수 있습니다.

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

get_temperatureset_temperature 는 불필요하고 클래스 네임 스페이스를 오염 시키기 때문에 계속해서 이름을 정의 할 수 없습니다 .

위의 문제를 처리 하는 pythonic 방법@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. setter로 기능해야하는 방법은 "@ temperature.setter"로 장식되어 있습니다. 함수가 "x"라고하면 "@ x.setter"로 장식해야합니다.
  3. 동일한 이름과 다른 수의 매개 변수 "def temperature (self)"및 "def temperature (self, x)"를 사용하여 "two"메서드를 작성했습니다.

보시다시피 코드는 덜 우아합니다.

이제 실생활에 관한 실제 장면에 대해 이야기 해 봅시다.

다음과 같이 클래스를 설계했다고 가정 해 봅시다.

class OurClass:

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


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

이제 우리 수업이 클라이언트들 사이에서 인기를 얻었고 프로그램에서 사용하기 시작했다고 가정합시다. 그들은 객체에 모든 종류의 과제를 수행했습니다.

그리고 운명적인 날, 신뢰할 수있는 고객이 우리에게 와서 "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
섭씨 클래스는 설정시 무한 재귀가됩니다 (인스턴스화시 의미).
Ted Petrou

1
@ 테드 Petrou 나는 당신을 얻지 못했습니까? 설정할 때 어떻게 무한히 재귀합니까?
Divyanshu Rawat

이것은 실제로 명확하지 않다 ... 사람들은 왜 묻고 있지만, 그 예는 설득력이 없다 ...
Sheng Bi

1
그것은 단지 개인적인 의견입니다. 당신의 대답은 정말 좋을 것입니다. 그러니 맡겨
Sheng Bi

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__거기가 될 오버라이드 (override) . 이것은 파이썬에서 디스크립터 패턴의 구현입니다.

일반적으로 디스크립터는 "바인딩 동작"이있는 객체 속성으로, 디스크립터 프로토콜의 메소드가 속성 액세스를 대체했습니다.

우리는 또한 속성을 사용할 수 있습니다 setter, getter그리고 deleter방법은 속성에 기능을 결합 할 수 있습니다. 다음 예를 확인하십시오. s2클래스 의 메소드 C는 속성을 double로 설정합니다 .

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, setter 메소드를 작성하고이를 속성 함수에 인수로 전달
  • @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

기본적으로 x 대신 x를 사용한다는 점을 제외하고 C (object) 예제와 동일 합니다. 또한 __init에서 초기화하지 않습니다. -...로 . 그러나 __x가 부분으로 정의되어 있기 때문에 제거 할 수 있습니다. 수업의 ....

출력은 다음과 같습니다.

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

init 에서 self.x = 1234를 주석 처리 하면 출력은 다음과 같습니다.

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

그리고 getter 함수에서 _default = None을 _default = 0으로 설정하면 (모든 getter가 기본값을 가져야하지만 내가 본 것의 속성 값에 의해 전달되지 않으므로 여기에서 정의 할 수 있습니다. 기본값을 한 번 정의하고 어디에서나 사용할 수 있기 때문에 실제로 나쁘지는 않습니다. 예 : def x (self, _default = 0) :

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

참고 : 게터 로직은 값을 조작하기 위해 값을 조작해야합니다. 인쇄 문과 동일합니다.

참고 : Lua에 익숙하고 단일 함수를 호출 할 때 동적으로 10 개 이상의 도우미를 만들 수 있으며 속성을 사용하지 않고 Python과 비슷한 것을 만들었지 만 어느 정도 작동하지만 이전에는 함수가 생성되었지만 사용되는 경우 생성되기 전에 호출되는 문제가 여전히 있습니다.이 방식은 코딩되지 않았기 때문에 이상합니다 ... 나는 Lua 메타 테이블의 유연성과 실제 세터 / 게터를 사용할 수 있다는 사실을 선호합니다. 본질적으로 변수에 직접 액세스하는 대신 ...하지만 gui 프로그램과 같이 Python으로 몇 가지를 얼마나 빨리 만들 수 있는지 좋아합니다. 많은 추가 라이브러리가 없으면 내가 설계하고있는 것이 가능하지 않을 수도 있습니다 .AutoHotkey에서 코드를 작성하면 필요한 dll 호출에 직접 액세스 할 수 있으며 Java, C #, C ++,

참고 :이 포럼의 코드 출력이 손상되었습니다-코드의 첫 번째 부분에 공백을 추가해야했습니다. 복사 / 붙여 넣기를하면 모든 공백을 탭으로 변환해야합니다. 10,000 줄의 파일 크기는 파일 크기가 크게 다르고 처리 시간이 단축되는 공간이있는 512KB ~ 1MB, 탭이있는 100 ~ 200KB 일 수 있습니다 ...

탭은 사용자별로 조정할 수도 있습니다. 따라서 너비 2, 4, 8 또는 원하는 것을 원하는 경우 시력 부족이있는 개발자에게 적합합니다.

참고 : 포럼 소프트웨어의 버그로 인해 클래스에 정의 된 모든 함수가 제대로 들여 쓰기되지 않습니다. 복사 / 붙여 넣기시 들여 쓰기


-3

한 가지 언급 : Python 2.x의 @property경우 상속하지 않았을 때 광고 된대로 작동하지 않았습니다 object.

class A():
    pass

그러나 다음과 같은 경우에 효과가있었습니다.

class A(object):
    pass

Python 3의 경우 항상 작동했습니다.


5
파이썬 2에서 상속되지 않는 클래스 object는 구식 클래스이고 구식 클래스는 디스크립터 프로토콜을 지원하지 않기 때문입니다 (이것이 property작동하는 방식으로 작동합니다). 파이썬 3에서는 구식 클래스가 더 이상 존재하지 않습니다. 모든 클래스는 파이썬 2에서 새로운 스타일의 클래스라고 불렀습니다.
chepner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.