这是我第一次在SO中提问,所以如果我不能正确地做到这一点,请不要犹豫地编辑或要求我修改它。
我认为我的问题很普遍,所以我没有找到任何与此主题相关的问题,我感到非常惊讶。如果我错过了这个并且这个问题是重复的,那么如果你能提供一个已经回答的地方的链接,我将非常感激。
想象一下,我需要使用(至少)三个参数来实现一个函数:数组a
,start
索引和end
索引。如果未提供,start
参数应引用数组的第一个位置(start = 0
),而end
参数应设置为最后一个位置(end = len(a) - 1
) 。显然,定义:
def function(a, start = 0, end = (len(a) - 1)):
#do_something
pass
不起作用,导致异常(NameError: name 'a' is not defined
)。有一些解决方法,例如使用end = -1
或end = None
,并在函数正文中有条件地将其分配给len(a) - 1
:
def function(a, start = 0, end = -1):
end = end if end != -1 else (len(a) -1)
#do_something
但我觉得应该有更多的" pythonic"处理这种情况的方法,不仅是数组的长度,还有任何参数,其默认值是另一个(非可选)参数的函数。你会如何应对这种情况?条件分配是最佳选择吗?
谢谢!
答案 0 :(得分:7)
使用None
之类的标记值是典型的:
def func(a, start=0, end=None):
if end is None:
end = # whatever
# do stuff
但是,对于您的实际用例,已经有一种内置的方法可以满足Python启动/停止/步进的方式 - 这使得您的函数提供了关于内置/其他库工作方式的一致界面:
def func(a, *args):
slc = slice(*args)
for el in a[slc]:
print(el)
请参阅https://docs.python.org/2/library/functions.html#slice
如果您只想按顺序支持开始/结束,那么(请注意None
在用作 end len(a)
实际上意味着 0
>或def func(a, start=0, end=None):
return a[start:end]
用作 start 时):
{{1}}
答案 1 :(得分:0)
这是不可能的,函数不能在参数列表中执行,你可以通过参数而不是它们的输出传递函数。
def function(a, start = 0):
end = len(a)
答案 2 :(得分:0)
我不确定你对数组的定义或问题的前提,但根据我的理解,你正在尝试将结束分配到a的长度。如果是这样,只需声明结束参数。像这样:
def function(a, start=0):
end=len(a)
或像你说的那样做条件:
def function(a, start=0, end=False):
if not end:
end = len(a)
或者在调用函数之前简单地声明结束变量并将其传递给参数! 不确定这是否回答了你的问题,但希望它能帮助大声笑!
答案 3 :(得分:0)
根据Function with dependent preset arguments中@NPE提供的答案,使用-1
或(更好)None
作为哨兵值的替代方法是使用对象(命名对象?)即使None
是函数的有效值,也可以使用它。例如:
default = object()
def function(a, start = 0, end = default):
if end is default: end = (len(a) - 1)
return start, end
允许调用:function([1,2,3,4])
,返回(0, 3)
我个人觉得这个解决方案很方便,至少对我自己而言
编辑:如果我们使用last
代替default
,则代码可能更具可读性:
last = object()
def function(a, start = 0, end = last):
if end is last: end = (len(a) - 1)
return start, end
答案 4 :(得分:0)
这必须是我写过的最骇客的代码。我认为它接近你要求的东西(我提出的另一个选择是在函数定义中使用lambda,但它需要一点太多的空间才能成为IMO):
import inspect
from functools import wraps
class defaultArguments(object):
def __init__(self):
self.lazyArgs = []
def initialize(self, func):
lazyArgs, self.lazyArgs = self.lazyArgs, []
@wraps(func)
def functionWrapper(*args, **kw):
if lazyArgs:
argNames, defaults = inspect.getargspec(func)[::3]
argValues = list(args) + [
kw[y] if y in kw else defaults[x]
for x,y in enumerate(argNames[len(args):])
]
oldGlobals = {}
for n,v in zip(argNames, argValues):
try:
oldGlobals[n] = globals()[n]
except:
oldGlobals[n] = None
if v not in lazyArgs:
globals()[n] = v
else:
globals()[n] = kw[n] = eval(v)
for o,v in oldGlobals.items(): globals()[o] = v
return func(*args, **kw)
return functionWrapper
def __call__(self, x):
self.lazyArgs.append(x)
return x
使用:
d = defaultArguments()
@d.initialize
def function1(a, start=d('a[-1]'), end=d('len(a)-1')):
print a, start, end
function1([1,2,8])
>>> [1, 2, 8] 8 2
function1([1,2,8,10], end=1)
>>> [1, 2, 8, 10] 10 1
@d.initialize
def function2(a, b, c, start=d('a*b*c'), end=d('a+b+c+start')):
print a, start, end
function2(2,4,6)
>>> 2 48 60
# Notice that `end` does take the calculated value of `start` into
# account. The logic here is based on what you'd expect to happen
# with normal assignment if the names were each assigned a value
# sequentially: a is evaluated, then b, then c, etc...
我确实为这样做感到内疚,尤其是我使用全局和其他作弊的方式。但是,我认为它可以按照您的要求运作。
不幸的是,你必须编写额外的东西(使用装饰器并且必须在d('')
中包装关键字值),但这是不可避免的,因为Python本身不支持这种情况。
我对语法的含糖部分进行了一些研究。将其缩短为简单的装饰器功能。
def initArgs(func):
@wraps(func)
def functionWrapper(*args, **kw):
argNames, defaults = inspect.getargspec(func)[::3]
for k in kw:
for i in argNames:
if k != i and ('_' + k) == i:
kw['_' + k] = kw[k]
del kw[k]
argValues = list(args) + [
kw[y] if y in kw else defaults[x]
for x,y in enumerate(argNames[len(args):])
]
oldGlobals = {}
for n,v in zip(argNames, argValues):
try:
oldGlobals[n] = globals()[n]
except:
oldGlobals[n] = None
if not n.startswith('_') or n in kw:
globals()[n] = v
else:
globals()[n] = kw[n] = eval(v)
for o,v in oldGlobals.items(): globals()[o] = v
return func(*args, **kw)
return functionWrapper
使用它:
# When using initArgs, the strings associated with the keyword arguments will
# get eval'd only if the name is preceded with an underscore(`_`). It's a
# bit strange and unpythonic for part of a name to have side effects, but then
# again name mangling works with double underscores (`__`) before methods.
# Example:
@initArgs
def function1(a, _start='a[-1]', _end='len(a)-1'):
print a, _start, _end
function1([1,2,8,10])
>>> [1, 2, 8, 10] 10 3
# Removing underscore (`_`) from start
@initArgs
def function2(a, start='a[-1]', _end='len(a)-1'):
print a, start, _end
function1([1,2,8,10])
>>> [1, 2, 8, 10] 'a[-1]' 3
# Outputs a string normally.
在调用者的框架中,参数start
和end
可以使用或不使用下划线,因此稍后在函数定义中更改其名称不会影响调用者。唯一的例外是函数本身,删除下划线(_
)需要在其他地方做同样的事情。