【问题标题】:Call Nested Function in Python在 Python 中调用嵌套函数
【发布时间】:2012-06-22 10:41:00
【问题描述】:

我有一个方法,我已经分解成更小的嵌套函数来分解代码库:

def foo(x,y):
    def do_this(x,y):
        pass
    def do_that(x,y):
        pass
    do_this(x,y)
    do_that(x,y)
    return

有没有办法自己运行其中一个嵌套函数。例如:

foo.do_this(x,y)

编辑:

我正在尝试在使用 pyramid_breaker 构建的 Web 服务器上设置缓存

def getThis(request):
    def invalidate_data(getData,'long_term',search_term):
         region_invalidate(getData,'long_term',search_term)
    @cached_region('long_term')
    def getData(search_term):
         return response
    search_term = request.matchdict['searchterm']
    return getData(search_term)

这是我的理解可能不准确:

现在我有这个的原因是装饰器用来创建缓存键的命名空间是从函数和争论中生成的。因此,您不能只将装饰器放在 getThis 上,因为请求变量是唯一的并且缓存是无用的。所以我创建了具有可重复参数(search_term)的内部函数。

然而,为了使缓存失效(即刷新),失效函数需要知道“getData”函数的范围,因此也需要嵌套。因此我需要调用嵌套函数。你们这些了不起的人已经明确表示这是不可能的,那么有人能解释一下我如何用不同的结构来做到这一点吗?

【问题讨论】:

  • 您的代码foo.do_this 将尝试将do_this 作为foo 的成员函数访问,这会给您一个属性错误,而是将foo 作为一个类
  • “分解代码库”是模块命名空间的优点。如果您真的想封装 do_ 函数,请使用@lazyr 显示的类。
  • 嵌套函数不是在 Python 中构造代码的方式(类也不是)。看看modules
  • 查看我的答案:*.com/questions/7054228/…

标签: python nested


【解决方案1】:

我假设do_thisdo_that 实际上依赖于foo 的某些参数,否则您可以将它们移出foo 并直接调用它们。

我建议将整个事情作为一个班级重新设计。像这样的:

