【问题标题】:Best way to implement different modes in a python function在 python 函数中实现不同模式的最佳方法
【发布时间】:2018-03-10 16:43:10
【问题描述】:

我想知道实现一个可以在不同模式下运行的函数的最 Pythonic 方式,即根据调用它的模式执行稍微不同的任务。

例如,我有以下基于一组输入索引提取 numpy 数组的子集。通常,我希望将这些索引作为[xMin, xMax, yMin, yMax] 形式的列表或元组传递,但有时我可能希望提供中心点和宽度/高度,如([xCoord, yCoord], [width, height])。 (注意这是空间数据&xMin、yMin等参考数组边界的空间坐标。)

def get_subset(array, *args):
    ## If window extents are given
    if len(args) == 1:
        [(xMin,xMax,yMin,yMax)] = args

        ## Set NULL extents to array bounds
        if xMin is None: xMin = array.xMin
        if xMax is None: xMax = array.xMax
        if yMin is None: yMin = array.yMin
        if yMax is None: yMax = array.yMax

        ## Convert window extents to array indices
        winLx = int(xMin - array.xMin)
        winRx = int(xMax - array.xMin)
        winBy = int(array.yMax - yMin)
        winTy = int(array.yMax - yMax)

    ## If window centroid and dimensions are given
    elif len(args) == 2:
        [(easting,northing),(width,height)] = args

        # Convert input coordinates into array indices
        xCell = int(easting - array.xMin)
        yCell = int(array.yMax - northing)

        # Generate L(eft), R(ight), T(op) and B(ottom) window extents
        winLx = xCell - width//2
        winRx = xCell + width//2
        winBy = yCell + height//2
        winTy = yCell - height//2

    subset = array.data[winTy:winBy, winLx:winRx, :]

有没有更好、更简洁或更 Pythonic 的方式来做到这一点?过去,我曾尝试在我的函数中使用mode 参数,然后使用if 循环来获得我想要的功能(类似于get_subset(array, window, mode='extent')),最终与我上面显示的没有太大不同。但是我想知道是否有一种很好的方法来使用装饰器或其他一些 python 功能来实现这一点。

【问题讨论】:

  • 为什么不使用两个独立的函数?
  • 更 Pythonic 的方式是没有一个函数执行两个不同的事情。
  • SRP - 与 OCP、LSP、ISP 一起出现和DIP:SOLID
  • 我开始怀疑将它压缩到一个函数中是不是一个坏主意。我想我的问题是:是否可以将这两个功能分开,同时将它们合并到一个可以执行我想要的功能的功能中?这似乎是合理的,因为从概念上讲,过程没有区别,只是窗口的构造方式有所不同。这就是为什么我想知道是否有一种方便的方式来使用装饰器,例如处理来自 2 个不同窗口构造方法的输入并将窗口传递给单个子集提取方法 - 或者这仍然是不好的形式?
  • 另外,感谢@PatrickArtner 提供的链接,它很有帮助。

标签: python


【解决方案1】:

选项 A) 您将函数拆分为两个函数。

get_subset_using_extents(array, extents)
get_subset_using_centroid_and_dimension(array, centroid, dimension)

好处:

  • 直截了当
  • 似乎是您问题的 cmets 中建议的最受欢迎的解决方案

选项 B) 您提出的选项。

def get_subset(array, *args):

    if len(args) == 1:
        return getsubset_using_extents(array, args[0])
        #Or just lay out all of the code here like you had done

    elif len(args) == 2:
        return get_subset_using_centroid_and_dimension(array, arg[0], arg[1])
        #Or just lay out all of the code here like you had done

    else:
        raise TypeError("get_subset() takes either 2 or 3 arguments")

好处:

  • get_subset 比两个长名称的函数更容易记住。

缺点:

  • 您无法使用关键字参数。
  • 读取调用此函数的代码时,不清楚使用的是哪种模式。

选项 C) 特定于模式的项目存储在字典中。

def get_subset(array, mode, options):

    if mode == "extent":
        return getsubset_using_extents(array, options["extent"])

    elif mode == "centroid_dimensions":
        return get_subset_using_centroid_and_dimension(array, options["centroid"], 
                   options["dimensions"])

    else:
        raise SubsetException("Invalid Mode: " + mode)

好处

  • 函数名很容易记住,而且由于调用者需要说明使用的模式,如何获得子集仍然很明显。

  • 允许您在不更改函数签名的情况下轻松添加/更改/删除选项。

  • 强制人们在使用您的函数时显式命名选项键。这在您的情况下并不那么重要,但这种技术可用于防止看起来像 some_function(true, true, false, true) 的函数调用。 (编辑:刚刚发现你也可以这样做this。)


想法

选项 A

因为有两种不同的模式没有任何重叠的选项,所以这是我会选择的选项。

选项 B

我永远不会使用这个选项。对这样的函数进行伪重载并不是 Python 式的事情。

选项 C

这在您的情况下不是必需的,但如果这两种模式有很多选项,其中一些在不同模式之间共享,那么这将是一个值得考虑的好模式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-31
    • 1970-01-01
    相关资源
    最近更新 更多