【问题标题】:Python slice how to distinguish slice arguments and default arguments (e.g., x[i:] vs. x[i:None])?Python slice 如何区分切片参数和默认参数(例如,x[i:] vs. x[i:None])?
【发布时间】:2016-05-12 19:42:48
【问题描述】:

假设variable = None,如何以编程方式区分以下切片语法(效果相同)?

obj[variable:]
obj[variable:None]

obj[:variable]
obj[None:variable]

obj[:]
obj[::]
obj[None:None]
obj[None:None:None]

我正在尝试自定义我的类的__getitem__ 方法以[ab]使用切片语法进行高级索引(语法糖),并希望以不同的方式处理不同的切片语法,因为我需要很多语法糖......(这可能不是一个好习惯,但只是为了实验......)。

def __getitem__(self, slice_obj):
    # slice_obj will be `slice(None, None, None)` for all 
    # `obj[variable:]`, `obj[:variable]` and `obj[:]`

我们可以看到使用dis库有不同的切片操作码:

https://docs.python.org/2/library/dis.html#opcode-SLICE+0

SLICE+0() 实现 TOS = TOS[:]。

SLICE+1() 实现 TOS = TOS1[TOS:]。

SLICE+2() 实现 TOS = TOS1[:TOS]。

SLICE+3() 实现 TOS = TOS2[TOS1:TOS]。

那么任何人都可以使用一些自省技巧来分辨不同的语法(inspect, dis, ast, 等)吗?

更多问题

如何区分函数中传入的参数和默认参数?例如,

def add(x, delta=1):
    return x + delta

add函数内部,如何判断是add(x)还是add(x, 1)

链接

[我们可以为 slice 使用唯一的、不同的默认参数(除了 None)吗?]

https://docs.python.org/2/c-api/slice.html#c.PySlice_New http://www.boost.org/doc/libs/1_39_0/libs/python/doc/v2/object.html#slice_nil-spec

感谢任何 cmets/解决方法/黑客!

【问题讨论】:

  • 感谢@chepner 指出。我正在尝试使用 slice 作为语法糖,我需要很多糖来表达不同的含义......我已经更新了我的问题以强调这一点。
  • 非常小心地滥用标准语法 - 当您的切片/切块背叛正常使用时,确保您的 API 聪明超过任何用户认知失调,尤其是在刚刚弄清楚切片是什么的新 Python 用户中。此外,您的 API 将如何处理本身就是切片对象的单个参数?

标签: python indexing syntax slice default-arguments


【解决方案1】:

给定一个slice 对象,无法确定用于创建它的索引表达式的类型。从a[:14] 创建的slicea[None:14] 或文字slice(None, 14) 创建的没有区别。

但是,如果您想弄得有点乱(并且很多不可移植),则可以通过查看父框架中的当前操作码来获得一些可靠的结果:

import sys, dis

class Moo(object):
    def __getitem__(self, i):
        parent_frame = sys._getframe().f_back
        accessing_opcode = parent_frame.f_code.co_code[parent_frame.f_lasti]
        return "This __getitem__ was invoked by way of a %s instruction" \
                % dis.opname[ord(accessing_opcode)]

会给出如下结果:

>>> m = Moo()
>>> m[5]
'This __getitem__ was invoked by way of a BINARY_SUBSCR instruction'
>>> m[5:]
'This __getitem__ was invoked by way of a SLICE+1 instruction'
>>> m[:5]
'This __getitem__ was invoked by way of a SLICE+2 instruction'
>>> m[5:5]
'This __getitem__ was invoked by way of a SLICE+3 instruction'
>>> m[:5:]
'This __getitem__ was invoked by way of a BINARY_SUBSCR instruction'
>>> m[1:5:2]
'This __getitem__ was invoked by way of a BINARY_SUBSCR instruction'

如果您的 __getitem__ 方法被显式调用,显然这将不太有用,但也许它会满足您的需求。

【讨论】:

  • 您可能还会遇到跨 Python 版本的问题。
  • 感谢您检查操作码的方法!这是我之前试图检查的。遗憾的是,我们仍然无法区分 slice 对象。正如@PaulMcGuire 所说,我可能不得不重新考虑我的语法设计。
  • 只是为了添加到你的好例子中,m[slice_obj] -> 'This __getitem__ was invoked by way of a BINARY_SUBSCR instruction'm.__getitem__(slice_obj) -> 'This __getitem__ was invoked by way of a CALL_FUNCTION instruction'
【解决方案2】:

第二部分确定函数的调用方式:

import inspect
>>> def add(a, b=0):
...     return a + b
... 
>>> inspect.getargspec(add)
(['a', 'b'], None, None, (0,))
>>> len(inspect.getargspec(add)[0])
2

【讨论】:

  • 谢谢,我会阅读更多关于检查和试验你的方法的内容。
  • 您也可以将参数默认为某个抽象值,检查该值,如果它仍然是抽象的,则将其设置为正确的默认值!如果您有几个参数,这可能会变得很长! def add(a, b=6996420): if b == 6996420: b = 1 return a+b
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
相关资源
最近更新 更多