黑马程序员技术交流社区

标题: 【上海校区】关于f字符串的一些问题研究 [打印本页]

作者: Mr吴    时间: 2018-4-27 12:24
标题: 【上海校区】关于f字符串的一些问题研究
本帖最后由 Mr吴 于 2018-4-27 15:43 编辑

在开始今天的正文之前, 看一下f字符串是怎么回事?
对于一个声称关注于以一个正确的方式去处理大部分情形的语言而言,python里面的字符串格式化正在受着猛烈的冲击,而且每一年都变得更加多种多样。以python 3.6开始,我们有3种方式格式化字符串,(简单的外部连接或者使用string.Template):
使用%操作符
str.format
内插替换字符串

%-格式化
%-格式化至少从版本1.0开始就是python的一部分了,如果你做了用python 3以前的python版本做了一些事后,你就应该知道这些。
"%s%s" % ('Hello', 'Python',)

这只支持有限的类型集,所以在将其传递给字符串格式器之前你必须在将你的自定义对象转化至这些类型集中的一种。
许多年以后,原始的字符串数据类型以一种格式方式而延伸了:
str.format()
这同一些像上下文管理器的东西一并载入了20088月发布的python2.6中。就像在PEP-3101中详细讲的一样,它尝试着去处理一些老式%二进制操作符中的短板,比如%只支持有限的类型集或者实际上处理整个表达式的右侧组成部分,而这会在一些特殊的条件下会导致错误。
>>>"%s" % ("lala",)
'lala'
>>>"%s" % "lala"
'lala'

因为.format是一种方法而不是一种操作符(对应到一种二进制方法),处理的参数更加明确。如果你遇到一个字符串,让它就会被解释成为字符串。如果你遇到一个只包含一个字符串的元组,它就会被解释成一个包含一个字符串的元组:
>>>"{}".format("lala")
'lala'
>>>"{}".format(("lala",))
"('lala',)"

相比于%,它也支持提供给你参数名而不需要使用字典:
"{firstname}{lastname}".format(firstname="Horst",lastname="Gutmann")

开始,这原本打算是作为%操作符的完全代替(计划打击python 3.1中的老式格式化函数),但是这永远都不可能发生了,因为这个字符串格式器的核心特点都与老式的%操作符大部分都是相似的,但就语法稍微有点不一样以及就我看而言更加直观而已。事实上,正是因为如此,Ulrich于是就和我创造了pyformat.info来帮助人们过渡到新的系统。
但是很显然的是,PEP-3101并不会在只消除老式的特点集上就停止脚步,它依旧引入了一个协议来允许与自定义类之间更加通用的交互。
classCountry:
    def __init__(self, name, iso):
        self.name, self.iso = name, iso
    def __format__(self, spec):
        if spec == 'short':
            return self.iso
        return self.name
country =Country("Austria", "AUT")
print("{}".format(country))
print("{:short}".format(country))

你能将format方法想象成为一个str来进行字符串格式化,这样你就能通过各种选项。一旦在你的对象中有format方法,它就会被用来替代str当你使用format-method时(除非你做了一些像"{!s}".format(country)"事情的时候)
实际上你能在python 3.4datatime.date类中找到一个很好地例子去解释怎么使用它。
classdate:
    ...
    def __format__(self, fmt):
        if len(fmt) != 0:
            return self.strftime(fmt)
        return str(self)

这让你在“parent”字符串格式中直接格式化日期,而你不再首先将日期转化成字符串然后再将其传递到字符串格式器中:
importdatetime
print("Todayis {:%A}".format(datetime.datetime.now()))
# Today isThursday

PEP-0498:字符串内插
尽管这就是现在进行字符串格式化的推荐方式,但是.format太繁琐了:
a ="Hello"
b ="World"
"{}{}".format(a, b)
# vs.
"%s%s" % (a, b,)

pep-0498通过提供在其他语言如RubyScala以及Perl中已经普及相当长一段时间的东西:内插字符串,尝试着去改善这种处境。其中表达式能够直接被整合进字符串,而这就意味着你不用明确地调用任何额外的函数。
ES2015把这个特性引入到JavaScript-world中的“recently”中,这被称为模板字符串
constusername = "Horst";
constwelcomeMsg = `Hello, ${username}!`;

python中反引号在python3.0以上版本中已成为历史了,因此它们并不可得。再次引入它们可能会再一次影响python的基本语法。作为代替,另外的文字前缀被引入了:f
a ="Hello"
b ="World"
f"{a}{b}"
f"{a+ ' ' + b}"

你不再需要直接调用一个字符串的.format()方法,但是要简单地用前缀f来标记格式以及内联最终字符串中你想要包括的表达式,不然它们就会被期望着去提供如同你从.format()函数得到的相同功能。这些格式化字符串也在文档中被称为“f字符串
那事实上看起来十分美好,但因为python 3.6会在未来的12个月后才能够发布,你恐怕还要等上一段时间。话虽这么说,代码却已经在那儿了,所以你能做的就是获取一个python 3.6预发布版本或者使用一些像pyenv的小诀窍,然后让它运行就行了。
其实还有很多,这里有其他的PEP0501),它想要引入i-strings而这类字符串导致了字符串的懒惰计算,以至于例如你能在最终评估之前能做一些国际化(i18n)或者安检。虽然这个提案已延缓至进一步的讨论,但这看起来是个很好的想法。
但是回到f-strings:如果你想要去了解更多关于为什么字符串内插解决了它存在的方式,那就请看一看PEP-0502,这里面包括了从其他语言上获得这个特性的灵感以及背后的动机的详细讨论。
接下来继续f字符串的一些小问题的研究
如果替的字符串包含字符'{',那么 f 字符串就会报错
use_input_string= 'Test string {'
print(f'Test- {use_input_string}'.format())

这段代码会报错:
ValueError:Single '{' encountered in format string

那我们是否需要验证用户输入的内容,然后拒绝用户输入的左大括号呢?是否有办法能够避免这个错误呢?
先不要向下翻,自己想一想这个问题?
先在自己心中想出一个自己觉得对的答案。
或者你实在是想不出来了......                                                               
那么马上要公布答案了哦~~
公布答案
f 字符串是隐式格式化的,也就是说你不需要调用format方法。这里调用format方法,相当于进行了两次格式化。实际上题主想要的正确代码应该是
use_input_string= 'Test string {'
print(f'Test- {use_input_string}')

我们画蛇添足地在 f 字符串后面又增加了一个format调用,就成了对 f 字符串格式化后的结果再次进行格式化了。也就是相当于这句
print('Test- {use_input_string}'.format())

自然地就报错了。
最后,我们用一段代码总结一下本题。
>>>use_input_string = 'Test string {'
>>>print(f'Test - {use_input_string}'.format())
Traceback(most recent call last):
    File "<stdin>", line 1, in<module>
ValueError:Single '{' encountered in format string
>>>print(f'Test - {use_input_string}')
    Test - Test string {   



作者: 吴琼老师    时间: 2018-7-5 16:50

作者: 不二晨    时间: 2018-7-10 14:53
奈斯




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2