Python 新特性

3.8

"""
新特性1:海象运算符
"""

a = [1, 2, 3, 4, 5, ]
if (n := len(a) )> 5:
    print(f"List len is too long ({n} elements, expected <= 10)")
else:
    print(f"List len <=5")


"""
新特性2:
1.增加一个 /,使a,b 必须使用位置参数调用,不能使用关键字参数调用
2.c,d 随便
3.e,f 必须使用关键字参数
"""

def test(a, b, /, c, d, *, e, f):
    print(f"a={a}, b={b}, c={c}, d={d}, e={e}, f={f}")

3.9

Python 3.9 支持使用 | 和|= 合并映射,这不难理解,因为二者也是并集运算符

使用 | 合并映射

>>> d1 = {'a':1,'b':3 }
>>> d2 = {'a':2,'b':4,'c':6}
>>> d1 | d2
{'a': 2, 'b': 4, 'c': 6}
>>> d1
{'a': 1, 'b': 3}
>>> d1 |= d2
>>> d1   # 就地更新现有映射
{'a': 2, 'b': 4, 'c': 6}
>>>

使用模式匹配处理映射

def get_creators(record: dict) -> list:
    match record:
        case {'type': 'book', 'api': 2, 'authors': [*names]}:  #1 names是一个列表,authors也是一个列表,*names将列表拆包装入authors中
            return names 
        case {'type': 'book', 'api': 1, 'author': name}: #2
            return [name]
        case {'type': 'book'}: #3
            raise ValueError(f'Invalid "book" record:{record!r}')
        case {'type': 'movie', 'director': name}: #4
            return [name]
        case _: #5
            raise ValueError(f'Invaild record:{record!r}')


if __name__ == '__main__':
    b1 = dict(api=1,author='Douglas Hofstadter',type='book',title='Godel,Escher,Bach')
    print(get_creators(b1)) # ['Douglas Hofstadter']

    from collections import OrderedDict
    
    b2 = OrderedDict(api=2,type='book',title='Python in a Nutshell',authors='Martelli Ravenscroft Holden'.split())
    print(get_creators(b2)) # ['Douglas Hofstadter']   # ['Martelli', 'Ravenscroft', 'Holden']

    print(get_creators({'type': 'book', 'pages': 770}))

    '''
    # out:
    Traceback (most recent call last):
      File "E:\PyProject\pytestDemo\demo2.py", line 57, in <module>
        print(get_creators({'type':'book','pages':770}))
      File "E:\PyProject\pytestDemo\demo2.py", line 43, in get_creators
        raise ValueError(f'Invalid "book" record:{record!r}')
    ValueError: Invalid "book" record:{'type': 'book', 'pages': 770}
    '''

    print(get_creators('Spam,spam,spam'))

    '''
    # out:
    Traceback (most recent call last):
      File "E:\PyProject\pytestDemo\demo2.py", line 69, in <module>
        print(get_creators('Spam,spam,spam'))
      File "E:\PyProject\pytestDemo\demo2.py", line 47, in get_creators
        raise ValueError(f'Invaild record:{record!r}')
    ValueError: Invaild record:'Spam,spam,spam'
    '''

1.匹配含有'type':'book','api':2,而且'authors'健映射一个序列的映射对象。以列表形式返回序列中的项
2.匹配含有'type':'book','api':1,而且'authors'健映射任何对象的映射对象,以列表形式返回匹配的对象
3.其他含有'type':'book'的映射均无效,抛出ValueError
4.匹配含有'type':'movie',而且'director'映射单个对象的映射对象,以列表形式返回匹配的对象
5.其他匹配对象均无效,抛出ValueError

通过上面案例可以处理半结构化数据(例如JSON记录)
注意模式中健的顺序无关紧要,即使b2是一个OrderDict,也能作为匹配对象

3.10

序列模式匹配

假设你在设计一个机器人,它接受以文字和数值序列的形式发送命令,例如 BEEPER 440 3。经过拆分和解析后,得到的消息['BEEPER',440,3]。可以使用如下的方法处理
这样的消息

def handle_command(self,message):
    match message: #1
        case ['BEEPER', frequency,times]: #2
            self.beep(times,frequency)
        case ['NECK',angle]: #3
            self.rotate_neck(angle)
        case ['LED', ident,intensity]: #4
            self.leds[ident].set_brightness(ident,intensity)
        case ['LED',ident,red,green,blue]: #5
            self.leds[ident].set_color(ident,red,green,blue)
        case _: #6
            raise InvalidCommand(message)

