파이썬 함수 속성-사용 및 남용 [폐쇄]


196

이 기능을 잘 아는 사람은 많지 않지만 Python의 함수 (및 메소드)는 속성 을 가질 수 있습니다 . 보다:

>>> def foo(x):
...     pass
...     
>>> foo.score = 10
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'score']
>>> foo.score
10
>>> foo.score += 1
>>> foo.score
11

파이썬에서이 기능의 가능한 사용과 남용은 무엇입니까? 내가 아는 한 가지 좋은 사용법은 구문 규칙을 메소드와 연관시키기 위해 PLY 의 docstring 사용입니다. 그러나 사용자 정의 속성은 어떻습니까? 그것들을 사용해야 할 좋은 이유가 있습니까?


3
PEP 232를 확인하십시오 .
user140352

2
이것은 매우 놀라운가? 일반적으로 Python 객체는 임시 속성을 지원합니다. 물론, 일부는 내장형이 아닌 것도 있습니다. 나에게, 이것을 지원하지 않는 사람들은 규칙이 아니라 예외 인 것처럼 보입니다.
allyourcode

3
장고의 한 응용 프로그램 : 관리자 변경 목록 사용자 지정
Grijesh 차우

2
@GrijeshChauhan 나는이 문서를 본 후에이 질문에 왔습니다!
Alexander Suraphel

5
유감스럽게도, 호출 코드에서 잡을 때 쉽게 액세스 할 수 있도록 함수에서 발생할 수있는 모든 사용자 정의 예외를 첨부 할 수 있다고 덧붙이고 싶습니다. 나는 예시적인 예를 제공하지만 대답에서 가장 잘 수행됩니다.
Will Hardy

답변:


154

일반적으로 함수 속성을 주석의 저장소로 사용합니다. C # 스타일로 작성하고 싶다고 가정하십시오 (특정 메소드가 웹 서비스 인터페이스의 일부 여야 함을 나타냄)

class Foo(WebService):
    @webmethod
    def bar(self, arg1, arg2):
         ...

그런 다음 정의 할 수 있습니다

def webmethod(func):
    func.is_webmethod = True
    return func

그런 다음 웹 서비스 호출이 도착하면 메소드를 찾고 기본 함수에 is_webmethod 속성이 있는지 확인하고 (실제 값은 관련이 없음) 메소드가 없거나 웹을 통해 호출 될 의도가 아닌 경우 서비스를 거부합니다.


2
이것에 단점이 있다고 생각합니까? 예를 들어 두 라이브러리가 동일한 임시 속성을 쓰려고하면 어떻게됩니까?
allyourcode

18
나는 이것을 정확하게 생각하고있었습니다. 그런 다음 나는 스스로를 멈췄다. "이것은 나쁜 생각입니까?" 난 궁금해. 그런 다음 나는 SO로 방황했다. 엉뚱한 소리가 난 후에이 질문 / 답변을 발견했습니다. 이것이 좋은 아이디어인지 여전히 확실하지 않습니다.
allyourcode

7
이것은 모든 답변에서 함수 속성을 가장 합법적으로 사용하는 것입니다 (2012 년 11 월 기준). 대부분의 다른 답변은 전역 변수를 대체하기 위해 함수 속성을 사용합니다. 그러나 전역 변수의 문제인 전역 상태를 제거하지는 않습니다. 값이 설정되면 변경되지 않기 때문에 이것은 다릅니다. 그것은 일정하다. 이것의 좋은 결과는 전역 변수에 내재 된 동기화 문제가 발생하지 않기 때문입니다. 그렇습니다, 당신은 당신의 자신의 동기화를 제공 할 수 있지만, 요점입니다 : 그것은 자동으로 안전하지 않습니다.
allyourcode

실제로, 속성이 문제의 함수의 동작을 변경하지 않는 한 좋습니다. 로 비교.__doc__
디마 Tisnek

이 접근법은 출력 설명을 데코 레이팅 된 함수에 첨부하는 데 사용될 수 있으며, 파이썬 2에서는 누락되었습니다. *
Juh_

126

함수의 정적 변수로 사용했습니다. 예를 들어 다음과 같은 C 코드가 제공됩니다.

