Programmatically turn module/set of functions into a Python class

前端 未结 4 1020
时光说笑
时光说笑 2021-01-21 22:28

Suppose I have a file with a bunch methods as bunch_methods.py:

def one(x):
  return int(x)

def two(y)
  return str(y)

Is there a way to take th

相关标签:
4条回答
  • 2021-01-21 22:54

    Here is a simple (but long) one-liner lambda that can do what you want (partially inspired by 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))})
    

    You might find the following function easier to read and the loops easier to see in the comprehensions.

    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))})
    

    The usage is practically the same as Bakuriu's answer as you can see when talking to the interpreter.

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

    Addendum:

    After realizing one of the uses of turning a module into a class, the following example program was written showing how to use the converted module as a base class. The pattern may not be recommendable for common use but does show an interesting application of the concept. The classify function should also be easier to read in the version shown below.

    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()
    
    0 讨论(0)
  • 2021-01-21 23:00

    I don't know why you would want this, as you can already use the module as a class, but anyway:

    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)
    
    0 讨论(0)
  • 2021-01-21 23:09

    No idea why you want to do this, but a simple approach could be:

    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
    

    Usage:

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

    If the module has also some variables, you could enhance it to add non-callable objects to the class too:

    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
    

    However doing more than this becomes really hard and ugly and you must have a really good reason for doing this, otherwise it's wasted effort.

    0 讨论(0)
  • 2021-01-21 23:11

    You can also solve this problem using the type meta-class. The format for using type to generate a class is as follows:

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

    First, we need to rework your functions to take a class as the first attribute.

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

    Save this as bunch_method.py, and now we can construct our class as follows.

    >>> 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'
    

    See the following post for a excellent (and long) guide on meta-classes. What is a metaclass in Python?

    0 讨论(0)
提交回复
热议问题