पायथन __call__ विशेष विधि व्यावहारिक उदाहरण


159

मुझे पता है कि __call__एक वर्ग में विधि तब शुरू होती है जब किसी वर्ग का उदाहरण कहा जाता है। हालाँकि, मुझे नहीं पता कि मैं इस विशेष पद्धति का उपयोग कब कर सकता हूं, क्योंकि कोई केवल एक नई विधि बना सकता है और __call__विधि में किया गया एक ही ऑपरेशन कर सकता है और उदाहरण को कॉल करने के बजाय, आप विधि को कॉल कर सकते हैं।

मैं वास्तव में इसकी सराहना करूंगा अगर कोई मुझे इस विशेष पद्धति का व्यावहारिक उपयोग करने दे।



8
की कार्यक्षमता _call_ बस की तरह है की अतिभारित ऑपरेटर () C ++ । यदि आप कक्षा के बाहर एक नई विधि बनाते हैं, तो आप किसी वर्ग में आंतरिक डेटा तक नहीं पहुँच सकते हैं।
एंडी

2
का सबसे आम उपयोग __call__सादे दृश्य में छिपा हुआ है; यह है कि आप किस तरह से एक क्लास को इंस्टेंट करते हैं : x = Foo()वास्तव में x = type(Foo).__call__(Foo), जहां __call__मेटाक्लास द्वारा परिभाषित किया गया है Foo
चेन्नेर २ che ’१

जवाबों:


88

Django रूपों मॉड्यूल __call__विधि सत्यापन के लिए एक संगत एपीआई को लागू करने के लिए अच्छी तरह से विधि का उपयोग करता है । आप एक फ़ंक्शन के रूप में Django में फ़ॉर्म के लिए अपना स्वयं का सत्यापनकर्ता लिख ​​सकते हैं।

def custom_validator(value):
    #your validation logic

Django में कुछ डिफ़ॉल्ट बिल्ट-इन वैरिडेटर्स हैं जैसे कि ईमेल वैलिडेटर्स, url वैलिडेटर्स आदि, जो मोटे तौर पर RegEx वैद्युतकों की छतरी के नीचे आते हैं। इन सफाई से लागू करने के लिए, Django कॉल करने योग्य कक्षाओं (कार्यों के बजाय) का समर्थन करता है। यह RegexValidator में डिफ़ॉल्ट Regex सत्यापन तर्क को लागू करता है और फिर अन्य मान्यताओं के लिए इन वर्गों को बढ़ाता है।

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

जैसा कि आप देख सकते हैं, Django में यह कार्यान्वयन उसी के समान है जो दूसरों ने नीचे दिए गए अपने उत्तरों में समझाया है। क्या इसे किसी अन्य तरीके से लागू किया जा सकता है? आप कर सकते हैं, लेकिन IMHO यह पठनीय या Django जैसे बड़े ढांचे के लिए आसानी से एक्स्टेंसिबल नहीं होगा।


5
इसलिए अगर सही तरीके से इस्तेमाल किया जाए तो यह कोड को अधिक पठनीय बना सकता है। मुझे लगता है कि अगर यह गलत जगह पर उपयोग किया जाता है, तो यह कोड को बहुत ही अपठनीय बना देगा।
मोहि६६

15
यह एक उदाहरण है कि इसका उपयोग कैसे किया जा सकता है, लेकिन मेरी राय में यह अच्छा नहीं है। इस मामले में कॉल करने योग्य उदाहरण होने का कोई लाभ नहीं है। एक विधि के साथ एक इंटरफ़ेस / अमूर्त वर्ग रखना बेहतर होगा, जैसे .validate (); यह केवल और अधिक स्पष्ट बात है। __Call__ का वास्तविक मूल्य एक ऐसे स्थान पर एक उदाहरण का उपयोग करने में सक्षम हो रहा है जहां एक कॉल करने योग्य होने की उम्मीद है। उदाहरण के लिए, सज्जाकार बनाते समय मैं __call__ का सबसे अधिक उपयोग करता हूं।
डेनियल

120

यह उदाहरण संस्मरण का उपयोग करता है , मूल रूप से एक तालिका में मान संग्रहीत करता है (इस मामले में शब्दकोश) ताकि आप उन्हें पुनर्गणना करने के बजाय बाद में देख सकें।

यहां हम एक फैक्टरियल फ़ंक्शन के बजाय फैक्टरियल फ़ंक्शन __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

और यह स्टेटफुल भी है।


2
मेरे पास एक ऐसी factवस्तु होगी जो आपके __call__कार्य के बाद से अनुक्रमित हो। इसके अलावा एक सूची के बजाय एक तानाशाही का उपयोग करेगा, लेकिन यह सिर्फ मुझे है।
क्रिस लुत्ज़