int fn(int i)
{
    static f = 1;
    f += i;
    return f;
}

파이썬에서 비슷하게 함수를 구현할 수 있습니다.

def fn(i):
    fn.f += i
    return fn.f
fn.f = 1

이것은 스펙트럼의 "구슬"끝에 빠질 것입니다.


2
흥미 롭군 파이썬에서 정적 변수를 구현하는 다른 방법이 있습니까?
Eli Bendersky

4
-1, 이것은 파이썬에서 생성기로 구현됩니다.

124
이것이이 답변을 내리는 꽤 나쁜 이유입니다.이 특정 함수를 작성하는 가장 좋은 방법을 옹호하지 않고 C와 Python 사이의 유사성을 보여줍니다.
Robert Rossney

3
@RobertRossney 그러나 제너레이터가 갈 길이라면 이것이 함수 속성을 잘 사용하지 않는 것입니다. 그렇다면 남용입니다. 그래도
남들

1
확실히, PEP (232) 당, 남용처럼 user140352 @ 감사를 보이고, 홉의 의견과 이 SO의 대답은
호브

53

JavaScript 방식으로 객체를 수행 할 수 있습니다 ... 의미가 없지만 작동합니다.)

>>> def FakeObject():
...   def test():
...     print "foo"
...   FakeObject.test = test
...   return FakeObject
>>> x = FakeObject()
>>> x.test()
foo

36
+1이 기능의 남용에 대한 좋은 예는 질문이 제기 한 것 중 하나입니다.
Michael Dunn

1
이것이 mipadi의 답변과 어떻게 다릅니 까? int 대신에 속성 값이 함수라는 것을 제외하고는 같은 것으로 보입니다.
allyourcode

입니다 def test()정말 필요?
Keerthana Prabhakaran

15

나는 그것들을 조금만 사용하지만 매우 편리 할 수 ​​있습니다.

def log(msg):
   log.logfile.write(msg)

이제 log모듈 전체에서 사용할 수 있으며 간단히 설정하여 출력을 리디렉션 할 수 있습니다 log.logfile. 그것을 달성하는 많은 방법이 많이 있지만, 이것은 가볍고 흙이 간단합니다. 처음했을 때 우스운 냄새가 났지만 전역 logfile변수 보다 냄새가 더 좋다고 믿었습니다 .


7
다시 냄새 : 이것은 전역 로그 파일을 제거하지는 않습니다. 그것은 다른 전역 로그 함수에서 그것을 멀리 다람쥐합니다.
allyourcode

2
@allyourcode : 그러나 동일한 모듈의 다른 기능에 대해 여러 개의 전역 로그 파일이 있어야하는 경우 이름 충돌을 피할 수 있습니다.
firegurafiku

11

함수 속성을 사용하여 코드와 관련 데이터를 함께 감싸는 간단한 클로저를 작성할 수 있습니다.

#!/usr/bin/env python

SW_DELTA = 0
SW_MARK  = 1
SW_BASE  = 2

def stopwatch():
   import time

   def _sw( action = SW_DELTA ):

      if action == SW_DELTA:
         return time.time() - _sw._time

      elif action == SW_MARK:
         _sw._time = time.time()
         return _sw._time

      elif action == SW_BASE:
         return _sw._time

      else:
         raise NotImplementedError

   _sw._time = time.time() # time of creation

   return _sw

# test code
sw=stopwatch()
sw2=stopwatch()
import os
os.system("sleep 1")
print sw() # defaults to "SW_DELTA"
sw( SW_MARK )
os.system("sleep 2")
print sw()
print sw2()

1.00934004784

2.00644397736

3.01593494415


3
수업이 편리한데 왜 푸시 함수를 사용합니까? 클래스가 함수를 에뮬레이트 할 수 있다는 것을 잊지 마십시오.
muhuk

1
또한 time.sleep(1)보다 낫다os.system('sleep 1')
보리스 Gorelik

3
@bgbg이 예제는 수면에 관한 것이 아니지만 사실입니다.
allyourcode

