2015-06-26 15 views
10

मैं प्रदर्शन बढ़ाने के उद्देश्य से एक सजावटी बनाने के बारे में सोच रहा था। एक सजावट जो उस समारोह के स्रोत कोड को संशोधित करता है जो इसे सजाने के लिए संशोधित करता है, और संशोधित फ़ंक्शन देता है।सजावट में कार्य संशोधित करें

इस बारे में सोचते समय, मुझे लगा कि अगर मुझे फ़ंक्शन का स्रोत कोड मिल सकता है, तो मैं यह कर सकता हूं। लेकिन क्या एक सजावट के अंदर एक समारोह के स्रोत कोड का उपयोग करना संभव है? अगर मैं इस तरह की एक डेकोरेटर है:

import inspect 

def decorate(f): 
    exec(inspect.getsource(f)) 
    return eval(f.__name__) 

@decorate 
def test(): 
    return 1 

मैं एक OSError मिलती है:

OSError: could not get source code 

इसका कारण यह है test पूरी तरह से पहले यह decorate में पारित हो जाता से नहीं बना है प्रतीत होता है। हालांकि, यह काम करता है:

import inspect 

def decorate(f): 
    exec(inspect.getsource(f)) 
    return eval(f.__name__) 

def test(): 
    return 1 
test = decorate(test) 

हालांकि इसमें उस सजावटी फ्लेयर नहीं है, हालांकि। ऐसा लगता है कि यह संभव हो सकता है, क्योंकि f.__code__ परिभाषित किया गया है।


से देखने पर, ऐसा लगता है कि यह केवल होता है जब मैं exec में inspect.getsource(f) डाल दिया। अन्यथा, ऐसा लगता है कि मैं स्रोत कोड प्राप्त कर सकता हूं।


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

def tail_recurse(acc_default): 
    def decorate(f): 
     def wrapper(*args, acc=acc_default): 
      args = args + (acc,) 
      while True: 
       return_type, *rargs = f(*args) 
       if return_type is None: 
        return rargs[-1] 
       args = rargs 
     return wrapper 
    return decorate 

असल में, मैं के साथ एक समारोह के शरीर की जगह के रूप में सरल कुछ करने का सोच रहा हूँ:

while True: 
    __body__ 
    update_args 
+3

अस्थिर शायद –

+0

के लिए अधिक उपयुक्त है 'exec (inspect.getsource (f)) से पहले एक पंक्ति खाली छोड़ दें और यह काम करेगा। दिलचस्प !! –

+1

@ अश्विनी चौधरी यह मेरे लिए कुछ भी नहीं करता .... लेकिन इससे मुझे लगता है कि यह एक सहमति मुद्दा हो सकता है, या कार्यान्वयन निर्भर – Justin

उत्तर

4

आप अपने मूल कोड के साथ functools.wraps उपयोग कर सकते हैं:

import inspect 
from functools import wraps 

@wraps 
def decorate(f): 
    exec(inspect.getsource(f)) 
    return eval(f.__name__) 

@decorate 
def test(): 
    return 1 

आउटपुट:

In [2]: test() 
Out[2]: 1 

आप रनटाइम पर स्रोत से बदल रहा है तो आप ast पुस्तकालय से परिचित हो जाना चाहिए पर योजना है, वहाँ है एक उत्कृष्ट video from pycon 2011 जहां मैथ्यू डेसमारास मूल बातें से स्रोत कोड को बदलने के लिए अस्थ मॉड्यूल का उपयोग करने के बारे में एक बात देता है, तो अधिक उन्नत विकल्प तक, यह पाइथन का जावास्क्रिप्ट अनुवादक टी का एक सरल कामकाजी उदाहरण है। बातचीत में टोपी का उपयोग किया जाता है, यह उपलब्ध उदाहरणों के लिए सरल उदाहरणों के लिए काम करेगा।

यह आपको अच्छी तरह से समझना चाहिए कि कैसे नोडट्रांसफॉर्मर काम करता है जो आप रनटाइम पर अपने कोड में हेरफेर करने के लिए उपयोग करना चाहते हैं, आप नीचे दिए गए डीक फ़ंक्शन के समान कुछ का उपयोग करके अपने कार्यों को सजाने के लिए, अंतर होगा संकलित कोड लौटा दी जाएगी:

