3

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 that group of methods by importing the module whole or select methods, and turn the imported into a class?

e.g. pseudo-wise

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

so

BunchClass = make_class_from_module(bunch_methods)

Sounds legit in my mind, but how viable is it? How would I begin to do something like this, if I should even, or what are my alternatives?

Why would I want to do this? Right now it is a mental & learning exercise, but my specific use in mind is take methods and create flask-classy FlaskView classes. I'd like to potentially take a grab bag of methods and potentially use & reuse them in differing contexts with FlaskView


Community
  • 1
  • 1
blueblank
  • 4,724
  • 9
  • 48
  • 73
  • 3
    Why do you want this? None of the functions have any state (as far as you've shown), so there really isn't any purpose for having a class here. – mgilson May 23 '13 at 15:11
  • What are you planning to do with this class? – Blender May 23 '13 at 15:12
  • To add to what @mgilson said, a module is already an instance of a class of type `module`. What would the value be of wrapping or mutating that container class of stateless functions with or into another container class? – Silas Ray May 23 '13 at 15:17
  • I don't see how `flask-classy` is a reason to do that. I doubt that is implemented with something like this. – Bakuriu May 23 '13 at 15:26
  • `bunch_methods.py` doesn't contain methods, just functions, so at best they could be turned into static methods of an instance of some class. They would already be contained in a module called `bunch_methods` if you `import`ed, them, so what advantage would be gained by making them part a class? – martineau May 23 '13 at 15:45
  • Why not? Suppose I don't know beforehand what FlaskViews I'm going to make, suppose I need to aggregate some common methods to dispatch to different flaskviews in different blueprints, etc. I can see some need to do things dynamically. – blueblank May 23 '13 at 15:51
  • http://www.ianlewis.org/en/dynamically-adding-method-classes-or-class-instanc would seem to indicate that there is a way to add functions to classes. – blueblank May 23 '13 at 15:53
  • Sure it can be done. Notice that the `new_method()` function added has a `self` parameter which it used to reference another attribute of the instance? It thus has the makings of a true method. – martineau May 23 '13 at 15:58
  • 1
    After thinking awhile why anyone would want to do this, it dawned on me that turning a module into a class allows class inheritance. The pattern may not be recommendable, but it certainly is an option. – Noctis Skytower Mar 10 '16 at 16:04

4 Answers4

2

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()
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
1

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.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
1

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?

Community
  • 1
  • 1
yardsale8
  • 940
  • 9
  • 15
0

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)
BrtH
  • 2,610
  • 16
  • 27
  • This is wrong. The OP wants to transform a module into a new class. Your code transforms it into an *instance* of a class. – Bakuriu May 23 '13 at 15:23
  • @Bakuriu That's true, but depending on the situation of the OP it is not necessarily a problem. In your answer you eventually make a instance too. If the OP doesn't need an instance, than why doesn't he just use the original module? But yes, you are correct. – BrtH May 23 '13 at 15:29