파이썬에서 속성 기능을 사용하는 방법에 대한 실제 예?


143

@property파이썬 에서 사용하는 방법에 관심이 있습니다 . 나는 파이썬 문서를 읽었으며 그 의견은 장난감 코드 일뿐입니다.

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속성 데코레이터로 채워서 어떤 이점을 얻을 수 있는지 모르겠습니다 . 왜 다음과 같이 구현하지 않습니까?

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

속성 기능은 일부 상황에서 유용 할 수 있다고 생각합니다. 하지만 때? 누군가 제게 실제 사례를 알려 주시겠습니까?

감사.


10
이것은 내가 가장 잘
꾸며진

2
제공 한 링크의 마지막 예에서 @Anubis는 c = Celsius (-500)로 설정하면 ValueError가 발생하지 않아 의도 한 결과를 얻지 못한다고 생각합니다.
Sajuuk

@Anubis에 동의하십시오. 그것은 제대로 여기에 구현 것 : python-course.eu/python3_properties.php
anon01을

답변:


92

다른 예로는 집합 속성의 유효성 검사 / 필터링 (한계 또는 수용 가능) 및 복잡하거나 빠르게 변하는 용어에 대한 게으른 평가가 있습니다.

속성 뒤에 숨겨진 복잡한 계산 :

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

확인:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value

1
PDB_Calculator 예제가 마음에 듭니다. 복잡한 것은 추상화되고 모든 것이 작동하며 사용자는 단순함을 즐길 수 있습니다!
Adam Kurkiewicz 2016 년

