- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
TL;TR 根据简单的规范,寻找习惯用法和模式,将位置参数和关键字参数解包为位置参数的有序序列,例如姓名列表。这个想法似乎类似于 scanf-like 解析。
我正在包装一个名为 someapi
的 Python 模块的函数。someapi
的函数只需要位置参数,在大多数情况下这很痛苦数字。我想让调用者能够灵活地向我的包装器传递参数。以下是我希望允许的包装器调用示例:
# foo calls someapi.foo()
foo(1, 2, 3, 4)
foo(1, 2, 3, 4, 5) # but forward only 1st 4 to someapi.foo
foo([1, 2, 3, 4])
foo([1, 2, 3, 4, 5, 6]) # but forward only 1st 4 to someapi.foo
foo({'x':1, 'y':2, 'z':3, 'r':4})
foo(x=1, y=2, z=3, r=4)
foo(a=0, b=0, x=1, y=2, z=3, r=4) # but forward only x,y,z,r someapi.foo
我认为没有必要支持混合位置参数和关键字参数的复杂情况:
foo(3, 4, x=1, y=2)
这是我第一次尝试为调用 someapi.foo
的 foo
包装器实现此类参数处理:
def foo(*args, **kwargs):
# BEGIN arguments un/re-packing
a = None
kwa = None
if len(args) > 1:
# foo(1, 2, 3, 4)
a = args
elif len(args) == 1:
if isinstance(args[0], (list, tuple)) and len(args[0]) > 1:
# foo([1, 2, 3, 4])
a = args[0]
if isinstance(args[0], dict):
# foo({'x':1, 'y':2, 'z':3, 'r':4})
kwa = args[0]
else:
# foo(x=1, y=2, z=3, r=4)
kwa = kwargs
if a:
(x, y, z, r) = a
elif kwa:
(x, y, z, r) = (kwa['x'], kwa['y'], kwa['z'], kwa['r'])
else:
raise ValueError("invalid arguments")
# END arguments un/re-packing
# make call forwarding unpacked arguments
someapi.foo(x, y, z, r)
据我所知,它按预期完成了工作,但存在两个问题:
someapi
函数要包装,那么如何避免在每个包装器的 BEGIN/END 标记之间复制和调整整个 block ?<我还不知道问题 1 的答案。
然而,这是我尝试解决问题 2 的尝试。
因此,我根据 names
的简单规范为参数定义了一个通用处理程序。names
指定了一些内容,具体取决于实际的包装器调用:
*args
中解压多少个参数? (参见下面的 len(names)
测试)**kwargs
中需要哪些关键字参数? (参见下面的 generator expression 返回元组)这是新版本:
def unpack_args(names, *args, **kwargs):
a = None
kwa = None
if len(args) >= len(names):
# foo(1, 2, 3, 4...)
a = args
elif len(args) == 1:
if isinstance(args[0], (list, tuple)) and len(args[0]) >= len(names):
# foo([1, 2, 3, 4...])
a = args[0]
if isinstance(args[0], dict):
# foo({'x':1, 'y':2, 'z':3, 'r':4...})
kwa = args[0]
else:
# foo(x=1, y=2, z=3, r=4)
kwa = kwargs
if a:
return a
elif kwa:
if all(name in kwa.keys() for name in names):
return (kwa[n] for n in names)
else:
raise ValueError("missing keys:", \
[name for name in names if name not in kwa.keys()])
else:
raise ValueError("invalid arguments")
这允许我以下列方式实现包装函数:
def bar(*args, **kwargs):
# arguments un/re-packing according to given of names
zargs = unpack_args(('a', 'b', 'c', 'd', 'e', 'f'), *args, **kwargs)
# make call forwarding unpacked arguments
someapi.bar(*zargs)
我想我已经实现了我一直在寻找的 foo
版本的所有优点:
为调用者提供所需的灵 active 。
紧凑的表格,减少复制和粘贴。
位置参数的灵活协议(protocol):bar
可以使用 7、8 和更多位置参数或一长串数字调用,但只考虑前 6 个。例如,它将允许迭代处理一长串数字(例如考虑几何坐标):
# meaw expects 2 numbers
n = [1,2,3,4,5,6,7,8]
for i in range(0, len(n), 2):
meaw(n[i:i+2])
回到上面的问题 1,我可以做得更好,让它更 Pythonic 吗?
此外,我想请您审查我的解决方案:您看到任何错误了吗?我忽略了什么吗?如何改进?
最佳答案
Python 是一种非常强大的语言,允许您以任何您想要的方式操作代码,但很难理解您在做什么。为此,您可以使用 inspect
模块。这是一个如何在 someapi
中包装函数的示例。 在这个例子中我只会考虑位置参数,你可以凭直觉知道如何进一步扩展它。你可以这样做:
import inspect
import someapi
def foo(args*):
argspec = inspect.getargspec(someapi.foo)
if len(args) > len(argspec.args):
args = args[:len(argspec.args)]
return someapi.foo(*args)
这将检测提供给 foo
的参数数量是否过多,如果是,它将去除多余的参数。另一方面,如果参数太少,它将什么也不做,让 foo
处理错误。
现在让它更像 pythonic。使用同一模板包装多个函数的理想方法是使用装饰器语法(假设您熟悉该主题,如果您想了解更多信息,请参阅 http://www.python.org/doc 上的文档)。尽管由于 装饰器语法 主要用于正在开发的函数而不是包装另一个 API,我们将制作一个装饰器,但仅将其用作我们 API 的工厂(工厂模式)。为了创建这个工厂,我们将使用 functools
模块来帮助我们(因此包装函数看起来应该如此)。所以我们可以把我们的例子变成:
import inspect
import functools
import someapi
def my_wrapper_maker(func):
@functools.wraps(func)
def wrapper(args*):
argspec = inspect.getargspec(func)
if len(args) > len(argspec.args):
args = args[:len(argspec.args)]
return func(*args)
return wrapper
foo = my_wrapper_maker(someapi.foo)
最后,如果 someapi
有一个相对较大的 API,可以在版本之间更改(或者我们只是想让我们的源文件更加模块化,以便它可以包装任何 API),那么我们可以自动化应用my_wrapper_maker
到模块 someapi
导出的所有内容。我们将这样做:
__all__ = ['my_wrapper_maker']
# Add the entire API of someapi to our program.
for func in someapi.__all__:
# Only add in bindings for functions.
if callable(getattr(someapi, func)):
globals()[func] = my_wrapper_maker(getattr(someapi, func))
__all__.append(func)
这可能被认为是最 pythonic 的实现方式,它充分利用了 Python 的元编程资源,并允许程序员在他们想要的任何地方使用这个 API,而不依赖于特定的 一些api
。
注意:这是否是最惯用的方式来做到这一点真的取决于意见。我个人认为这很好地遵循了“The Zen of Python”中提出的哲学,所以对我来说这是非常地道的。
关于python - 在 Python 中处理灵活的函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27271876/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!