क्या एक पायथन फ़ंक्शन को अचार करने का एक आसान तरीका है (या अन्यथा इसके कोड को क्रमबद्ध करें)?


100

मैं एक नेटवर्क कनेक्शन (asyncore का उपयोग करके) में एक फ़ंक्शन को स्थानांतरित करने की कोशिश कर रहा हूं। क्या इस तरह के हस्तांतरण के लिए एक पायथन फ़ंक्शन (एक है कि कम से कम इस मामले में कोई पक्ष प्रभावित नहीं होगा) को अनुक्रमित करने का एक आसान तरीका है?

मैं आदर्श रूप से इन के समान कार्यों की एक जोड़ी रखना चाहूंगा:

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()

जवाबों:


120

आप फ़ंक्शन बायटेकोड को क्रमबद्ध कर सकते हैं और फिर इसे कॉलर पर फिर से संगठित कर सकते हैं। मार्शल मॉड्यूल serialise कोड वस्तुओं, जो तब एक समारोह में फिर से जोड़ा जा सकता है के लिए इस्तेमाल किया जा सकता है। अर्थात:

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)

फिर दूरस्थ प्रक्रिया में (code_string स्थानांतरित करने के बाद):

import marshal, types

code = marshal.loads(code_string)
func = types.FunctionType(code, globals(), "some_func_name")

func(10)  # gives 100

कुछ चेतावनी:

  • मार्शल का प्रारूप (उस मामले के लिए कोई भी अजगर बाइटकोड) प्रमुख अजगर संस्करणों के बीच अनुकूल नहीं हो सकता है।

  • केवल cpython कार्यान्वयन के लिए काम करेगा।

  • यदि फ़ंक्शन ग्लोबल्स (आयातित मॉड्यूल, अन्य फ़ंक्शंस आदि सहित) को संदर्भित करता है, जिसे आपको लेने की आवश्यकता है, तो आपको इन्हें भी अनुक्रमित करना होगा या उन्हें रिमोट साइड पर फिर से बनाना होगा। मेरा उदाहरण बस इसे दूरस्थ प्रक्रिया के वैश्विक नाम स्थान देता है।

  • आपको संभवतः अधिक जटिल मामलों का समर्थन करने के लिए थोड़ा और अधिक करने की आवश्यकता होगी, जैसे क्लोजर या जनरेटर फ़ंक्शन।


1
पायथन 2.5 में, "नया" मॉड्यूल पदावनत है। 'new.function' को "import type" के बाद 'type.FunctionType' द्वारा प्रतिस्थापित किया जाना चाहिए, मेरा मानना ​​है।
एरिक ओ लेबिगॉट

2
धन्यवाद। यही वह है जिसकी तलाश में मैं हूं। कुछ सरसरी परीक्षण के आधार पर, यह जनरेटर के लिए काम करता है।
माइकल फेयरले

2
यदि आप मार्शल मॉड्यूल पर पैराग्राफ के पहले जोड़े को पढ़ते हैं, तो आप इसे अचार के बजाय दृढ़ता से उपयोग करने का सुझाव देते हैं? अचार पृष्ठ के लिए भी। docs.python.org/2/library/marshal.html
dgorissen

1
मैं marshalमॉड्यूल को शब्दकोश के एक शब्दकोश को क्रमबद्ध करने के लिए लागू करने की कोशिश कर रहा हूं defaultdict(lambda : defaultdict(int))। लेकिन यह त्रुटि लौटाता है ValueError: unmarshallable object। ध्यान दें आइसम usy python2.7। कोई उपाय? धन्यवाद
user17375

2
पायथन 3.5.3 पर, foo.func_codeउठाता है AttributeError। क्या फ़ंक्शन कोड प्राप्त करने का एक और तरीका है?
अलक्विमिस्ट

41

कार्यों सहित अधिक से अधिक प्रकारों का समर्थन करने के लिए, डिल की अचार लाइब्रेरी का विस्तार करने वाले डिल की जांच करें :

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

यह फ़ंक्शन के बंद होने में वस्तुओं के संदर्भ का भी समर्थन करता है:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3

2
डिल फंक्शंस और लैम्ब्डा से सोर्स कोड प्राप्त करने और उन डिस्क को सहेजने का एक बहुत अच्छा काम करता है, यदि आप उस वस्तु को चुनना पसंद करते हैं।
माइक मैकेर्नस

14

Pyro आपके लिए ऐसा करने में सक्षम है ।


मुझे इस विशेष परियोजना के लिए मानक पुस्तकालय के साथ रहना होगा।
माइकल फेयरले

21
लेकिन यह है कि आप नहीं कर सकते हैं इसका मतलब यह नहीं लग Pyro के कोड में यह कैसे किया जाता है :) देखने के लिए
हारून Digulla

4
@ आरोनडिगुल्ला- सच है, लेकिन यह ध्यान देने योग्य है कि किसी और के प्रकाशित कोड की एक पंक्ति को पढ़ने से पहले, आपको हमेशा सॉफ़्टवेयर के लाइसेंस की जांच करनी चाहिए। किसी और के कोड को पढ़ना और स्रोत का हवाला दिए बिना विचारों का पुन: उपयोग करना या लाइसेंस / बाधाओं की नकल करना कई मामलों में साहित्यिक चोरी और / या कॉपीराइट उल्लंघन माना जा सकता है।
mdscruggs

12

सबसे सरल तरीका शायद है inspect.getsource(object)( निरीक्षण मॉड्यूल देखें ) जो फ़ंक्शन या किसी विधि के लिए स्रोत कोड के साथ स्ट्रिंग लौटाता है।