2
아마도 프로 입장에서 볼 때 이것들은 아주 좋은 예입니다. 그러나 noobie 로서이 예제는 매우 효과적이지 않습니다. 내 나쁜 ... :(
kmonsoor

78

하나의 간단한 유스 케이스는 읽기 전용 인스턴스 속성을 설정하는 것 _x입니다. 파이썬에서 하나의 밑줄 로 변수 이름을 가져 오는 것은 보통 개인용 (내부 사용)을 의미하지만 때로는 인스턴스 속성을 읽고 쓸 수는 없습니다. 우리는 property이것을 위해 사용할 수 있습니다 :

>>> class C(object):

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

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

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute

7
c._x사용자가 원하는 경우 여전히을 설정할 수 있습니다 . 파이썬에는 실제로 개인 속성이 없습니다.

21

매우 실용적 으로이 기사 를 살펴보십시오 . 간단히 말해서, 파이썬에서 보통 명시적인 getter / setter 메소드를 버릴 수있는 방법을 설명합니다. 어떤 단계에서 필요하다면 property매끄러운 구현에 사용할 수 있기 때문 입니다.


15

내가 사용했던 한 가지는 데이터베이스에 저장된 느리게 보이지만 변경되지 않는 값을 캐싱하는 것입니다. 이것은 속성이 계산이나 요구가 많은 다른 긴 작업 (예 : 데이터베이스 확인, 네트워크 통신)이 필요한 상황을 일반화합니다.

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

이것은 웹 페이지에서 주어진 페이지보기 가이 종류의 특정 속성을 하나만 필요로하지만 기본 객체 자체에는 여러 가지 속성이있을 수 있습니다. 시공시 모두 초기화하면 낭비가되며 속성을 사용하면 유연 할 수 있습니다 속성은 게으르고 그렇지 않습니다.


1
@cached_property이것을 사용할 수 없습니까 ?
adarsh

@adarsh-흥미있는 소리. 어디있어?
detly

나는 그것을 사용했지만 나는 그것이 내장 아니라는 것을 잊었지만 당신이 그것을 사용할 수 있습니다 pypi.python.org/pypi/cached-property/0.1.5
ADARSH

2
흥미 롭군 나는이 답변 후에 처음으로 출판되었다고 생각하지만, 이것을 읽는 사람은 아마도 그것을 사용해야합니다.
detly

10

답변과 의견을 읽으면 주요 주제는 답변이 간단하지만 유용한 예가 누락 된 것 같습니다. 여기에 @property데코레이터 의 간단한 사용법을 보여주는 매우 간단한 것이 포함되어 있습니다 . 사용자가 다양한 단위 (예 : in_feet또는)를 사용하여 거리 측정을 지정하고 얻을 수있는 클래스입니다 in_metres.

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

용법:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14

개인적으로 getters / setters의 가장 좋은 예는 사람들에게 나중에 변경해야 할 종류의 변화를 보여 주지만 분명히 시간이 조금 더 걸립니다.
dtc

7

속성은 특정 필드를 조작하고 미들웨어 계산을 수행 할 수있는 방법을보다 세밀하게 제어 할 수있는 필드 주변의 추상화입니다. 기억에 남는 사용법 중 일부는 유효성 검사와 사전 초기화 및 액세스 제한입니다.

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x

6

예, 게시 된 원래 예제의 경우 속성은 단순히 인스턴스 변수 'x'를 갖는 것과 정확히 동일하게 작동합니다.

이것은 파이썬 속성에 대한 가장 좋은 것입니다. 외부에서는 인스턴스 변수와 똑같이 작동합니다! 클래스 외부에서 인스턴스 변수를 사용할 수 있습니다.

이것은 첫 번째 예제가 실제로 인스턴스 변수를 사용할 수 있음을 의미합니다. 상황이 변경 된 다음 구현을 변경하기로 결정하고 속성이 유용한 경우 속성에 대한 인터페이스는 클래스 외부의 코드와 동일합니다. 인스턴스 변수에서 속성으로 변경해도 클래스 외부의 코드에는 영향을 미치지 않습니다.

다른 많은 언어와 프로그래밍 과정에서는 프로그래머가 인스턴스 변수를 노출하지 말고 클래스 외부에서 액세스 할 수있는 'getters'및 'setters'를 사용하십시오 (질문에 인용 된 간단한 경우도).

많은 언어 (예 : Java)를 사용하는 클래스 외부의 코드

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

그리고 클래스를 구현할 때 첫 번째 예제와 정확히 같은 많은 'getter'와 'setter'가 있습니다. 단순히 인스턴스 변수를 복제하십시오. 클래스 구현이 변경되면 클래스 외부의 모든 코드를 변경해야하므로 이러한 getter 및 setter가 필요합니다. 그러나 파이썬 속성은 클래스 외부의 코드가 인스턴스 변수와 동일하도록 허용합니다. 따라서 속성을 추가하거나 간단한 인스턴스 변수가 있으면 클래스 외부의 코드를 변경할 필요가 없습니다. 따라서 대부분의 객체 지향 언어와 달리 간단한 예제 에서는 실제로 필요하지 않은 'getter'및 'setter'대신 인스턴스 변수를 사용할 있으며 나중에 속성으로 변경하면 코드를 사용한다는 사실을 알고 있습니다. 수업은 변경할 필요가 없습니다.

즉, 복잡한 동작이있는 경우에만 속성을 작성해야하며, 질문에 설명 된대로 간단한 인스턴스 변수 만 있으면 인스턴스 변수를 사용할 수있는 매우 일반적인 간단한 경우에 해당합니다.


6

setter와 getter를 사용하는 것보다 속성의 또 다른 멋진 기능은 속성, 액세스 제어, 캐싱 등을 유지하면서 속성에서 OP = 연산자 (예 : + =,-=, * = 등)를 계속 사용할 수 있도록합니다. 세터와 게터가 공급합니다.

예를 들어 Personsetter setage(newage)와 getter로 클래스를 작성한 경우 getage()연령을 늘리려면 다음 과 같이 작성해야합니다.

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

그러나 당신이 age재산을 만들면 훨씬 더 깨끗하게 쓸 수 있습니다.

bob.age += 1

5

귀하의 질문에 대한 짧은 대답은 귀하의 예에서 이점이 없다는 것입니다. 속성이 포함되지 않은 양식을 사용해야합니다.

속성이 존재하는 이유는 나중에 코드가 변경되고 데이터 캐시, 액세스 보호, 외부 리소스 쿼리 등의 데이터로 갑자기 더 많은 작업을 수행해야하는 경우입니다. 클래스를 쉽게 수정하여 게터를 추가 할 수 있습니다. 인터페이스 변경 하지 않고 데이터에 대한 setter를 사용 하므로 코드에서 데이터에 액세스하는 모든 위치를 찾아 변경할 필요가 없습니다.


4

많은 사람들이 처음에는 눈치 채지 못했던 것은 자신이 소유 한 하위 클래스를 만들 수 있다는 것입니다. 이것은 읽고 쓸 수는 있지만 제거 할 수는없는 읽기 전용 객체 속성이나 속성을 노출하는 데 매우 유용하다는 것을 알았습니다. 또한 객체 필드의 수정 사항 추적과 같은 기능을 래핑하는 훌륭한 방법입니다.

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')

나는 이것을 좋아한다. 접근자가 삭제 기능없이 읽기 / 쓰기 인 동안 해당 리더에서 읽기 전용으로 올바르게 읽습니까? 그래도 데이터 유효성 검사를 어떻게 추가 하시겠습니까? 나는 파이썬에 익숙하지 않지만 attr = reader('_attr')라인에 콜백을 추가하는 방법 이나 사전 검사와 같은 형태 를 추가하는 방법이 있다고 생각 attr = if self.__isValid(value): reader('_attr')합니다. 제안?
Gabe Spradlin

죄송합니다. 읽기 전용 변수에 대한 데이터 유효성 검사에 대해 묻는 중입니다. 그러나 분명히 이것은 접근 자 클래스의 setter 부분에만 적용됩니다. 로 변경 attr = reader('_attr')하십시오 attr = accessor('_attr'). 감사합니다
Gabe Spradlin

당신이 유효성 검사를 원한다면 유효하지 않은 경우 (또는 당신이 좋아하는 행동을 포함하여) init에 예외를 확인하고 제기하는 함수를 추가하는 것이 옳습니다 . 가능한 패턴으로 위의 내용을 수정했습니다. 유효성 검사기는 세트의 발생 여부를 안내하기 위해 True | False를 반환해야합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.