[python] 파이썬 __call__ 특수 메소드 실제 예제

__call__클래스의 인스턴스가 호출되면 클래스의 메서드가 트리거 된다는 것을 알고 있습니다. 그러나이 특별한 방법을 언제 사용할 수 있는지 전혀 알지 못합니다. 왜냐하면 단순히 새로운 방법을 만들고 동일한 작업을 수행 할 수 있기 때문입니다.__call__ 할 수 있으며 인스턴스를 호출하는 대신 메소드를 호출 할 수 있기 때문입니다.

누군가 가이 특별한 방법을 실제로 사용한다면 나에게 정말로 감사하겠습니다.



답변

Django 양식 모듈은 __call__메소드를 잘 사용 하여 양식 유효성 검사를위한 일관된 API를 구현합니다. Django에서 양식에 대한 자체 유효성 검사기를 함수로 작성할 수 있습니다.

def custom_validator(value):
    #your validation logic

Django에는 이메일 유효성 검사기, URL 유효성 검사기 등과 같은 기본 내장 유효성 검사기가 있으며 RegEx 유효성 검사기의 범위에 속합니다. 이를 깨끗하게 구현하기 위해 Django는 함수 대신 호출 가능한 클래스를 사용합니다. RegexValidator에서 기본 정규식 유효성 검사 논리를 구현 한 다음 다른 유효성 검사를 위해 이러한 클래스를 확장합니다.

class RegexValidator(object):
    def __call__(self, value):
        # validation logic

class URLValidator(RegexValidator):
    def __call__(self, value):
        super(URLValidator, self).__call__(value)
        #additional logic

class EmailValidator(RegexValidator):
    # some logic

이제 사용자 정의 함수와 내장 EmailValidator를 동일한 구문으로 호출 할 수 있습니다.

for v in [custom_validator, EmailValidator()]:
    v(value) # <-----

보시다시피, 장고 에서의이 구현은 다른 사람들이 아래 답변에서 설명한 것과 유사합니다. 다른 방법으로 구현할 수 있습니까? 당신은 할 수 있지만 IMHO는 장고와 같은 큰 프레임 워크에서 읽거나 쉽게 확장 할 수 없습니다.


답변

이 예제는 기본적으로 값을 테이블에 저장하는 memoization을 사용하므로 (이 경우 사전) 나중에 다시 계산하는 대신 값을 찾을 수 있습니다.

여기서는 정적 변수를 포함하는 계승 함수 (Python에서는 불가능) 대신 __call__계승을 계산 하는 메소드 와 함께 간단한 클래스를 사용합니다 ( 호출 가능한 객체를 통해 ).

class Factorial:
    def __init__(self):
        self.cache = {}
    def __call__(self, n):
        if n not in self.cache:
            if n == 0:
                self.cache[n] = 1
            else:
                self.cache[n] = n * self.__call__(n-1)
        return self.cache[n]

fact = Factorial()

이제 fact다른 모든 함수와 마찬가지로 호출 가능한 객체가 있습니다. 예를 들어

for i in xrange(10):
    print("{}! = {}".format(i, fact(i)))

# output
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

그리고 그것은 또한 상태입니다.


답변

사용하기 쉽고 (특정 인수가 필요한 호출 가능한 객체가 있음) API를 만들 수 있기 때문에 유용하며 객체 지향 사례를 사용할 수 있기 때문에 구현하기 쉽습니다.

다음은 어제 내가 작성한 코드로 hashlib.foo문자열 대신 전체 파일을 해시 하는 메소드 버전을 만듭니다 .

# filehash.py
import hashlib


class Hasher(object):
    """
    A wrapper around the hashlib hash algorithms that allows an entire file to
    be hashed in a chunked manner.
    """
    def __init__(self, algorithm):
        self.algorithm = algorithm

    def __call__(self, file):
        hash = self.algorithm()
        with open(file, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), ''):
                hash.update(chunk)
        return hash.hexdigest()


