Python 3.5中如何使用str.format进行左截断?

3

问:是否可以使用Python 3.5的字符串格式化语法创建格式化字符串并进行左截断?

基本上我想做的是获取git SHA:

"c1e33f6717b9d0125b53688d315aff9cf8dd9977"

仅使用格式字符串,获取并显示右侧8个字符:

"f8dd9977"

我尝试过的方法:

语法错误

>>> "{foo[-8:]}".format(foo="c1e33f6717b9d0125b53688d315aff9cf8dd9977")
>>> "{foo[-8]}".format(foo="c1e33f6717b9d0125b53688d315aff9cf8dd9977")  
>>> "{:8.-8}".format("c1e33f6717b9d0125b53688d315aff9cf8dd9977")

错误的结果

### Results in first 8 not last 8. 
>>> "{:8.8}".format("c1e33f6717b9d0125b53688d315aff9cf8dd9977")

虽然能够工作,但是不灵活且繁琐

### solution requires that bar is always length of 40.
>>> bar="c1e33f6717b9d0125b53688d315aff9cf8dd9977"
>>> "{foo[32]}{foo[33]}{foo[34]}{foo[35]}{foo[36]}{foo[37]}{foo[38]}{foo[39]}".format(foo=bar)

有一个类似的问题被问到了,但从未得到答案。然而我的问题不同之处在于,我只能使用格式化字符串,并且无法更改输入参数的范围。这意味着以下解决方案是不可接受的:

>>> bar="c1e33f6717b9d0125b53688d315aff9cf8dd9977"
>>> "{0}".format(bar[-8:])

我需要澄清的另一个方面是... 上述内容解释了问题最简单的形式。在实际背景下,这个问题更正确地表达为:

>>> import os
>>> "foo {git_sha}".format(**os.environ)

我希望能够左截取 "git_sha" 环境变量。诚然,这比最简单的形式稍微复杂了一些,但如果我能解决最简单的问题,我就能找到解决更复杂问题的方法。


4
如果输入是一个字符串,那么在进行格式化之前有什么阻止你从末尾删除最后的8个字符? - arewm
在Python 3.6中,它将会非常简单,只需要使用 f'{s[-8:]}' 即可。 - Padraic Cunningham
@arewm,基本上我有一个配置文件,它是一个模板,我可以将环境变量替换到标题中。更改环境只是一堆工作...如果能通过格式化解决,那就更容易了。 - Jim
@PadraicCunningham 那很合理...但只适用于Python 3.5 :-( - Jim
@JacquesGaudin 你没理解重点...只是格式化字符串... - Jim
只是随便提一下 - 不确定它是否可行,而且仍然很僵硬/笨重:'{foo[32]}{foo[33]}{foo[34]}{foo[35]}{foo[36]}{foo[37]}{foo[38]}{foo[39]}'.format(foo='{foo:>40}'.format(foo=text)) - Nick
2个回答

1

以下是我的解决方案,感谢 @JacquesGaudin 和#Python社区提供的指导...

class MyStr(object):
    """Additional format string options."""
    def __init__(self, obj):
        super(MyStr, self).__init__()
        self.obj = obj

    def __format__(self, spec):
        if spec.startswith("ltrunc."):
            offset = int(spec[7:])
            return self.obj[offset:]
        else:
            return self.obj.__format__(spec)

当进行以下操作时,这个代码会起作用:

>>> f = {k: MyStr(v) for k, v in os.environ.items()} 
>>> "{PATH:ltrunc.-8}".format(**f)

看起来不错。如果可以的话,我会将偏移量作为正整数传递,并在进行切片时添加减号,但这只是一个细节问题。干得好。 - Jacques Gaudin
我想我会保持原样...有些情况下,我仍然希望进行相同的左截断,但是知道从“str”开头而不是结尾的偏移量。 - Jim

0

可以通过子类化 str 并重写 __format__ 方法来实现:

class CustomStr(str):
    def __format__(self, spec):
        if spec == 'trunc_left':
            return self[-8:]
        else:
            return super().__format__(spec)

git_sha = 'c1e33f6717b9d0125b53688d315aff9cf8dd9977'
s = CustomStr(git_sha)

print('{:trunc_left}'.format(s))

更好的方法是,您可以创建一个自定义的Formatter,它继承自string.Formatter并提供一个format方法。通过这样做,您可以覆盖在格式化字符串过程中使用的许多方法。在您的情况下,您需要覆盖format_field
from string import Formatter

class CustomFormatter(Formatter):
        
     def format_field(self, value, format_spec):
         if format_spec.startswith('trunc_left.'):
             char_number = int(format_spec[len('trunc_left.'):])
             return value[-char_number:]
         return super().format_field(value, format_spec)

environ = {'git_sha': 'c1e33f6717b9d0125b53688d315aff9cf8dd9977'}
fmt = CustomFormatter()
print(fmt.format('{git_sha:trunc_left.8}', **environ))

根据用途,您可以将此放在上下文管理器中并临时隐藏内置的format函数:
from string import Formatter

class CustomFormat:
    
    class CustomFormatter(Formatter):
        
        def format_field(self, value, format_spec):
            if format_spec.startswith('trunc_left.'):
                char_number = int(format_spec[len('trunc_left.'):])
                return value[-char_number:]
            return super().format_field(value, format_spec)
            
    def __init__(self):
        self.custom_formatter = self.CustomFormatter()
            
    def __enter__(self):
        self.builtin_format = format
        return self.custom_formatter.format
        
    def __exit__(self, exc_type, exc_value, traceback):
        # make sure global format is set back to the original
        global format
        format = self.builtin_format

    
environ = {'git_sha': 'c1e33f6717b9d0125b53688d315aff9cf8dd9977'}

with CustomFormat() as format:
    # Inside this context, format is our custom formatter's method
    print(format('{git_sha:trunc_left.8}', **environ))

print(format)  # checking that format is now the builtin function

那么问题可能是,是否可以通过执行str=CustomStrbasestring=CustomStr来重载strCustomStr - Jim
根据 os.environ 的要求编辑了答案。希望这能有所帮助。 - Jacques Gaudin
我选择给@JacquesGaudin答案,因为它是最有帮助的。我不想继承str或dict,但使用他的解决方案来构建自己的答案列表。 - Jim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接