from ast import parse, NodeTransformer 


class Transformer(NodeTransformer): 
    def __init__(self): 
     self.src = "" 
     self.indent = 0 

    def translate(self, node): 
     self.visit(node) 
     return self.src 

    def _indent(self, line): 
     return "{}{line}".format(" " * self.indent, line=line) 

    def render(self, body): 
     self.indent += 2 
     for stmt in body: 
      self.visit(stmt) 
     self.indent -= 2 

    def visit_Num(self, node): 
     self.src += "{}".format(node.n) 

    def visit_Str(self, node): 
     self.src += "{}".format(node.s) 

    def visit_FunctionDef(self, defn): 
     args = ",".join(name.arg for name in defn.args.args) 
     js_defn = "var {} = function({}){{\n" 
     self.src += self._indent(js_defn.format(defn.name, args)) 
     self.render(defn.body) 
     self.src += self._indent("}\n") 

    def visit_Eq(self, less): 
     self.src += "==" 

    def visit_Name(self, name): 
     self.src += "{}".format(name.id) 

    def visit_BinOp(self, binop): 
     self.visit(binop.left) 
     self.src += " " 
     self.visit(binop.op) 
     self.src += " " 
     self.visit(binop.right) 

    def visit_If(self, _if): 
     self.src += self._indent("if (") 
     self.visit(_if.test) 
     self.src += ") {\n" 
     self.render(_if.body) 
      self.src += " "*self.indent + "}\n" 


    def visit_Compare(self, comp): 
     self.visit(comp.left) 
     self.src += " " 
     self.visit(comp.ops[0]) 
     self.src += " " 
     self.visit(comp.comparators[0]) 

    def visit_Call(self, call): 
     self.src += " " 
     self.src += "{}(".format(call.func.id) 
     self.visit(call.args[0]) 
     self.src += ")" 

    def visit_Add(self, add): 
     self.src += "+" 

    def visit_Sub(self, add): 
     self.src += "-" 

    def visit_Return(self, ret): 
     self.src += self._indent("return") 
     if ret.value: 
      self.src += " " 
      self.visit(ret.value) 
     self.src += ";\n" 


def dec(f): 
    source = getsource(f) 
    _ast = parse(source) 
    trans = Transformer() 
    trans.indent = 0 
    return trans.translate(_ast) 


from inspect import getsource 


def fibonacci(n): 
    if n == 0: 
     return 0 
    if n == 1: 
     return 1 
    return fibonacci(n - 1) + fibonacci(n - 2) 

दिसम्बर समारोह चल रहा है जावास्क्रिप्ट के रूप में हमारे अजगर आउटपुट:

print(dec(fibonacci)) 
var fibonacci = function(n){ 
    if (n == 0) { 
    return 0; 
    } 
    if (n == 1) { 
    return 1; 
    } 
    return fibonacci(n - 1) + fibonacci(n - 2); 
} 

greentreesnakes डॉक्स भी पढ़ने लायक हैं।

1

यह काम करता है:

import inspect, itertools 

def decorate(f): 
    source = itertools.dropwhile(lambda line: line.startswith('@'), inspect.getsource(f).splitlines()) 
    exec('\n'.join(source)) 
    return eval(f.__name__) 

@decorate 
def test(): 
    return 1 

मुझे लगता है कि समस्या समारोह स्रोत में डेकोरेटर के शामिल किए जाने है।

# foo.py 
import inspect 

def decorate(f): 
    print inspect.getsource(f) 

@decorate 
def test(): 
    return 1 

>>> import foo 
@decorate 
def test(): 
    return 1 
>>> # Notice the decorator is included in the source. 

exec एक test एक स्ट्रिंग में परिभाषित के लिए @decorate देखता है, तो यह decorate रिकर्सिवली कहता है, लेकिन इस बार inspect.getsource विफल रहता है, क्योंकि यह एक समारोह में परिभाषित के स्रोत नहीं मिल सकता है एक स्ट्रिंग।

+0

अहह का उपयोग करके एक उदाहरण फेंक दूंगा, जो समझ में आता है – Justin

+0

यह स्मार्ट है। ऐसा लगता है कि 'exec' सजावटी को देख रहा है और इसे पुन: लागू करने में विफल रहा है, विफल रहा है। – Justin

संबंधित मुद्दे