如何跟踪嵌套装饰器的深度?

时间:2019-06-25 10:27:22

标签: python nested decorator nested-forms

为简化起见,我指的是HTML代码,尽管有所不同。

我使用装饰器生成此类代码。

我必须通过缩进来格式化输出。 我认为我需要跟踪嵌套级别以适应缩进,但是我不知道是否以及如何跟踪嵌套级别。 例如(https://www.thecodeship.com/patterns/guide-to-python-function-decorators/

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

def strong_decorate(func):
    def func_wrapper(name):
        return "<strong>{0}</strong>".format(func(name))
    return func_wrapper

def div_decorate(func):
    def func_wrapper(name):
        return "<div>{0}</div>".format(func(name))
    return func_wrapper


get_text = div_decorate(p_decorate(strong_decorate(get_text)))

@div_decorate
@p_decorate
@strong_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print(get_text("John"))

# Outputs <div><p><strong>lorem ipsum, John dolor sit amet</strong></p></div>

我想使用通用算法获得

<div>
    <p>
        <strong>lorem ipsum, John dolor sit amet</strong>
    </p>
</div>

有可能吗?以及如何?

我有一个代码,但是很长。

EDit:由于人们在这里询问了真实的示例,所以您拥有我旨在获得的输出

config vdom
edit <vdom>
config router static
    edit <number>
        set dst <ip> <netmask>
        set gateway <ip>
        set device <intf>
    next
end
end

谢谢

2 个答案:

答案 0 :(得分:1)

您可以使用递归来构建嵌套的列表列表以存储整体结构。由于您的装饰器遵循类似的{tag}_decorate模式,因此可以使用带有__getattr__的类来消除为每个所需的HTML标签创建单独的装饰器功能的需要。可以递归遍历返回的嵌套列表以生成所需的结构。 traverse函数存储一个计数器以跟踪缩进:

class HTML:
   def __getattr__(self, tag):
      def outer(f):
        def wrapper(name):
            return [tag, f(name)]
        return wrapper
      return outer


def traverse(d, c = 0):
   a, b = d
   if not isinstance(b, list):
      return f'{"  "*c}<{a}>{b}</{a}>'
   return f'{"  "*c}<{a}>\n{" "*(c+1)}{traverse(b, c+1)}\n{"   "*c}</{a}>'

html = HTML()

@html.div
@html.p
@html.strong
def get_text(name):
  return "lorem ipsum, {0} dolor sit amet".format(name)

print(traverse(get_text("John")))

输出:

<div>
   <p>
      <strong>lorem ipsum, John dolor sit amet</strong>
   </p>
</div>

答案 1 :(得分:0)

感谢大家的贡献。最终我结束了这个。 答案中的egt_stack_size

How do I get the current depth of the Python interpreter stack?(第二个答案,我不知道是否有直接链接)

import sys
TAB=" "
def get_stack_size():
    size = 2  # current frame and caller's frame always exist
    while True:
        try:
            sys._getframe(size)
            size += 1
        except ValueError:
            return size - 1  # subtract current frame

def p_decorate(func):
    def func_wrapper(name):
        ind=get_stack_size()-2
        ret = ind*TAB+"<p>\n"+ \
              "{0}\n".format(func(name))+ \
              ind*TAB+"</p>"
        return ret
    return func_wrapper

def strong_decorate(func):
    def func_wrapper(name):
        ind=get_stack_size()-2
        ret = ind*TAB+"<strong>\n"+ \
              "{0}\n".format(func(name))+ \
              ind*TAB+"</strong>"
        return ret
    return func_wrapper

def div_decorate(func):
    def func_wrapper(name):
        ind=get_stack_size()-2
        ret = ind*TAB+"<div>\n"+ \
              "{0}\n".format(func(name))+ \
              ind*TAB+"</div>"
        return ret
    return func_wrapper

#get_text = div_decorate(p_decorate(strong_decorate(get_text)))

@strong_decorate
@div_decorate
@p_decorate
def get_text(name):
    ind=get_stack_size()-2
    return ind*TAB+"lorem ipsum, {0} dolor sit amet".format(name)

print(get_text("John"))

正如@brunodesthuilliers指出的那样,排雷可能不是最好的方法。 @ Ajax1234我担心您的标签对我不起作用,因为开始和结束标签不仅是一个单词,而且是一个句子,因此我应该处理标签的名称以获得正确的“标签”,但是您的标签是一个很好的练习这样可以节省很多代码:-)