1.match 关键字后面的表达式是匹配对象,即各个case子句中的模式尝试匹配的数据。
2.这个模式匹配一个含有3个项的序列。第一个必须是字符串‘BEEPER’。第二个项和第三个项任意,依次绑定到变量frequency和times上
3.这个模式匹配任何含有两项,而且第一项为‘NECK’的序列
4.这个模式匹配第一项为'LED',共有3项的序列。如果项数不匹配,Python继续执行下个case子句
5.这个模式也匹配第一项为‘LED’的序列,不过一共有5个项
6.这是默认的case子句,前面所有模式都不匹配时执行。

match/case 与C语言 中的swtich/case 语句很像,但这的只是表象,与swtich相比,match的一大改进是支持析构,这是一种高级的拆包形式

案例2:

metro_areas = [
    ('Tokyo','JP',36.933,(35.5555585,139.691667)),
    ('Delhi NCP','IN',21.935,(28.613889,77.208889)),
    ('Mexico City','MX',20.142,(19.433333,-99.133333)),
    ('New York-Newark','US',20.104,(40.808611,-74.020386)),
    ('Sao Paulo','BR',19.649,(-23.547778,-46.635833))
]

def main():
    for record in metro_areas:
        match record:
            case [name,_,_,(lat,lon)] if lon <=0:  #1
                print(name,lat,lon)

if __name__ == '__main__':
    main()



"""
# out:
Mexico City 19.433333 -99.133333
New York-Newark 40.808611 -74.020386
Sao Paulo -23.547778 -46.635833
"""

1.一个case子句由两部分组成,一部分是模式,一部分是使用if 关键字指定的卫语句(guard clause 可选)

一般来说,匹配对象同时满足以下条件方能匹配序列模式

  • 匹配对象是序列
  • 匹配对象和模式的项数相等
  • 对应的项相互匹配,包括嵌套的项

例如:模式[name,,,(lat,lon)] 匹配一个含有4个项的序列,而且最后一个项必须是一个含有两个项的序列。

序列模式可以些成元组或列表,或者任意形式的嵌套元组和列表,使用哪种句法都没有区别,因为在序列模式中,方括号
和圆括号的意思是一样的。上面的案例中,模式写成列表形式,其中嵌套的序列写成元组形式,这样做只是为了避免重复
使用方括号或圆括号

注意:
在match/case 的上下文中,str,bytes,bytearray 实例不作为序列处理

与拆包不同,模式不析构序列以外的可迭代对象(如迭代器)
模式中的任何一部分均可使用as关键字绑定到变量上
case [name,_,_,(lat,lon) as coord]: => 可以匹配['Shanghai','CN',24.9,(31.1,121.3)]并设定以下变量:

  • name: 'Shanghai'
  • lat: 31.1
  • lon: 121.3
  • coord: (31.3,121.3)

添加类型信息可以让模式更具体

metro_areas = [
    ('Tokyo', 'JP', 36.933, (35.5555585, 139.691667)),
    ('Delhi NCP', 'IN', 21.935, (28.613889, 77.208889)),
    ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
    ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
    ('Sao Paulo', 'BR', 19.649, (-23, -46))
]


def main():
    for record in metro_areas:
        match record:
            case [str(name), _, _, (float(lat), float(lon))] if lon <= 0:  #lat 只匹配float类型
                print(name, lat, lon)


if __name__ == '__main__':
    main()
"""
Mexico City 19.433333 -99.133333
New York-Newark 40.808611 -74.020386
"""

case [str(name), _, _, (float(lat), lon)],在模式上下文中,这种句法的作用是在运行时检查类型。前面的模式将匹配一个4项序列,其中第一个
必须是字符串,第四项必须是一对浮点数。而且第一项中的字符串将绑定到name变量上,第四项中的一对浮点数将分别绑定到lat和lon两个变量上

如果想要匹配任何一个以字符串开头,以嵌套两个浮点数的序列结尾的序列,可以使用如下模式:
case [str(name),_*,(float(lat),float(lon))]
_ 匹配任意数量的项,而且不绑定变量。
如果把
_,更换为*extra,匹配的零项或多项将作为列表绑定到extra变量上

posted @ 2022-01-24 21:12  chuangzhou  阅读(29)  评论(0编辑  收藏  举报