简体   繁体   English

Python argparse:从参数创建timedelta对象?

[英]Python argparse: Create timedelta object from argument?

I am attempting to use argparse to convert an argument into a timedelta object.我正在尝试使用argparse将参数转换为timedelta对象。 My program reads in strings supplied by the user and converts them to various datetime objects for later usage.我的程序读取用户提供的字符串并将它们转换为各种datetime对象以供以后使用。 I cannot get the filter_length argument to process correctly though.我无法正确处理filter_length参数。 My code:我的代码:

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.

I run the program, passing 1 as the timedelta value (I only want it to be one day):我运行程序,传递1作为timedelta值(我只希望它是一天):

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

But I get the following error:但我收到以下错误:

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

I don't understand why the value is invalid.我不明白为什么该值无效。 If I call the mkdelta function on its own, like this:如果我自己调用 mkdelta 函数,如下所示:

mkdelta(1)
print mkdelta(1)

It returns:它返回:

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

This is exactly the value that I'm looking for.这正是我正在寻找的价值。 Can someone help me figure out how to do this conversion properly using argparse ?有人可以帮我弄清楚如何使用argparse正确进行这种转换吗?

Notice the quotes around '1' in your error message?请注意错误消息中'1'周围的引号? You pass a string to mkdelta, whereas in your test code, you pass an integer.您将一个字符串传递给 mkdelta,而在您的测试代码中,您传递一个整数。

Your function doesn't handle a string argument, which is what argparse is handing it;您的函数不处理字符串参数,这是 argparse 处理它的内容; call int() on it:调用int()就可以了:

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

If you need to support more than days, you'll have to find a way to parse the argument passed in into timedelta arguments.如果您需要支持多天,则必须找到一种方法将传入的参数解析为 timedelta 参数。

You could, for example, support d , h , m or s postfixes to denote days, hours, minutes or seconds:例如,您可以支持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)

Now your mkdelta method accepts more complete deltas, and even integers still:现在您的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)

The default unit is days.默认单位是天。

You could use a custom action to collect all the remaining args and parse them into a timedelta .您可以使用自定义操作来收集所有剩余的参数并将它们解析为timedelta

This will allow you to write CLI commands such as这将允许您编写 CLI 命令,例如

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

You could also provide optional arguments for --days , --seconds , etc, so you can write CLI commands such as您还可以为--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))

这个要点似乎解决了你的问题: https : //gist.github.com/jnothman/4057689

Just in case somebody lands here looking to add a pandas.Timedelta to an argument parser (as I just did), it turns out the following works just fine:以防万一有人想要将pandas.Timedelta添加到参数解析器(正如我刚刚所做的那样),结果证明以下工作正常:

parser = argparse.ArgumentParser()
parser.add_argument('--timeout', type=pd.Timedelta)

Then:然后:

>>> parser.parse_args(['--timeout', '24 hours'])
Namespace(timeout=Timedelta('1 days 00:00:00'))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM