【问题标题】:Programmatically turn module/set of functions into a Python class以编程方式将模块/函数集转换为 Python 类
【发布时间】:2013-05-23 15:09:31
【问题描述】:

假设我有一个包含一堆方法的文件,例如 bundle_methods.py:

def one(x):
  return int(x)

def two(y)
  return str(y)

有没有办法通过导入模块整体或选择方法来获取该组方法,并将导入的方法转换为类?

例如伪明智的

def make_class_from_module(which_module_or_listing_of_methods):
    class = turn_module_to_class(which_module_or_listing_of_methods)
    return class

所以

BunchClass = make_class_from_module(bunch_methods)

在我看来这听起来很合理,但它的可行性如何?如果我应该这样做,我将如何开始做这样的事情,或者我的替代方案是什么?

我为什么要这样做?现在这是一个心理和学习练习,但我的具体用途是采取方法并创建flask-classy FlaskView classes。我想可能会采取一些方法,并可能在 FlaskView 的不同上下文中使用和重用它们


【问题讨论】:

  • 你为什么要这个?没有一个函数有任何状态(就你所展示的而言),所以在这里有一个类真的没有任何目的。
  • 这门课你打算做什么?
  • 补充@mgilson 所说的,模块已经是module 类型的类的实例。将无状态函数的容器类与另一个容器类一起包装或变异有什么价值?
  • 我不明白flask-classy 是这样做的理由。我怀疑这是用这样的东西实现的。
  • 在思考了为什么有人要这样做之后,我突然意识到将模块转换为类允许类继承。该模式可能不值得推荐,但它肯定是一种选择。

标签: python class function methods module


【解决方案1】:

这是一个简单(但很长)的单行 lambda,可以做你想做的事(部分灵感来自 Bakuriu)。

classify = lambda module: type(module.__name__, (), {key: staticmethod(value) if callable(value) else value for key, value in ((name, getattr(module, name)) for name in dir(module))})

您可能会发现以下函数更易于阅读,并且循环在推导中更易于查看。

def classify(module):
    return type(module.__name__, (),
                {key: staticmethod(value) if callable(value) else value
                 for key, value in ((name, getattr(module, name))
                                    for name in dir(module))})

用法实际上与Bakuriu 的答案相同,正如您在与口译员交谈时所看到的那样。

>>> import math
>>> MathClass = classify(math)
>>> MathClass.sin(5)
-0.9589242746631385
>>> instance = MathClass()
>>> instance.sin(5)
-0.9589242746631385
>>> math.sin(5)
-0.9589242746631385
>>> 

附录:

在实现了将模块转换为类的一种用途后,编写了以下示例程序,展示了如何将转换后的模块用作基类。该模式可能不推荐用于常见用途,但确实显示了该概念的有趣应用。 classify 函数在下面显示的版本中也应该更容易阅读。

import math


def main():
    print(Point(1, 1) + Point.polar(45, Point.sqrt(2)))


def classify(module):
    return type(module.__name__, (), {
        key: staticmethod(value) if callable(value) else value
        for key, value in vars(module).items()
    })


class Point(classify(math)):

    def __init__(self, x, y):
        self.__x, self.__y = float(x), float(y)

    def __str__(self):
        return str((self.x, self.y))

    def __add__(self, other):
        return type(self)(self.x + other.x, self.y + other.y)

    @property
    def x(self):
        return self.__x

    @property
    def y(self):
        return self.__y

    @classmethod
    def polar(cls, direction, length):
        radians = cls.radians(direction)
        x = round(cls.sin(radians) * length, 10)
        y = round(cls.cos(radians) * length, 10)
        return cls(x, y)


if __name__ == '__main__':
    main()

【讨论】:

    【解决方案2】:

    您也可以使用type 元类来解决这个问题。使用type生成类的格式如下:

    type(name of the class, 
       tuple of the parent class (for inheritance, can be empty), 
       dictionary containing attributes names and values)
    

    首先,我们需要重新编写函数以将类作为第一个属性。

    def one(cls, x):
        return int(x)
    
    def two(cls, y):
        return str(y)
    

    将其保存为 bundle_method.py,现在我们可以如下构造我们的类。

    >>> import bunch_methods as bm
    >>> Bunch_Class = type('Bunch_Class', (), bm.__dict__)
    >>> bunch_object = Bunch_Class()
    >>> bunch_object.__class__
    <class '__main__.Bunch_Class'>
    >>> bunch_object.one(1)
    1
    >>> bunch_object.two(1)
    '1'
    

    有关元类的优秀(且长)指南请参阅以下帖子。 What is a metaclass in Python?

    【讨论】:

      【解决方案3】:

      不知道你为什么要这样做,但一个简单的方法可能是:

      def to_class(module):
          class TheClass(object): pass
          for attr in dir(module):
              val = getattr(module, attr)
              if callable(val):
                  setattr(TheClass, attr, staticmethod(val))
          return TheClass
      

      用法:

      >>> import math
      >>> Math = to_class(math)
      >>> m = Math()
      >>> m.sin(5)
      -0.9589242746631385
      >>> math.sin(5)
      -0.9589242746631385
      >>> Math.sin(5)
      -0.9589242746631385
      

      如果模块也有一些变量,您可以对其进行增强以将不可调用的对象也添加到类中:

      def to_class(module):
          class TheClass(object): pass
          for attr in dir(module):
              val = getattr(module, attr)
              if callable(val):
                  setattr(TheClass, attr, staticmethod(val))
              else:
                  setattr(TheClass, attr, val)
          return TheClass
      

      但是,做更多的事情变得非常困难和丑陋,你必须有一个非常充分的理由这样做,否则就是白费力气。

      【讨论】:

        【解决方案4】:

        我不知道你为什么想要这个,因为你已经可以将模块用作一个类,但无论如何:

        import bunch_methods as bm
        
        print bm.one('1')
        print bm.two(1)
        
        class BunchClass:
            def __init__(self, methods):
                self.__dict__.update(methods.__dict__)
        
        bc = BunchClass(bm)
        
        print bc.one('2')
        print bc.two(2)
        

        【讨论】:

        • 这是错误的。 OP 想要将一个模块转换为一个新类。您的代码将其转换为一个类的instance
        • @Bakuriu 是的,但根据 OP 的情况,这不一定是问题。在您的回答中,您最终也会创建一个实例。如果 OP 不需要实例,那么他为什么不使用原始模块呢?但是,是的,你是对的。
        猜你喜欢
        • 2013-02-17
        • 1970-01-01
        • 2011-02-06
        • 2022-11-26
        • 2019-05-09
        • 2018-07-03
        • 2011-09-20
        • 2013-02-09
        • 2010-10-27
        相关资源
        最近更新 更多