class Foo(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def do_this(self):
        pass

    def do_that(self):
        pass

    def __call__(self):
        self.do_this()
        self.do_that()

foo = Foo(x, y)
foo()
foo.do_this()

【讨论】:

  • 非常有帮助的答案,感觉我的整个结构可能是错误的。如果我解释大局以查看您是否可以提供帮助,可能会更有帮助(如果太难,请不要担心)。
  • @user1474424 我认为你应该把它放在一个新问题中。
  • @user1474424 看了这个问题后,我觉得最好将它作为一个新问题发布。我不熟悉您正在使用的这个库。删除更新并将其作为新问题发布。
  • 我尝试了新问题 - 没有人查看/回答...感谢您的帮助。
  • @user1474424 你忘记了最重要的“python”关键字,这就是为什么没有人回答。您可以删除您添加的所有其他关键字。我建议您删除该问题并使用正确的关键字重新发布。你应该做一个更好的标题——如果你想让人们看到你的问题,这些是很重要的——比如“嵌套函数问题”或“如何重构嵌套函数代码”。您的解释也可以改进。包括指向您正在使用的模块的链接,也许还有这个问题。
【解决方案2】:

这些以前的答案,告诉你不能这样做,当然是错误的。 这是 python,你几乎可以使用一些神奇的代码魔法来做任何你想做的事情。

我们可以从 foo 的函数代码中取出第一个常量,这将是 do_this 函数。然后我们可以使用这段代码来创建一个新的函数。

请参阅https://docs.python.org/2/library/new.html 了解有关 new 的更多信息和https://docs.python.org/2/library/inspect.html 了解有关如何获取内部代码的更多信息。

警告:不是因为你能做到这一点,你就应该这样做, 重新思考构建函数的方式是可行的方法,但如果你想要一个快速而肮脏的 hack,它可能会在未来被破坏,那么你可以这样做:

import new
myfoo = new.function(foo.func_code.co_consts[1],{}) 
myfoo(x,y) # hooray we have a new function that does what I want

更新:在 python3 中,您可以使用带有 foo.__code__ 的 types 模块:

import types
myfoo = types.FunctionType(foo.__code__.co_consts[1], {})
myfoo()  # behaves like it is do_this()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: do_this() missing 2 required positional arguments: 'x' and 'y'

【讨论】:

  • 在 Python 3 中我可以使用 types?
  • @VitalyZdanevich 是的,根据documentationDeprecated since version 2.6: The new module has been removed in Python 3. Use the types module’s classes instead.
  • 它打破了,至少在内部函数中使用enumerate:p
  • 这似乎是唯一真正回答问题的答案。它应该是公认的答案。
【解决方案3】:

有,你必须将它们作为函数对象的属性。但这只有在第一次调用 foo 之后才会起作用。

def foo(x,y):
    def do_this(x,y):
        pass
    def do_that(x,y):
        pass
    do_this(x,y)
    do_that(x,y)
    foo.do_this = do_this
    foo.do_that = do_that
    return

>>> foo.do_this(1, 2)
AttributeError: 'function' object has no attribute 'do_this'
>>> foo(1, 2)
>>> foo.do_this(1, 2)
>>>

【讨论】:

    【解决方案4】:

    不(除了在闭包对象中四处寻找,这在这里完全是矫枉过正)。如果需要,请使用类。

    class foo(object):
        def do_this(self, x, y):
           ...
        def do_that(self, x, y):
           ...
        def do_other_stuff(self, x, y):
           # or __call__, possibly
    

    或者只是将这些函数放在外部范围内,因为无论如何您都将所有内容作为参数传递:

    def foo(x, y):
        do_this(x, y)
        do_that(x, y)
    
    def do_this(x, y):
        ...
    
    def do_that(x, y):
        ...
    

    【讨论】:

      【解决方案5】:

      不,没有。由于您可以从嵌套函数中访问外部范围内的变量:

      def foo(x,y):
          def do_this(z):
              print(x,y,z)
          # ...
      

      在为xy 提供绑定的同时,无法调用do_this

      如果您必须从其他地方调用do_this,只需将其设置为与foo 处于同一级别的*函数。

      【讨论】:

      • 可以,看我的回答:myfoo = types.FunctionType(foo.__code__.co_consts[1], {'x': 'value for x here', 'y': 'value for y here'})
      【解决方案6】:

      你可以试试这个方法:

      def a(x, y):
          name = 'Michael'
          a.name = name
      
          a.z = z = x * y
          #a.z = z
      
      def b():
          def give_me_price(f,g):
              price = f * g
              return price
      
          def two(j,k):
              surname = 'Jordan' # without return surname give None
      
          # two = two('arg1', 'arg2')
          # b.blabla = two
      
          one = give_me_price(5, 10)
          b.halabala = one
      
          print(a.name) # ;)
      
      x = 20
      y = 30
      
      a(x,y) # IMPORTANT! first you must run function
      print(a.z)
      print(a.name * 5)
      
      print('-'*12)
      b() # IMPORTANT! first you must run function
      print('price is: ' + str(b.give_me_price(5, 25)))
      # print(b.blabla)
      

      【讨论】:

        【解决方案7】:

        我就是这样做的。

        代码

        def getMessage(a="", b="", c=""):
            def getErrorMessage(aa, bb):
                return "Error Message with/without params: {}{}".format(aa, bb)
        
            def getSuccessMessage(bb, cc):
                return "Success Message with/without params:  {}{}".format(bb, cc)
        
            def getWarningMessage(aa, cc):
                return "Warning Message with/without params:  {}{}".format(aa, cc)
        
            return {
                "getErrorMessage": getErrorMessage(a, b),
                "getSuccessMessage": getSuccessMessage(b, c),
                "getWarningMessage": getWarningMessage(a, c),
            }
        
        
        a = "hello"
        b = " World"
        c = "!"
        print(getMessage(a, b)["getErrorMessage"])
        print(getMessage(b=b, c=c)["getSuccessMessage"])
        print(getMessage(a=a, c=c)["getWarningMessage"])
        print(getMessage(c=c)["getWarningMessage"])
        

        输出

        Error Message with/without params: hello World
        Success Message with/without params:   World!
        Warning Message with/without params:  hello!
        Warning Message with/without params:  !
        

        【讨论】: