게터와 세터를 사용하는 파이썬적인 방법은 무엇입니까?


341

나는 다음과 같이하고있다 :

def set_property(property,value):  
def get_property(property):  

또는

object.property = value  
value = object.property

저는 Python을 처음 사용하므로 여전히 구문을 탐색하고 있으며이 작업에 대한 조언을 원합니다.

답변:


684

이것을 시도하십시오 : 파이썬 속성

샘플 코드는 다음과 같습니다.

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

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

    @x.setter
    def x(self, value):
        print("setter of x called")
        self._x = value

    @x.deleter
    def x(self):
        print("deleter of x called")
        del self._x


c = C()
c.x = 'foo'  # setter called
foo = c.x    # getter called
del c.x      # deleter called

2
_x를 인스턴스화 할 때 이니셜 라이저에서 x의 setter가 호출됩니까?
Casey

7
@Casey : 아니요 ._x. 속성이 아니라 일반 속성 인 참조는 property줄 바꿈을 무시합니다 . 를 통해서만 참조 .x하십시오 property.
ShadowRanger

278

게터와 세터를 사용하는 파이썬적인 방법은 무엇입니까?

은 "파이썬"방법은 없다 "게터"와 "세터"를 사용하지만, 문제는 보여 같은 일반 속성을 사용하고, del삭제하는 (그러나 이름은 무죄 ... 내장 명령을 보호하기 위해 변경) :

value = 'something'

obj.attribute = value  
value = obj.attribute
del obj.attribute

나중에 설정을 수정하고 가져 property오려면 데코레이터 를 사용하여 사용자 코드를 변경하지 않고도 변경할 수 있습니다 .

class Obj:
    """property demo"""
    #
    @property            # first decorate the getter method
    def attribute(self): # This getter method name is *the* name
        return self._attribute
    #
    @attribute.setter    # the property decorates with `.setter` now
    def attribute(self, value):   # name, e.g. "attribute", is the same
        self._attribute = value   # the "value" name isn't special
    #
    @attribute.deleter     # decorate with `.deleter`
    def attribute(self):   # again, the method name is the same
        del self._attribute

(각 데코레이터 사용법은 이전 속성 객체를 복사하고 업데이트하므로 각 세트, 가져 오기 및 삭제 기능 / 방법에 대해 동일한 이름을 사용해야합니다.

위의 내용을 정의한 후 코드의 원래 설정, 가져 오기 및 삭제는 동일합니다.

obj = Obj()
obj.attribute = value  
the_value = obj.attribute
del obj.attribute

이것을 피해야합니다 :

def set_property(property,value):  
def get_property(property):  

첫째, 속성이 (보통 self) 으로 설정된 인스턴스에 대한 인수를 제공하지 않기 때문에 위와 같이 작동하지 않습니다 .

class Obj:

    def set_property(self, property, value): # don't do this
        ...
    def get_property(self, property):        # don't do this either
        ...

둘째, 이것은 두 가지 특별한 방법 __setattr__과 목적을 복제합니다 __getattr__.

셋째, 우리는 또한이 setattrgetattr내장 기능.

setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value)  # default is optional

@property장식은 getter 및 setter를 만드는 것입니다.

예를 들어 설정 동작을 수정하여 설정중인 값을 제한 할 수 있습니다.

class Protective(object):

    @property
    def protected_value(self):
        return self._protected_value

    @protected_value.setter
    def protected_value(self, value):
        if acceptable(value): # e.g. type or range check
            self._protected_value = value

일반적으로 우리는 사용을 피하고 property직접 속성을 사용하려고 합니다.

이것은 파이썬 사용자가 기대하는 것입니다. 가장 놀랍게도 규칙에 따라 반대되는 이유가 없다면 사용자에게 기대하는 것을 제공해야합니다.

데모

예를 들어, 객체의 protected 속성이 0에서 100 사이의 정수 여야하고 사용자에게 올바른 사용법을 알려주는 적절한 메시지와 함께 삭제를 방지해야한다고 가정 해보십시오.

class Protective(object):
    """protected property demo"""
    #
    def __init__(self, start_protected_value=0):
        self.protected_value = start_protected_value
    # 
    @property
    def protected_value(self):
        return self._protected_value
    #
    @protected_value.setter
    def protected_value(self, value):
        if value != int(value):
            raise TypeError("protected_value must be an integer")
        if 0 <= value <= 100:
            self._protected_value = int(value)
        else:
            raise ValueError("protected_value must be " +
                             "between 0 and 100 inclusive")
    #
    @protected_value.deleter
    def protected_value(self):
        raise AttributeError("do not delete, protected_value can be set to 0")

(주는 __init__를 의미 self.protected_value하지만, 등록 방법을 참조하십시오 self._protected_value.이 때문에 즉__init__ 사용을 보장 공개 API를 통해 속성은 "보호"이다.)

그리고 사용법 :

>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0

이름이 중요합니까?

예 그들은 . .setter그리고 .deleter원래 재산의 복사본을 만든다. 이를 통해 서브 클래스는 부모의 동작을 변경하지 않고 동작을 올바르게 수정할 수 있습니다.

