【问题标题】:Python function pointers within the same Class同一类中的 Python 函数指针
【发布时间】:2013-12-21 07:48:38
【问题描述】:

我在 Python 中有以下类,我试图用它来通过具有可用函数指针的字典调用一组它自己的方法:

class Test():

    functions = {
        'operation_a' : Test.function_a;
        'operation_b' : Test.function_b;
    }

    def run_operations(operation, *args, **kwargs):

        try:
            functions[str(operation)](self, args, kwargs)
        except KeyError:
            // some log ...

    def function_a(self, *args, **kwargs):
        print A

    def function_b(self, *args, **kwargs):
        print B

第一种方法似乎不正确,因为 Python 解释器找不到类“Test”(NameError:“Test”未定义)。我找不到方法(导入包、包和模块,from package.module import *...等)因此,我有 3 个解决方案可以解决这个问题:

  1. 在类构造函数中定义操作字典 (__init__()),
  2. 将可调用函数移至不同的类(在我的情况下,该类位于不同的模块中,我没有尝试使用同一模块中的类),
  3. 在同一个类中定义函数为@staticmethod

但是,我仍然不知道为什么最初的方法似乎不正确。因此,如何在实例化之前引用同一类中的函数?

【问题讨论】:

    标签: python function class function-pointers


    【解决方案1】:

    类对象在类语句主体结束之前不存在。但是这些函数在 def 语句主体之后的命名空间 中可用。所以你想要的是:

    class Test(object):
    
        def run_operations(self, operation, *args, **kwargs):
            try:
                function = self.functions[operation]
            except KeyError:
                # some log ...
            else:
                function(self, args, kwargs)
    
        def function_a(self, *args, **kwargs):
            print "A"
    
        def function_b(self, *args, **kwargs):
            print "B"
    
        functions = {
            'operation_a' : function_a,
            'operation_b' : function_b,
            }
    

    编辑: 正如 alko 所提到的,您也可以使用 getattr 与当前实例和方法名称来获取方法,但这意味着所有方法都成为潜在的“操作”,这都是不明确的和潜在的安全问题。仍然使用 getattr 并明确“选择”合法操作的一种方法是向相关函数添加一个标记,即:

    def operation(func):
        func.is_operation = True
        return func
    
    class Test(object):
        def run_operations(self, operation, *args, **kwargs):
            method = getattr(self, operation, None)
            if method is None:
                # raise or log or whatever
            elif not method.is_operation:
                # raise or log or whatever
            else:
                method(*args, **kwargs)
    
        @operation
        def operation_a(self, *args, **kwargs):
            print "A"
    
        @operation
        def operation_b(self, *args, **kwargs):
            print "B"
    
        def not_an_operation(self):
            print "not me"
    

    另一种解决方案是使用内部类作为操作的命名空间,即:

    class Test(object):
    
        def run_operations(self, operation, *args, **kwargs):
            method = getattr(self.operations, operation, None)
            if method is None: 
                # raise or log or whatever
            else:
                method(self, *args, **kwargs)
    
        class operations(object):
            @classmethod
            def operation_a(cls, instance, *args, **kwargs):
                print "A"
    
            @classmethod
            def operation_b(cls, instance, *args, **kwargs):
                print "B"
    

    还有其他可能的解决方案。哪个是“最好的”取决于您的需求,但除非您正在构建一个框架,否则基于 dict 的框架将尽可能简单、可读和有效。

    【讨论】:

      【解决方案2】:
      class Test():
      
      
          def run_operations(operation, *args, **kwargs):
      
              try:
                  functions[str(operation)](self, args, kwargs)
              except KeyError:
                  // some log ...
      
          def function_a(self, *args, **kwargs):
              print A
      
          def function_b(self, *args, **kwargs):
              print B
      
          functions = {
              'operation_a' : function_a, #now you can reference it since it exists
              'operation_b' : function_b, #you do not prefix it with class name
          }
      

      【讨论】:

      • 那么,最后,函数在类中的位置是相关的,因为 Python 是一种脚本语言......这是正确的吗?我的意思是,解释器必须从上到下读取所有类语句,然后才能将函数名包含在命名空间中。
      • 错误结论...语句的相对位置是相关的,因为所有 Python 语句都是可执行的,但这并不意味着“解释器必须在函数之前从上到下读取所有类语句名称包含在命名空间中”。如果代码首先编译为字节码,那么所有顶级语句都会被执行。 class 语句的执行意味着创建一个命名空间,在这个命名空间中执行所有的语句——包括def 语句,然后从这个命名空间创建一个class 对象并将这个类对象绑定到包含的命名空间中。
      • 谢谢布鲁诺,这比我愿意输入的要多,所以我只是想我会让他有一个稍微错误的结论,只要它能让他工作代码:P
      【解决方案3】:

      你真的需要字典吗?

      getattr(Test(), 'function_a')
      <bound method Test.function_a of <__main__.Test object at 0x019011B0>>
      

      所有实例方法:

      >>> import inspect
      >>> dict(filter(lambda x: inspect.ismethod(x[1]), inspect.getmembers(Test())))
      {'run_operations': <bound method Test.run_operations of <__main__.Test object at 0x01901D70>>, 'function_b': <bound method Test.
      function_b of <__main__.Test object at 0x01901D70>>, 'function_a': <bound method Test.function_a of <__main__.Test object at 0x0
      1901D70>>}   
      

      【讨论】:

      • 我更喜欢将它与 dict 一起使用,以便之后扩展此类并更改新子类的允许操作。此外,我发现以这种方式使用 dict 比使用检查方法容易得多,因为如果您在“run_operations”函数中包含不可“调用”的附加函数,您将如何通过使用来区分它们这种内省的方法?
      • @Ricardo 我的意思是您可以拥有名称字典,而不是实际功能。
      • @Ricardo:是的,最好对合法的“操作”函数/方法进行一些明确的标记。但是您仍然可以使用基于 getattr 的解决方案,参见我编辑的答案。
      【解决方案4】:

      你根本不能,因为这个类还没有定义。类定义与 Python 中的所有其他内容一样,都是可执行代码,并且在执行定义之前不会将类名分配给命名空间。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-09
        • 2012-01-25
        • 1970-01-01
        相关资源
        最近更新 更多