Декоратор для замены определенной строки в функции на другую строку - PullRequest
1 голос
/ 23 октября 2019

Можно ли так поступить?

def my_func():
    my_list = ['abc', 'def', 'ghi']
    my_str = 'abc'

Если я передам A декоратору -> заменим 'abc' на 'xxx' в функции, передам B -> yyy

@decorator ('abc')

def my_func():
    my_list = ['xxx', 'def', 'ghi']
    my_str = 'xxx'

Я не знаю, возможно ли это или нет.

Как я могу это сделать?

Ответы [ 3 ]

2 голосов
/ 23 октября 2019

Вы можете использовать декоратор, который использует ast.NodeTransformer для изменения любого строкового узла с целевым значением с заданной заменой в AST функции:

import ast
import inspect
from textwrap import dedent

class Replace(ast.NodeTransformer):
    def __init__(self, target, replacement):
        self.target = target
        self.replacement = replacement

    def visit_Str(self, node):
        if node.s == self.target:
            node.s = self.replacement
        return node

    # remove 'replace' from the function's decorator list to avoid re-decorating during exec
    def visit_FunctionDef(self, node):
        node.decorator_list = [
            decorator for decorator in node.decorator_list
            if not isinstance(decorator, ast.Call) or decorator.func.id != 'replace'
        ]
        self.generic_visit(node)
        return node

def replace(target, repl):
    def decorator(func):
        tree = Replace(target, repl).visit(ast.parse(dedent(inspect.getsource(func))))
        ast.fix_missing_locations(tree)
        scope = {}
        exec(compile(tree, inspect.getfile(func), "exec"), func.__globals__, scope)
        return scope[func.__name__]
    return decorator

так, чтобы:

@replace('abc', 'xxx')
def my_func():
    my_list = ['abc', 'def', 'ghi']
    my_str = 'abc'
    print(my_list, my_str)

my_func()

выходы:

['xxx', 'def', 'ghi'] xxx

Демонстрация: https://repl.it/@blhsing/ValuableLimeVideogames

1 голос
/ 23 октября 2019

Можно заменить объект кода функции декоратором, тем самым заменив внутри него константы, но вряд ли это решение, которое вы на самом деле хотите использовать.

Это может быть неприемлемым практически при любоммасштабируйте, как если бы у вас были большие разделы, похожие на:

# Note, this is python 3.x specific
CodeType(
    argcount,             #   integer
    kwonlyargcount,       #   integer
    nlocals,              #   integer
    stacksize,            #   integer
    flags,                #   integer
    codestring,           #   bytes
    consts,               #   tuple
    names,                #   tuple
    varnames,             #   tuple
    filename,             #   string
    name,                 #   string
    firstlineno,          #   integer
    lnotab,               #   bytes
    freevars,             #   tuple
    cellvars,             #   tuple
)

, в которых вам нужно сделать копии из исходного объекта кода, изменив их по своему усмотрению.

Лучшее решение для этогоПроблема в том, чтобы разрешить передачу строки в функцию в качестве параметра. Если вам нужно, чтобы функция потом вызывалась без присутствующей строки, вы можете использовать частичное (см. functools.partial )

0 голосов
/ 23 октября 2019

Декоратор не может изменить логику внутри вашей функции. Вы можете сделать операцию над аргументами или что-то, что вы возвращаете. В вашем случае вы можете использовать почтовый декоратор

...