md5    = Hasher(hashlib.md5)
sha1   = Hasher(hashlib.sha1)
sha224 = Hasher(hashlib.sha224)
sha256 = Hasher(hashlib.sha256)
sha384 = Hasher(hashlib.sha384)
sha512 = Hasher(hashlib.sha512)

이 구현을 통해 함수와 비슷한 방식으로 함수를 사용할 수 hashlib.foo있습니다.

from filehash import sha1
print sha1('somefile.txt')

물론 다른 방법으로 구현할 수도 있었지만이 경우에는 간단한 접근 방식처럼 보였습니다.


답변

__call__파이썬에서 데코레이터 클래스를 구현하는데도 사용됩니다. 이 경우 데코레이터가있는 메소드가 호출 될 때 클래스의 인스턴스가 호출됩니다.

class EnterExitParam(object):

    def __init__(self, p1):
        self.p1 = p1

    def __call__(self, f):
        def new_f():
            print("Entering", f.__name__)
            print("p1=", self.p1)
            f()
            print("Leaving", f.__name__)
        return new_f


@EnterExitParam("foo bar")
def hello():
    print("Hello")


if __name__ == "__main__":
    hello()


답변

예, 객체를 다루고 있다는 것을 알면 명시 적 메서드 호출을 사용하는 것이 가능합니다. 그러나 때로는 호출 가능한 객체를 기대하는 코드 (일반적으로 기능)를 처리하지만 __call__인스턴스 데이터 및 더 호출 가능한 반복 작업 등을 위임하는 더 많은 메소드를 사용하여 더 복잡한 객체를 작성할 수 있습니다.

또한 때로는 복잡한 작업 (전용 클래스를 작성하는 것이 합리적)에 대한 객체와 간단한 작업 (기능에 이미 존재하거나 더 쉽게 기능으로 작성되는)에 대한 객체를 모두 사용하고 있습니다. 공통 인터페이스를 가지려면 해당 인터페이스를 예상 인터페이스로 래핑하는 작은 클래스를 작성하거나 함수를 유지하고 더 복잡한 객체를 호출 가능하게 만들어야합니다. 실을 예로 들어 봅시다. Thread표준 라이브러리 모듈threading객체 는 호출 가능한 target인수 를 인수 로 원합니다 (예 : 새 스레드에서 수행 할 작업). 호출 가능한 객체를 사용하면 함수로 제한되지 않고 다른 스레드에서 작업을 가져 와서 순차적으로 실행하는 비교적 복잡한 작업자와 같은 다른 객체도 전달할 수 있습니다.

class Worker(object):
    def __init__(self, *args, **kwargs):
        self.queue = queue.Queue()
        self.args = args
        self.kwargs = kwargs

    def add_task(self, task):
        self.queue.put(task)

    def __call__(self):
        while True:
            next_action = self.queue.get()
            success = next_action(*self.args, **self.kwargs)
            if not success:
               self.add_task(next_action)

이것은 내 머리 꼭대기의 예일 뿐이지 만 클래스를 보증하기에 충분히 복잡하다고 생각합니다. 함수로만이 작업을 수행하는 것은 어렵습니다. 적어도 두 개의 함수를 반환해야하며 천천히 복잡해집니다. 하나는 이름을 __call__다른 것으로하고, 바인딩 된 메서드를 통과,하지만 약간 덜 분명 스레드를 생성하는 코드를 만들고, 값을 추가하지 않습니다.


답변

클래스 기반 데코레이터 __call__는 래핑 된 함수를 참조하는 데 사용 됩니다. 예 :

class Deco(object):
    def __init__(self,f):
        self.f = f
    def __call__(self, *args, **kwargs):
        print args
        print kwargs
        self.f(*args, **kwargs)

Artima.com 의 다양한 옵션에 대한 좋은 설명이 있습니다.


답변

IMHO __call__메소드와 클로저는 파이썬에서 STRATEGY 디자인 패턴을 만드는 자연스러운 방법을 제공합니다. 우리는 알고리즘 군을 정의하고, 각각을 캡슐화하고, 상호 교환 가능하게 만들고 결국 공통 단계 세트를 실행하고 파일의 해시를 계산할 수 있습니다.