4
@ डायलेन - लगभग कुछ भी कई अलग-अलग तरीकों से किया जा सकता है। कौन सा अधिक पठनीय है यह पाठक पर निर्भर करता है।
क्रिस लुत्ज

1
@ क्रिस लुत्ज़: आप उन प्रकार के परिवर्तनों पर विचार करने के लिए स्वतंत्र हैं। सामान्य रूप से संस्मरण के लिए , एक शब्दकोश अच्छी तरह से काम करता है क्योंकि आप उस आदेश की गारंटी नहीं दे सकते हैं जिसमें चीजें आपकी सूची को भरती हैं। इस मामले में, एक सूची काम कर सकती है, लेकिन यह कोई तेज़ या सरल नहीं है।
S.Lott

8
@delnan: यह कम से कम होने का इरादा नहीं है। कोड गोल्फ में कोई नहीं जीतता। यह दिखाने का इरादा है __call__, सरल हो और अधिक कुछ नहीं हो।
एस.लॉट

3
लेकिन यह उस तरह के उदाहरण को बर्बाद कर देता है जब प्रदर्शन की गई तकनीक कार्यों के लिए आदर्श नहीं होती है? (और मैं "चलो इसे बचाने के लिए लाइनों को बचाने के बारे में नहीं था" -short, मैं बात कर रहा था "इसे समान रूप से स्पष्ट तरीके से लिखने और कुछ बॉयलरप्लेट कोड को बचाने के लिए" -short। बाकी का आश्वासन दिया है कि मैं नहीं हूँ। उन पागलों में से एक जो सबसे छोटा कोड संभव लिखने की कोशिश कर रहा है, मैं केवल बॉयलरप्लेट कोड से बचना चाहता हूं जो पाठक के लिए कुछ नहीं जोड़ता है।)

40

मुझे यह उपयोगी लगता है क्योंकि यह मुझे एपीआई बनाने की अनुमति देता है जो उपयोग करने में आसान है (आपके पास कुछ कॉल करने योग्य वस्तु है जो कुछ विशिष्ट तर्क की आवश्यकता है), और लागू करने में आसान है क्योंकि आप ऑब्जेक्ट ओरिएंटेड प्रथाओं का उपयोग कर सकते हैं।

निम्नलिखित कोड मैंने कल लिखा है जो उन 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')

बेशक मैं इसे एक अलग तरीके से लागू कर सकता था, लेकिन इस मामले में यह एक सरल दृष्टिकोण की तरह लग रहा था।


7
फिर, बंद इस उदाहरण को बर्बाद करते हैं। pastebin.com/961vU0ay 80% लाइनें हैं और बस के रूप में स्पष्ट है।

8
मुझे यकीन नहीं है कि यह हमेशा किसी के लिए स्पष्ट होगा (उदाहरण के लिए शायद कोई ऐसा व्यक्ति जिसने केवल जावा का उपयोग किया हो)। नेस्टेड फ़ंक्शन और वेरिएबल लुकअप / स्कोप भ्रामक हो सकते हैं। मुझे लगता है कि मेरी बात यह थी कि __call__आपको एक उपकरण दिया गया है जिससे आप समस्याओं को हल करने के लिए OO तकनीकों का उपयोग कर सकते हैं।
bradley.ayers

4
मुझे लगता है कि प्रश्न "क्यों वाई पर एक्स का उपयोग करता है" जब दोनों समान कार्यक्षमता प्रदान करते हैं बहुत ही व्यक्तिपरक है। कुछ लोगों के लिए ओओ दृष्टिकोण को समझना आसान है, दूसरों के लिए बंद दृष्टिकोण है। जब तक आपके पास एक ऐसी स्थिति न हो, जब तक कि आपको ऐसी स्थिति का उपयोग न करना पड़े, जब तक कि आपको उसी तरह का उपयोग न करना पड़ेisinstance
bradley.ayers

2
@delnan आपके बंद होने का उदाहरण कोड की कम लाइनें हैं, लेकिन यह उतना ही स्पष्ट है जितना कि बहस करना अधिक कठिन है।
डेनिस

8
एक उदाहरण जहां आप __call__एक क्लोजर के बजाय एक विधि का उपयोग करेंगे , जब आप मल्टीप्रोसेसिंग मॉड्यूल के साथ काम कर रहे हैं, जो अचार का उपयोग प्रक्रियाओं के बीच जानकारी देने के लिए करता है। आप एक करीबी अचार नहीं कर सकते हैं, लेकिन आप एक वर्ग का एक उदाहरण चुन सकते हैं।
जॉन पीटर थॉम्पसन गार्स