class Obj:
    """property demo"""
    #
    @property
    def get_only(self):
        return self._attribute
    #
    @get_only.setter
    def get_or_set(self, value):
        self._attribute = value
    #
    @get_or_set.deleter
    def get_set_or_delete(self):
        del self._attribute

이제 이것이 작동하려면 해당 이름을 사용해야합니다.

obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'  
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error

이것이 어디에 유용한 지 잘 모르겠지만 유스 케이스는 get, set 및 / 또는 delete-only 속성을 원하는 경우입니다. 이름이 같은 의미 적으로 동일한 속성을 고수하는 것이 가장 좋습니다.

결론

간단한 속성으로 시작하십시오.

나중에 설정, 가져 오기 및 삭제와 관련된 기능이 필요한 경우 속성 데코레이터를 사용하여 추가 할 수 있습니다.

피 기능은 이름 set_...get_...의 어떤 속성에 대한 것을 -.


사용 가능한 기능을 복제하는 것 외에도 왜 자신 만의 세터와 게터를 작성하지 않아야합니까? 나는 그것이 파이썬 방식이 아닐 수도 있다는 것을 이해하지만, 그렇지 않으면 실제로 발생할 수있는 심각한 문제가 있습니까?
user1350191

4
데모에서 __init__메소드는 참조 self.protected_value하지만 getter 및 setter는 참조합니다 self._protected_value. 어떻게 작동하는지 설명해 주시겠습니까? 코드를 테스트했으며 그대로 작동하므로 오타가 아닙니다.
codeforester

2
@ codeforester 나는 대답에 앞서 대답하기를 바랐지만 가능할 때 까지이 의견으로 충분합니다. 공공 API를 통해 해당 자산을 사용하여 "보호 된"상태임을 확인하시기 바랍니다. 속성으로 "보호"한 다음 비공개 API를 대신 사용하는 것이 합리적 __init__이지 않습니까?
Aaron Hall

2
예, @AaronHall이 지금 얻었습니다. self.protected_value = start_protected_value실제로 setter 함수를 호출 한다는 것을 몰랐습니다 . 나는 그것이 임무라고 생각했다.
codeforester

1
내가 파이썬을 올바르게 이해했다면 imho 이것은 대답이 될 것이다. 기본적으로 모든 것을 비공개로 만들고 파이썬에서 공개적으로 필요할 때 추가 코드를 작성하는 대신 모든 것을 공개하고 나중에 프라이버시를 추가 할 수 있습니다.
idclev 463035818

27
In [1]: class test(object):
    def __init__(self):
        self.pants = 'pants'
    @property
    def p(self):
        return self.pants
    @p.setter
    def p(self, value):
        self.pants = value * 2
   ....: 
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20

17

사용 @property하고 @attribute.setter당신이 "파이썬"방법을 사용하는 것이 아니라 도움뿐만 아니라 속성의 유효성을 확인하는 두 개체를 만드는 동안이를 변경할 때.

class Person(object):
    def __init__(self, p_name=None):
        self.name = p_name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, new_name):
        if type(new_name) == str: #type checking for name property
            self._name = new_name
        else:
            raise Exception("Invalid value for name")

이를 통해 실제로 _name클라이언트 개발자로부터 속성을 '숨기기' 하고 이름 속성 유형을 확인합니다. 시작하는 동안에도이 접근 방식을 따르면 setter가 호출됩니다. 그래서:

p = Person(12)

다음으로 이어질 것입니다 :

Exception: Invalid value for name

그러나:

>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception

16

@property데코레이터를 확인하십시오 .


34
이것은 거의 링크 전용 답변입니다.
Aaron Hall


7
이것이 어떻게 완전한 답입니까? 링크는 답이 아닙니다.
Andy_A̷n̷d̷y̷

