2012-09-17 65 views
4

我试图用​​将参数转换成timedelta对象。我的程序在用户提供的字符串中读取并把它们转换成各种datetime对象以备后用。我无法正确处理filter_length参数。我的代码:Python argparse:从参数创建timedelta对象?

import datetime 
import time 
import argparse 

def mkdate(datestring): 
    return datetime.datetime.strptime(datestring, '%Y-%m-%d').date() 

def mktime(timestring): 
    return datetime.datetime.strptime(timestring, '%I:%M%p').time() 

def mkdelta(deltatuple): 
    return datetime.timedelta(deltatuple) 

parser = argparse.ArgumentParser() 
parser.add_argument('start_date', type=mkdate, nargs=1) 
parser.add_argument('start_time', type=mktime, nargs=1,) 
parser.add_argument('filter_length', type=mkdelta, nargs=1, default=datetime.timedelta(1))#default filter length is 1 day. 

我运行的程序,通过1作为timedelta值(我只希望它是一天):

> python program.py 2012-09-16 11:00am 1 

,但我得到了以下错误:

>>> program.py: error: argument filter_length: invalid mkdelta value: '1' 

我不明白为什么值无效。如果我请自行mkdelta功能,像这样:

mkdelta(1) 
print mkdelta(1) 

它返回:

datetime.timedelta(1) 
1 day, 0:00:00 

这正是我要寻找的价值。有人可以帮我弄清楚如何正确使用​​来做这个转换吗?

回答

4

你的功能不处理字符串参数,这是什么argparse正在把它;呼吁int()它:

def mkdelta(deltatuple): 
    return datetime.timedelta(int(deltatuple)) 

如果需要支持比天以上,你必须找到一种方法来解析传递到timedelta参数的参数。

,你可以,例如,支持dhms后缀表示天,小时,分钟或秒:

_units = dict(d=60*60*24, h=60*60, m=60, s=1) 
def mkdelta(deltavalue): 
    seconds = 0 
    defaultunit = unit = _units['d'] # default to days 
    value = '' 
    for ch in list(str(deltavalue).strip()): 
     if ch.isdigit(): 
      value += ch 
      continue 
     if ch in _units: 
      unit = _units[ch] 
      if value: 
       seconds += unit * int(value) 
       value = '' 
       unit = defaultunit 
      continue 
     if ch in ' \t': 
      # skip whitespace 
      continue 
     raise ValueError('Invalid time delta: %s' % deltavalue) 
    if value: 
     seconds = unit * int(value) 
    return datetime.timedelta(seconds=seconds) 

现在你mkdelta方法接受更全面的三角洲和偶数还是:

>>> mkdelta('1d') 
datetime.timedelta(1) 
>>> mkdelta('10s') 
datetime.timedelta(0, 10) 
>>> mkdelta('5d 10h 3m 10s') 
datetime.timedelta(5, 36190) 
>>> mkdelta(5) 
datetime.timedelta(5) 
>>> mkdelta('1') 
datetime.timedelta(1) 

默认单位是天。

+0

所以,这个工作,但是,它只允许我指定函数的日期。它实际上不支持完整的timedelta表示法:([days [,seconds [,微秒[,毫秒[,分钟[,小时[,星期]]]]]]])。我需要能够传递所有日期时间值作为参数。我会怎么做呢? – user1675914

+0

您必须为此创建文本格式,或使用多个命令行开关,然后将其解析为timedelta对象的参数。 –

+1

为什么你需要支持完整的timedelta符号?在你的要求中,你说你只需要一天的时间。如果你想让你的用户指定更多的标志,你需要传递nargs ='?'。这样mkdelta将会收到一个带有剩余参数的字符串。 –

6

通知各地'1'报价在您的错误信息?你传递一个字符串给mkdelta,而在你的测试代码中,你传递一个整数。

+0

我怎么能把一个非字符串对象作为参数传递? – user1675914

+0

在您的测试代码中,只需键入'mkdelta('1')'。否则,请按照Martijn Pieters的建议,通过将deltatuple转换为int来调整您的代码。 –

+0

另外,我可以建议,如果你这样做,你重命名deltatuple? –

2

你可以使用自定义操作来收集所有剩余的指定参数和把它解析为一个timedelta

这将允许你写的CLI命令如

% test.py 2012-09-16 11:00am 2 3 4 5 
datetime.timedelta(2, 3, 5004) # args.filter_length 

您也可以为--days--seconds等提供可选的参数,所以你可以写CLI命令如

% test.py 2012-09-16 11:00am --weeks 6 --days 0 
datetime.timedelta(42)   # args.filter_length 

% test.py 2012-09-16 11:00am --weeks 6.5 --days 0 
datetime.timedelta(45, 43200) 

import datetime as dt 
import argparse 

def mkdate(datestring): 
    return dt.datetime.strptime(datestring, '%Y-%m-%d').date() 

def mktime(timestring): 
    return dt.datetime.strptime(timestring, '%I:%M%p').time() 

class TimeDeltaAction(argparse.Action): 
    def __call__(self, parser, args, values, option_string = None): 
     # print '{n} {v} {o}'.format(n = args, v = values, o = option_string) 
     setattr(args, self.dest, dt.timedelta(*map(float, values))) 

parser = argparse.ArgumentParser() 
parser.add_argument('start_date', type = mkdate) 
parser.add_argument('start_time', type = mktime) 
parser.add_argument('--days', type = float, default = 1) 
parser.add_argument('--seconds', type = float, default = 0) 
parser.add_argument('--microseconds', type = float, default = 0) 
parser.add_argument('--milliseconds', type = float, default = 0) 
parser.add_argument('--minutes', type = float, default = 0) 
parser.add_argument('--hours', type = float, default = 0) 
parser.add_argument('--weeks', type = float, default = 0) 
parser.add_argument('filter_length', nargs = '*', action = TimeDeltaAction) 

args = parser.parse_args() 
if not args.filter_length: 
    args.filter_length = dt.timedelta(
     args.days, args.seconds, args.microseconds, args.milliseconds, 
     args.minutes, args.hours, args.weeks) 
print(repr(args.filter_length))