이것은 확실히 남용입니다. 여기서 함수의 사용은 완전히 무의미합니다. muhuk이 옳습니다. 클래스가 더 나은 솔루션입니다.
allyourcode

1
또한 "클래스에 비해이 점의 장점은 무엇입니까?" 많은 파이썬 프로그래머에게는 이것이 분명하지 않다는 단점을 극복하기 위해.
cjs

6

이 도우미 데코레이터를 만들어 기능 속성을 쉽게 설정할 수 있습니다.

def with_attrs(**func_attrs):
    """Set attributes in the decorated function, at definition time.
    Only accepts keyword arguments.
    E.g.:
        @with_attrs(counter=0, something='boing')
        def count_it():
            count_it.counter += 1
        print count_it.counter
        print count_it.something
        # Out:
        # >>> 0
        # >>> 'boing'
    """
    def attr_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            return fn(*args, **kwargs)

        for attr, value in func_attrs.iteritems():
            setattr(wrapper, attr, value)

        return wrapper

    return attr_decorator

유스 케이스는 팩토리 콜렉션을 작성하고 함수 메타 레벨에서 작성할 수있는 데이터 유형을 조회하는 것입니다.
예를 들어 (매우 바보 같은) :

@with_attrs(datatype=list)
def factory1():
    return [1, 2, 3]

@with_attrs(datatype=SomeClass)
def factory2():
    return SomeClass()

factories = [factory1, factory2]

def create(datatype):
    for f in factories:
        if f.datatype == datatype:
            return f()
    return None

데코레이터는 어떻게 도움이됩니까? 왜 factory1.datatype=list구식 데코레이터처럼 선언 바로 아래에 설정하지 않습니까?
Ceasar Bautista 2016 년

2
2 가지 주요 차이점 : 스타일, 여러 속성을 쉽게 설정할 수 있습니다. 확실히 속성으로 설정할 수는 있지만 내 의견으로는 여러 속성으로 자세하게 설명 할 수 있으며 추가 처리를 위해 데코레이터를 확장 할 수있는 기회를 얻습니다 (예 : 기본값은 함수를 사용하는 모든 장소 대신 한 장소에서 정의되거나 속성이 설정된 후 추가 함수를 호출하십시오). 이 모든 결과를 달성 할 수있는 다른 방법이 있습니다. 저는 이것이 더 깨끗하지만 내 마음을 바꾸게되어 기쁩니다.)
DiogoNeves

빠른 업데이트 : Python 3에서는 items()대신 대신 사용해야 합니다 iteritems().
Scott은

4

때로는 이미 계산 된 값을 캐싱하기 위해 함수의 속성을 사용합니다. 이 방법을 일반화하는 일반 데코레이터를 사용할 수도 있습니다. 동시성 문제와 이러한 기능의 부작용에주의하십시오!


나는이 아이디어를 좋아한다! 계산 된 값을 캐싱하는 더 일반적인 트릭은 dict를 호출자가 제공하지 않을 속성의 기본값으로 사용하는 것입니다 .Python은 함수를 정의 할 때 한 번만 데이터를 저장하고 거기에 데이터를 저장할 수 있다고 평가하므로 주위에. 함수 속성을 사용하는 것이 덜 명확하지만 나에게는 해키가 덜 느낍니다.
Soren Bjornstad

1

나는 이것이 가능한 유일한 이유는 문서 문자열이나 다른 것들을 넣을 수있는 논리적 장소가 항상 있다고 가정했습니다. 프로덕션 코드에 사용하면 대부분의 사람들이 혼란스럽게 할 것입니다.


1
나는 이것이 혼란 스러울 가능성에 대한 당신의 요점에 동의하지만, 문서 문자열을 다시 작성하십시오. docstring을 보유하기위한 고정 된 속성 세트가있을 수 있습니다.
allyourcode

@allyourcode 특정 임시 사례가 아닌 일반적인 사례를 언어로 설계하면 상황이 더 단순 해지고 이전 버전의 Python과의 호환성이 향상됩니다. (예를 들어, docstring을 설정 / 조작하는 코드는 속성이 존재하지 않는 경우를 처리하는 한 docstring을 수행하지 않는 Python 버전에서도 작동합니다.)
cjs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.