나는 그것이 문서를 사용하는 방법을 명확하게 설명하고 (파이썬 구현이 변경되면 최신 상태를 유지하고 응답자가 제안하는 방법으로 OP를 지시하기 때문에 좋은 대답이라고 생각합니다. @ Jean-FrançoisCorbett '어떻게'그것은 완전한 답변입니다
HunnyBear

어쨌든이 답변은 다른 답변에 유용한 것을 추가하지 않으므로 이상적으로 삭제해야합니다.
Georgy

5

접근 자 / 뮤 테이터 (예 : @attr.setter@property)를 사용할 수 있지만 가장 중요한 것은 일관된 것입니다 !

@property단순히 속성에 액세스하는 데 사용 하는 경우 ( 예 :

class myClass:
    def __init__(a):
        self._a = a

    @property
    def a(self):
        return self._a

이것을 사용하여 every * 속성 에 액세스하십시오 ! 접근자를 사용 하지 않고 일부 속성을 사용 @property하고 다른 속성을 공개 (예 : 밑줄없는 이름)로 두는 것은 좋지 않습니다. 예를 들어

class myClass:
    def __init__(a, b):
        self.a = a
        self.b = b

    @property
    def a(self):
        return self.a

참고 self.b 가 공공 비록 여기에서 명시 적으로 접근이 없습니다.

세터 (또는 뮤 테이터 ) 와 마찬가지로 자유롭게 사용 @attribute.setter하지만 일관성을 유지하십시오! 예를 들어

class myClass:
    def __init__(a, b):
        self.a = a
        self.b = b 

    @a.setter
    def a(self, value):
        return self.a = value

당신의 의도를 추측하기가 어렵습니다. 한 손으로 당신은 두 말을하는지 a그리고 b내가 이론적 접근 / 개의 mutate (GET / SET) 모두 허용해야하므로 (이름에 선두에 밑줄) 공개됩니다. 하지만 당신은 단지에 대한 명시 뮤 테이터 지정 a어쩌면 내가 설정할 수 없습니다 것을 나에게 알려줍니다 b. 명시 적 뮤 테이터를 제공했기 때문에 명시 적 접근 자 ( @property) 가 부족 하여 해당 변수 중 하나에 액세스 할 수 없어야하는지 또는 단순히 사용하는 것에 대해 절박한 것인지 확실 하지 않습니다 @property.

* 일부 변수를 명시 적으로 액세스 가능하거나 변경 가능하게 만들거나 두 가지 모두 허용하지 않으려는 경우 또는 속성에 액세스하거나 변경할 때 추가 논리를 수행하려는 경우는 예외입니다 . 이것은 내가 개인적으로 사용 @property하고있는 경우입니다 @attribute.setter(그렇지 않으면 공개 속성에 대한 명시 적 후임 / 뮤 테이터가 없음).

마지막으로 PEP8 및 Google 스타일 가이드 제안 :

상속을위한 디자인 PEP8 은 다음과 같이 말합니다.

단순한 공개 데이터 속성의 경우 복잡한 접근 자 / 돌연변이 방법없이 속성 이름 만 노출 하는 것이 가장 좋습니다 . 간단한 데이터 속성이 기능적 행동을 키워야한다는 사실을 알게되면 Python은 향후 개선을위한 쉬운 길을 제공한다는 점을 명심하십시오. 이 경우 속성을 사용하여 간단한 데이터 속성 액세스 구문 뒤에 기능 구현을 숨기십시오.

반면, Google 스타일 가이드 Python 언어 규칙 / 속성에 따르면 권장 사항은 다음과 같습니다.

새 코드의 속성을 사용하여 일반적으로 단순하고 가벼운 접근 자 또는 세터 메서드를 사용했던 데이터에 액세스하거나 데이터를 설정하십시오. @property데코레이터로 속성을 만들어야합니다 .

이 접근법의 장점 :

간단한 속성 액세스를위한 명시적인 get 및 set 메소드 호출을 제거함으로써 가독성이 향상되었습니다. 계산이 지연 될 수 있습니다. 클래스의 인터페이스를 유지하는 파이썬 방식을 고려했습니다. 성능면에서 직접 변수 액세스가 합리적인 경우 속성을 허용하는 것은 사소한 접근 자 방법이 필요하지 않습니다. 또한 나중에 인터페이스를 손상시키지 않고 접근 자 메서드를 추가 할 수 있습니다.

그리고 단점 :

object파이썬 2에서 상속 받아야 합니다. 연산자 오버로딩과 같은 부작용을 숨길 수 있습니다. 서브 클래스를 혼동 할 수 있습니다.


1
나는 매우 동의하지 않습니다. 객체에 15 개의 속성이 있고으로 계산하려는 @property경우 나머지도 사용 @property하는 것은 좋지 않은 결정처럼 보입니다.
Quelklef

동의해야하는 특정 속성에 대한 특정 사항이있는 경우에만 동의하십시오 @property(예 : 속성을 반환하기 전에 일부 특수 로직 실행). 그렇지 않으면 왜 하나의 속성을 @propery다른 속성으로 장식 하지 않겠습니까?
Tomasz Bartkowiak

@Quelklef는 게시물에서 별표를 참조하십시오 (별표 표시).
Tomasz Bartkowiak

글쎄 ... 당신이 사이드 노트에서 언급 한 것들 중 하나를하지 않는다면 @property, 시작하기 위해 사용해서는 안됩니다 . getter가 return this._x있고 setter가 this._x = new_x인 경우 @property전혀 사용 하는 것이 어리 석습니다.
Quelklef

1
흠. 나는 개인적으로 그것이 좋지 않다고 말할 것입니다. 그것은 전적으로 불필요한 것입니다. 하지만 어디에서 왔는지 알 수 있습니다. 나는 "사용할 때 가장 중요한 것은 @property일관성이있다"는 글을 읽은 것 같습니다 .
Quelklef

-1

당신은 마법의 방법을 사용할 수 있습니다 __getattribute____setattr__.

class MyClass:
    def __init__(self, attrvalue):
        self.myattr = attrvalue
    def __getattribute__(self, attr):
        if attr == "myattr":
            #Getter for myattr
    def __setattr__(self, attr):
        if attr == "myattr":
            #Setter for myattr

__getattr__그리고 __getattribute__동일하지 않다는 것을 명심하십시오 . __getattr__속성을 찾을 수 없을 때만 호출됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.