处理参数以便在Python中进行预处理,如果顺序很重要

处理参数以便在Python中进行预处理,如果顺序很重要,python,function,arguments,Python,Function,Arguments,我想对命名变量的外观顺序做一点改变 我想有一个关于编辑图像的管道,它会对我首先应用的技术产生影响。与其单独调用函数,我仍然希望使用包装器函数 例如: def preprocess(cutoff=None, resize=None, blur=None): .... 现在,如果我调用像preprocess(resize=(100100),cutoff=55)这样的函数,我希望它首先调整大小,然后执行cutoff,尽管当预处理(cutoff=55,resize=(100100))时,反之亦

我想对命名变量的外观顺序做一点改变

我想有一个关于编辑图像的管道,它会对我首先应用的技术产生影响。与其单独调用函数,我仍然希望使用包装器函数

例如:

def preprocess(cutoff=None, resize=None, blur=None):
    ....
现在,如果我调用像
preprocess(resize=(100100),cutoff=55)
这样的函数,我希望它首先
调整大小
,然后执行cutoff,尽管当
预处理(cutoff=55,resize=(100100))时,反之亦然。


实现这一点的最佳方法是什么?

您不能使用关键字参数。如果您像以前一样通过普通参数接受它们,那么唯一的顺序就是参数的顺序。如果您通过
**kwargs
接受它们,您将获得一个
dict
(没有订单)

如果你想知道参数的顺序,它们必须是位置参数;真的没办法

然而,可能有更好的设计。例如,为什么不把它们分开呢?然后,与此相反:

newfoo = foo.preprocess(resize=(100, 100), cutoff=55)
…您可以这样做:

newfoo = foo.resize(100, 100).cutoff(55)

或者提供简单的类型,这些类型只对参数进行包装:

newfoo = foo.preprocess(Resize(100, 100), Cutoff(55))
然后您可以让
预处理
通过
*args
获取其参数,并根据
类型(arg)
进行分派


如果你看看你将如何实现这一点,它指出了另一种可能性:

class DumbArgWrapper(object):
    def __init__(self, *args):
        self.args = args

class Resize(DumbArgWrapper): pass
class Cutoff(DumbArgWrapper): pass
class Blur(DumbArgWrapper): pass

class ImageProcessor(object):
    def preprocess(self, *args):
        for arg in args:
            if isinstance(arg, Resize):
                do_resize(*arg.args)
            # etc.
如果您只需将
do_resize
代码移动到
resize
(换句话说,使其成为一种“惰性函数”对象),它会更干净:

class SmartArgWrapper(object):
    def __init__(self, *args):
        self.args = args

class Resize(SmartArgWrapper):
    def __call__(self, target):
        do_resize(target, *self.args)
# etc.

class ImageProcessor(object):
    def preprocess(self, *args):
        for arg in args:
            arg(self)

这样做之后,您就可以制作一个简单的包装器,将任何函数转换为
SmartArtWrapper
,而不是制作一堆子类

然后



关键是,有很多可能性。对于知道你想要什么和为什么的人来说,应该有一种而且只有一种明显的方法来实现……但是对于不知道你想要什么和为什么的人来说,这是不对的。

为什么你希望这是一个单一的功能?这似乎是一个违反直觉的设计,如果我们知道背后的原因,我敢打赌有人会想出一个更好的设计来解决同样的问题。主要原因是我知道大多数情况下会出现默认行为,但我现在发现,也许在某些情况下,我想再次切换顺序。通过这种方式,仍然可以简单地将其称为
预处理(5,(10,10))
,但这个循环仍然不是完全确定的,现在不需要很多长函数名。那么使用
调整大小
方法、
切断
方法、
模糊
方法怎么样,然后是一个
preprocess
方法,它只适用于(非常常见的)情况,当您需要1到3个最常见的顺序时?所以,通常您只需调用
preprocess(5,(10,10))
,但是如果您想向后执行操作,您可以
调整大小(10,10)。切断(5)
?我想这很公平。谢谢分享你的想法。我在想,也许我遗漏了一些明显的东西。嗯,你正在寻找的功能不存在可能并不明显。事实上,如果在
**kwargs
语法之前就已经发明了
orderedict
的当前智能优化实现,那么它可能是可行的(不是很常见,但不是被禁止的,因为为什么要麻烦禁止它?)。例如,在所有支持运算符重载的编程语言中,第三方库设计人员非常喜欢的一件事是使用
运算符将顺序操作链接在一起。因此,在这种情况下,不要使用
preprocess
函数,而是写入
foo | resize(100100)| cutoff(55)
。但是,这可能会导致一些混乱的界面,我怀疑这就是为什么第一方语言设计师不那么容易找到它;-)@SteveJessop:我不会说它在“所有支持操作符重载的编程语言”中都很流行,尤其是在Python中,它不流行,也不惯用。(在C++中,另一方面…)“流行”可能是错误的词。我的意思是,有人总是试图这样做,而不是人们经常这样做。上一次,我发现有几次Python的尝试,我并没有努力寻找。并不像C++中的每个库作者都直接达到<代码> < /C>作为流水线操作符,大多数仍然使用函数调用。但是,它在C++中比Python更常见。@史蒂夫杰索普:当然,只要你能做到,就有人会像写一个C++ -助推lambda风格来娱乐一样。只有傻瓜才会在Python中这样做,但我做到了。:)