21

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

9

हां, जब आप जानते हैं कि आप वस्तुओं के साथ काम कर रहे हैं, तो एक स्पष्ट विधि कॉल का उपयोग करना पूरी तरह से संभव है (और कई मामलों में)। हालांकि, कभी-कभी आप कोड के साथ सौदा करते हैं जो कि कॉल करने योग्य वस्तुओं की अपेक्षा करता है - आम तौर पर कार्य करता है, लेकिन __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__कुछ और करने के लिए और एक बाध्य विधि गुजरती हैं, लेकिन यह है कि धागा थोड़ा कम स्पष्ट बनाने कोड बनाता है, और किसी भी मूल्य नहीं जोड़ता है।


3
यहां "डक टाइपिंग" ( en.wikipedia.org/wiki/Duck_typing#In_Python ) वाक्यांश का उपयोग करना संभवतः उपयोगी है - आप इस तरह से अधिक जटिल वर्ग ऑब्जेक्ट का उपयोग करके फ़ंक्शन की नकल कर सकते हैं ।
एंड्रयू जाफ

2
संबंधित उदाहरण के रूप में, मैंने __call__WSGI अनुप्रयोगों के रूप में कक्षा के उदाहरणों (कार्यों के बजाय) का उपयोग करने के लिए उपयोग किया है। यहां "द डेफिनिटिव गाइड टू पाइलन्स" का एक उदाहरण दिया गया है: कक्षाओं के उदाहरणों का उपयोग करना
जोश रोसेन

5

क्लास-आधारित डेकोरेटर __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 पर यहां विभिन्न विकल्पों का अच्छा वर्णन है


मैं शायद ही कभी कक्षा सज्जाकारों को देखता हूं, क्योंकि उन्हें तरीकों के साथ काम करने के लिए कुछ गैर-स्पष्ट बॉयलरप्लेट कोड की आवश्यकता होती है।

4

__call__पायथन में स्ट्रैटेजी डिजाइन पैटर्न बनाने के लिए IMHO विधि और क्लोजर हमें एक प्राकृतिक तरीका देते हैं। हम एल्गोरिदम के एक परिवार को परिभाषित करते हैं, प्रत्येक को एनकैप्सुलेट करते हैं, उन्हें विनिमेय बनाते हैं और अंत में हम चरणों के एक सामान्य सेट को निष्पादित कर सकते हैं और, उदाहरण के लिए, फ़ाइल के लिए एक हैश की गणना करें।


4

मैं सिर्फ __call__()कंसर्ट के उपयोग पर लड़खड़ा गया, __getattr__()जिसके साथ मुझे लगता है कि सुंदर है। यह आपको एक ऑब्जेक्ट के अंदर JSON / HTTP / (हालांकि_serialized) API के कई स्तरों को छिपाने की अनुमति देता है।

__getattr__()भाग iteratively एक ही कक्षा का एक संशोधित उदाहरण लौटने, एक समय में एक से अधिक विशेषता में भरने का ख्याल रखता है। फिर, सारी जानकारी समाप्त हो जाने के बाद, __call__()आपने जो भी तर्क पारित किए हैं , उन पर अधिकार कर लेता है।

इस मॉडल का उपयोग करके, आप उदाहरण के लिए एक कॉल कर सकते हैं api.v2.volumes.ssd.update(size=20), जैसे कि PUT अनुरोध में समाप्त होता है https://some.tld/api/v2/volumes/ssd/update

OpenStack में एक विशेष वॉल्यूम बैकएंड के लिए विशेष कोड एक ब्लॉक स्टोरेज ड्राइवर है, आप इसे यहां देख सकते हैं: https://github.com/openstack/cinder/blob/master/cinder/volume/drivers/neppenta/jsonrpc.py

EDIT: मास्टर रिवीजन को इंगित करने के लिए लिंक अपडेट किया गया।


वह अच्छा हैं। मैंने एक बार विशेषता पहुंच का उपयोग करते हुए एक मनमाना XML ट्री को ट्रेस करने के लिए एक ही तंत्र का उपयोग किया था।
पेट्री

1

निर्दिष्ट करें __metaclass__और __call__विधि को ओवरराइड करें , और निर्दिष्ट मेटा क्लासेस की __new__विधि कक्षा का एक उदाहरण लौटाएं, वायोला आपके पास विधियों के साथ एक "फ़ंक्शन" है।


1

हम __call__अन्य श्रेणी विधियों को स्थिर विधियों के रूप में उपयोग करने के लिए विधि का उपयोग कर सकते हैं ।

    class _Callable:
        def __init__(self, anycallable):
            self.__call__ = anycallable

    class Model:

        def get_instance(conn, table_name):

            """ do something"""

        get_instance = _Callable(get_instance)

    provs_fac = Model.get_instance(connection, "users")             

0

एक सामान्य उदाहरण __call__में है functools.partial, यहाँ एक सरलीकृत संस्करण है (पायथन के साथ = = 3.5):

class partial:
    """New function with partial application of the given arguments and keywords."""

    def __new__(cls, func, *args, **kwargs):
        if not callable(func):
            raise TypeError("the first argument must be callable")
        self = super().__new__(cls)

        self.func = func
        self.args = args
        self.kwargs = kwargs
        return self

    def __call__(self, *args, **kwargs):
        return self.func(*self.args, *args, **self.kwargs, **kwargs)

उपयोग:

def add(x, y):
    return x + y

inc = partial(add, y=1)
print(inc(41))  # 42

0

फ़ंक्शन कॉल ऑपरेटर।

class Foo:
    def __call__(self, a, b, c):
        # do something

x = Foo()
x(1, 2, 3)

__Call__ विधि नए सिरे से परिभाषित / पुनः प्रारंभ करने का उद्देश्य एक ही इस्तेमाल किया जा सकता। यह एक वर्ग के उदाहरणों / वस्तुओं के उपयोग की सुविधा भी देता है ताकि वस्तुओं को तर्क दिया जा सके।


यह कब उपयोगी होगा? फू (1, 2, 3) स्पष्ट लगता है।
यारोस्लाव निकितेंको

0

मैं एक अच्छी जगह प्रतिदेय वस्तुओं का उपयोग करने के लिए, उन है कि परिभाषित __call__(), जब अजगर में कार्यात्मक प्रोग्रामिंग क्षमताओं का उपयोग, जैसे है map(), filter(), reduce()

किसी सादे फ़ंक्शन या लंबो फ़ंक्शन पर कॉल करने योग्य ऑब्जेक्ट का उपयोग करने का सबसे अच्छा समय तब होता है जब तर्क जटिल होता है और कुछ स्थिति को बनाए रखने की आवश्यकता होती है या __call__()फ़ंक्शन में पारित नहीं होने पर अन्य जानकारी का उपयोग करने की आवश्यकता होती है।

यहाँ कुछ कोड है जो फ़ाइल नाम को उनके फ़ाइल एक्सटेंशन के आधार पर कॉल करने योग्य ऑब्जेक्ट का उपयोग करके फ़िल्टर करता है और filter()

प्रतिदेय:

import os

class FileAcceptor(object):
    def __init__(self, accepted_extensions):
        self.accepted_extensions = accepted_extensions

    def __call__(self, filename):
        base, ext = os.path.splitext(filename)
        return ext in self.accepted_extensions

class ImageFileAcceptor(FileAcceptor):
    def __init__(self):
        image_extensions = ('.jpg', '.jpeg', '.gif', '.bmp')
        super(ImageFileAcceptor, self).__init__(image_extensions)

उपयोग:

filenames = [
    'me.jpg',
    'me.txt',
    'friend1.jpg',
    'friend2.bmp',
    'you.jpeg',
    'you.xml']

acceptor = ImageFileAcceptor()
image_filenames = filter(acceptor, filenames)
print image_filenames

आउटपुट:

['me.jpg', 'friend1.jpg', 'friend2.bmp', 'you.jpeg']

0

यह बहुत देर हो चुकी है लेकिन मैं एक उदाहरण दे रहा हूं। कल्पना कीजिए कि आपके पास एक Vectorवर्ग और एक Pointवर्ग है। दोनों x, yस्थिति के रूप में लेते हैं । आइए कल्पना करें कि आप एक फ़ंक्शन बनाना चाहते हैं जो वेक्टर पर लगाए जाने वाले बिंदु को स्थानांतरित करता है।

4 समाधान

  • put_point_on_vec(point, vec)

  • इसे वेक्टर क्लास पर एक विधि बनाएं। जैसे my_vec.put_point(point)

  • इसे Pointकक्षा पर एक विधि बनाएं ।my_point.put_on_vec(vec)
  • Vector औजार __call__ , ताकि आप इसे पसंद कर सकेंmy_vec_instance(point)

यह वास्तव में कुछ उदाहरणों का हिस्सा है, जिन पर मैं मैथ्स के साथ समझाए गए तरीकों के लिए काम कर रहा हूं, जो कि मैं जल्द या बाद में जारी करने वाला हूं।

मैंने बिंदु को स्थानांतरित करने का तर्क छोड़ दिया क्योंकि यह वह नहीं है जो यह सवाल है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.