यह अच्छा लग रहा है, सिवाय इसके कि फ़ंक्शन नाम को स्पष्ट रूप से कोड में परिभाषित किया गया है, जो थोड़ा समस्याग्रस्त है। मैं कोड की पहली पंक्ति को बंद कर सकता था, लेकिन 'def \ / n func ():' जैसा कुछ करने से यह टूटने योग्य है। मैं फ़ंक्शन के साथ ही फ़ंक्शन का नाम चुन सकता हूं, लेकिन मेरी कोई गारंटी नहीं है कि नाम टकराएगा नहीं, या मुझे फ़ंक्शन को एक आवरण में रखना होगा, जो अभी भी सबसे साफ समाधान नहीं है, लेकिन यह करना पड़ सकता है।
माइकल फेयरले

1
ध्यान दें कि निरीक्षण मॉड्यूल वास्तव में केवल उस फ़ंक्शन को पूछ रहा है जहां इसे परिभाषित किया गया था, और फिर स्रोत कोड फ़ाइल से उन पंक्तियों में पढ़ना - शायद ही परिष्कृत।
बहुत ज्यादा php

1
आप .__ name__ विशेषता का उपयोग करके फ़ंक्शन के नाम का पता लगा सकते हैं। आप एक रेगेक्स को ^ def \ s * {name} \ s * पर प्रतिस्थापित कर सकते हैं (और इसे जो भी आपको पसंद है उसे दे सकते हैं। यह मूर्खतापूर्ण नहीं है, लेकिन यह ज्यादातर चीजों के लिए काम करेगा।
बहुत ज्यादा php

6

यह सब इस बात पर निर्भर करता है कि आप रनटाइम पर फंक्शन जेनरेट करते हैं या नहीं:

यदि आप करते हैं - inspect.getsource(object)गतिशील रूप से उत्पन्न कार्यों के लिए काम नहीं करेगा क्योंकि यह .pyफ़ाइल से ऑब्जेक्ट का स्रोत प्राप्त करता है, इसलिए निष्पादन से पहले परिभाषित कार्यों को स्रोत के रूप में पुनर्प्राप्त किया जा सकता है।

और अगर आपके कार्यों को किसी भी तरह से फाइलों में रखा गया है, तो उन्हें रिसीवर एक्सेस क्यों न दें और केवल मॉड्यूल और फ़ंक्शन नामों के आसपास से गुजरें।

गतिशील रूप से बनाए गए फ़ंक्शंस का एकमात्र समाधान जो मैं सोच सकता हूं कि ट्रांसमिशन से पहले एक स्ट्रिंग के रूप में फ़ंक्शन का निर्माण करना है, स्रोत को प्रसारित करना है, और फिर eval()इसे रिसीवर की तरफ।

संपादित करें: marshalसमाधान भी बहुत स्मार्ट लगता है, पता नहीं है कि आप कुछ अन्य कि निर्मित ins क्रमबद्ध कर सकते हैं



2
code_string = '' ''
डीओ फू (एक्स):
    वापसी x * २
डीईएफ़ बार (x):
    वापसी x ** 2
'' '

obj = pickle.dumps (code_string)

अभी

कार्यकारी (pickle.loads (obj))

foo (1)
> २
बार (3)
> ९

2

तुम यह केर सकते हो:

def fn_generator():
    def fn(x, y):
        return x + y
    return fn

अब, मॉड्यूल नाम के संदर्भ transmit(fn_generator())के fn(x,y)बजाय वास्तविक निश्चितता भेजेंगे ।

आप नेटवर्क पर कक्षाएं भेजने के लिए उसी चाल का उपयोग कर सकते हैं।


1

इस मॉड्यूल के लिए उपयोग किए जाने वाले मूल कार्य आपकी क्वेरी को कवर करते हैं, साथ ही आपको तार पर सबसे अच्छा संपीड़न मिलता है; शिक्षाप्रद स्रोत कोड देखें:

y_serial.py मॉड्यूल :: वेयरहाउस पायथन ऑब्जेक्ट SQLite के साथ

"सीरियलाइज़ेशन + निरंतरता :: कोड की कुछ पंक्तियों में, SQLite में पायथन ऑब्जेक्ट्स को संपीड़ित और एनोटेट करें; फिर बाद में उन्हें बिना किसी SQL के कीवर्ड द्वारा कालानुक्रमिक रूप से पुनर्प्राप्त करें। स्कीमा-कम डेटा संग्रहीत करने के लिए डेटाबेस के लिए सबसे उपयोगी" मानक "मॉड्यूल।"

http://yserial.sourceforge.net


1

Cloudpickle शायद वही है जो आप ढूंढ रहे हैं। Cloudpickle का वर्णन इस प्रकार है:

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

उपयोग उदाहरण:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)

0

यहाँ एक सहायक वर्ग है जिसे आप पिक करने योग्य बनाने के लिए कार्यों को लपेटने के लिए उपयोग कर सकते हैं। पहले से बताए गए कैविट्स marshalलागू होंगे लेकिन जब भी संभव हो अचार का उपयोग करने का प्रयास किया जाता है। क्रमबद्धता के दौरान ग्लोबल्स या क्लोजर को संरक्षित करने के लिए कोई प्रयास नहीं किया जाता है।

    class PicklableFunction:
        def __init__(self, fun):
            self._fun = fun

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

        def __getstate__(self):
            try:
                return pickle.dumps(self._fun)
            except Exception:
                return marshal.dumps((self._fun.__code__, self._fun.__name__))

        def __setstate__(self, state):
            try:
                self._fun = pickle.loads(state)
            except Exception:
                code, name = marshal.loads(state)
                self._fun = types.FunctionType(code, {}, name)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.