简体   繁体   English

使OOP代码为FP的Python装饰器; 好主意还是坏主意?

[英]Python decorator that makes OOP code FP; good or bad idea?

Recently I've been trying to figure out a solution to the 'expression problem' of choosing between implementing my code in OOP or FP (functional programming). 最近,我一直在尝试解决“表达问题”的解决方案,即在OOP或FP(功能性编程)中实现我的代码之间进行选择。 The example I used to illustrate my problem was a Vector2D class. 我用来说明问题的示例是Vector2D类。 I could make a class that contains all the necessary functions for a 2D vector (dot product, magnitude, etc.), or I could make a set of functions that take a 2-tuple representing a vector. 我可以创建一个包含2D向量的所有必需函数(点积,幅度等)的类,也可以创建一组以2元组表示向量的函数。 Which option do I chose? 我选择哪个选项?

To cope with this problem, I thought it might be nice to make a decorator that takes a class's methods and turns them into global functions. 为了解决这个问题,我认为最好制作一个使用类的方法并将其转换为全局函数的装饰器。 This is how I did it: 这是我的方法:

import types

def function(method):
    method._function = True
    return method

def make_functions(cls):
    for key in cls.__dict__:
        method = getattr(cls, key)
        if not isinstance(method, types.FunctionType):
            continue
        if hasattr(method, '_function') and method._function:
            globals()[method.__name__] = method
    return cls

@make_functions
class Vector2D:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Vector(%g, %g)' % (self.x, self.y)

    def __iter__(self):
        for component in self.x, self.y:
            yield component

    def __getitem__(self, key):
        return (self.x, self.y)[key]

    def __setitem__(self, key, val):
        if key == 0:
            self.x = val
        elif key == 1:
            self.y = val
        else:
            print('not cool man')

    def __add__(self, other):
        x = self[0] + other[0]
        y = self[1] + other[1]
        return self.__class__(x, y)
    __radd__ = __add__

    def __sub__(self, other):
        x = self[0] - other[0]
        y = self[1] - other[1]
        return self.__class__(x, y)

    def __rsub__(self, other):
        x = other[0] - self[0]
        y = other[1] - self[1]
        return self.__class__(x, y)

    def __mul__(self, other):
        x = other * self[0]
        y = other * self[1]
        return self.__class__(x, y)
    __rmul__ = __mul__

    @function
    def dot_product(self, other):
        return self[0]*other[1] + self[1]*other[0]

Now, dot_product is not only a method of the Vector2D class, but it is also a global function that takes in two vectors (or vector-like objects). 现在, dot_product不仅是Vector2D类的方法,而且还是一个接受两个向量(或类似向量的对象)的全局函数。 This satisfies both the functional and object-oriented approaches to implementing an object like this. 这满足了实现这样的对象的功能和面向对象的方法。 The only problem I can foresee this approach making is that any class that can be represented as another object like a tuple or a list, must be defined to work in the same ways as the objects which act like it. 我可以预见的是,这种方法的唯一问题是,必须将可以表示为另一个对象(如元组或列表)的任何类定义为以与像其作用的对象相同的方式工作。 This is not so bad for a Vector that can also be a tuple, since all we have to do is define the __getitem__ and __iter__ methods, however I can see this getting wildly out of control for classes that have multiple contrasting implementations 这对于也可以是一个元组的Vector来说还不错,因为我们要做的就是定义__getitem____iter__方法,但是我可以看到,对于具有多个对比实现的类,这变得非常失控

Is this a fair solution to the problem? 这是问题的公平解决方案吗? Is it bad practice or technique? 这是不好的做法还是技巧? Should I solely provide one or the other? 我应该只提供其中一个吗?

Python has a @staticmethod decorator for using class methods without an instantiation of that class. Python有一个@staticmethod装饰器,用于使用类方法而不实例化该类。 Simply annotate a class method with the static method wrapper (note the method now does not take a self reference), and you can call it from the class itself. 只需使用静态方法包装器对类方法进行注释(请注意,该方法现在不具有自我引用),您可以从类本身进行调用。

In your case, for the dot product, simply do: 对于您的情况,对于点积,只需执行以下操作:

class Vector2D():
    # Magic methods here...

    @staticmethod
    def dot_product(a, b):
        return a[0]*b[1] + a[1]*b[0]

Then, simply call Vector2D.dot_product(my_vector1, my_vector2) to use the function from the Vector2D class itself. 然后,只需调用Vector2D.dot_product(my_vector1, my_vector2)即可使用Vector2D类本身的函数。

Assigning class methods to global functions sounds like a very dangerous, buggy, complex, and verbose solution. 将类方法分配给全局函数听起来像是一个非常危险,错误,复杂且冗长的解决方案。 I would avoid it at all costs. 我会不惜一切代价避免它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM