1. 程式人生 > 遊戲 >騰訊入股《了不起的修仙模擬器》開發商 看好國內單機

騰訊入股《了不起的修仙模擬器》開發商 看好國內單機

很多人在學習了基本的Python語言知識後,就轉入應用階段了,後期很少對語言本身的新變化、新內容進行跟蹤學習和知識更新,甚至連已經發布了好幾年的Python3.6的新特性都缺乏瞭解。

本文列舉了Python3.6、3.7、3.8三個版本的新特性,學習它們有助於提高對Python的瞭解,跟上最新的潮流。

** 一、Python3.6新特性 **

1、新的格式化字串方式

新的格式化字串方式,即在普通字串前新增 f 或 F 字首,其效果類似於str.format()。比如

    name = "red"
    print(f"He said his name is {name}.") 
    # 'He said his name is red.'

相當於:

    print("He said his name is {name}.".format(**locals()))
    

此外,此特性還支援巢狀欄位,比如:

    import decimal
    width = 10
    precision = 4
    value = decimal.Decimal("12.34567")
    print(f"result: {value:{width}.{precision}}") 
    #'result: 12.35'
    

2、變數宣告語法

可以像下面一樣宣告一個變數並指定型別:

    from typing import List, Dict
     
    primes: List[int] = []
    captain: str # 此時沒有初始值
     
    class Starship:
     stats: Dict[str, int] = {}

3、數字的下劃線寫法

允許在數字中使用下劃線,以提高多位數字的可讀性。

    a = 1_000_000_000_000_000  # 1000000000000000
    b = 0x_FF_FF_FF_FF    # 4294967295

除此之外,字串格式化也支援_選項,以打印出更易讀的數字字串:

    '{:_}'.format(1000000)   # '1_000_000'
    '{:_x}'.format(0xFFFFFFFF)  # 'ffff_ffff'

4、非同步生成器

在Python3.5中,引入了新的語法 async 和 await 來實現協同程式。但是有個限制,不能在同一個函式體內同時使用 yield 和
await。Python3.6中,這個限制被放開了,允許定義非同步生成器:

    async def ticker(delay, to):
    """Yield numbers from 0 to *to* every *delay* seconds."""
     for i in range(to):
      yield i
      await asyncio.sleep(delay)
    

5、非同步解析器

允許在列表list、集合set 和字典dict 解析器中使用 async 或 await 語法。

    result = [i async for i in aiter() if i % 2]
    result = [await fun() for fun in funcs if await condition()]

6、新增加模組

標準庫(The Standard Library)中增加了一個新的模組:secrets。該模組用來生成一些安全性更高的隨機數,用於管理passwords,
account authentication, security tokens, 以及related secrets等資料。

7、其他新特性

  • 新的 PYTHONMALLOC 環境變數允許開發者設定記憶體分配器,以及註冊debug鉤子等。
  • asyncio模組更加穩定、高效,並且不再是臨時模組,其中的API也都是穩定版的了。
  • typing模組也有了一定改進,並且不再是臨時模組。
  • datetime.strftime 和 date.strftime 開始支援ISO 8601的時間識別符號%G, %u, %V。
  • hashlib 和 ssl 模組開始支援OpenSSL1.1.0。
  • hashlib模組開始支援新的hash演算法,比如BLAKE2, SHA-3 和 SHAKE。
  • Windows上的 filesystem 和 console 預設編碼改為UTF-8。
  • json模組中的 json.load() 和 json.loads() 函式開始支援 binary 型別輸入。

更多內容參考官方文件: [ What's New In Python 3.6

](https://docs.python.org/3.6/whatsnew/3.6.html)

二、Python3.7新特性

Python 3.7於2018年6月27日釋出,
包含許多新特性和優化,增添了眾多新的類,可用於資料處理、針對指令碼編譯和垃圾收集的優化以及更快的非同步I/O,主要如下:

  • 用類處理資料時減少樣板程式碼的資料類。
  • 一處可能無法向後相容的變更涉及處理生成器中的異常。
  • 面向直譯器的“開發模式”。
  • 具有納秒解析度的時間物件。
  • 環境中預設使用UTF-8編碼的UTF-8模式。
  • 觸發偵錯程式的一個新的內建函式。

1、新增內建函式breakpoint()

使用該內建函式,相當於通過程式碼的方式設定了斷點,會自動進入Pbd除錯模式。

如果在環境變數中設定PYTHONBREAKPOINT=0會忽略此函式。並且,pdb 只是眾多可用偵錯程式之一,你可以通過設定新的
PYTHONBREAKPOINT 環境變數來配置想要使用的偵錯程式。

下面有一個簡單例子,使用者需要輸入一個數字,判斷它是否和目標數字一樣:

    """猜數字遊戲"""
    
    def guess(target):
      user_guess = input("請輸入你猜的數 >>> ")
      if user_guess == target:
        return "你猜對了!"
      else:
        return "猜錯了"
    
    
    if __name__ == '__main__':
      a = 100
      print(guess(a))
    
    

不幸的是,即使猜的數和目標數一樣,列印的結果也是‘猜錯了',並且沒有任何異常或錯誤資訊。

為了弄清楚發生了什麼,我們可以插入一個斷點,來除錯一下。以往一般通過print大法或者IDE的除錯工具,但現在我們可以使用 breakpoint()。

    """猜數字遊戲"""
    
    
    def guess(target):
      user_guess = input("請輸入你猜的數 >>> ")
      breakpoint()  //加入這一行
      if user_guess == target:
        return "你猜對了!"
      else:
        return "猜錯了"
    
    
    if __name__ == '__main__':
      a = 100
      print(guess(a))

在 pdb 提示符下,我們可以呼叫 locals() 來檢視當前的本地作用域的所有變數。(pdb 有大量的命令,你也可以在其中執行正常的Python 語句)

    請輸入你猜的數 >>> 100
    > d:\work\for_test\py3_test\test.py(7)guess()
    -> if user_guess == target:
    (Pdb) locals()
    {'target': 100, 'user_guess': '100'}
    (Pdb) type(user_guess)
    <class 'str'>

搞明白了,target是一個整數,而user_guess 是一個字串,這裡發生了型別對比錯誤。

2、型別和註解

從 Python 3.5 開始,型別註解就越來越受歡迎。對於那些不熟悉型別提示的人來說,這是一種完全可選的註釋程式碼的方式,以指定變數的型別。

什麼是註解?它們是關聯元資料與變數的語法支援,可以是任意表達式,在執行時被 Python 計算但被忽略。註解可以是任何有效的 Python 表示式。

下面是個對比的例子:

    # 不帶型別註解
    def foo(bar, baz):
    # 帶型別註解
    def foo(bar: 'Describe the bar', baz: print('random')) -> 'return thingy':

上面的做法,其實是Python對自身弱型別語言的強化,希望獲得一定的型別可靠和健壯度,向Java等語言靠攏。

在 Python 3.5 中,註解的語法獲得標準化,此後,Python 社群廣泛使用了註解型別提示。

但是,註解僅僅是一種開發工具,可以使用 PyCharm 等 IDE 或 Mypy 等第三方工具進行檢查,並不是語法層面的限制。

我們前面的猜數程式如果新增型別註解,它應該是這樣的:

    """猜數字遊戲"""
    
    
    def guess(target:str):
      user_guess:str = input("請輸入你猜的數 >>> ")
      breakpoint()
      if user_guess == target:
        return "你猜對了!"
      else:
        return "猜錯了"
    
    
    if __name__ == '__main__':
      a:int = 100
      print(guess(a))

PyCharm會給我們灰色的規範錯誤提醒,但不會給紅色的語法錯誤提示。

用註解作為型別提示時,有兩個主要問題:啟動效能和前向引用。

  • 在定義時計算大量任意表達式相當影響啟動效能,而且 typing 模組非常慢
  • 你不能用尚未宣告的型別來註解

typing 模組如此緩慢的部分原因是,最初的設計目標是在不修改核心 CPython 直譯器的情況下實現 typing
模組。隨著型別提示變得越來越流行,這一限制已經被移除,這意味著現在有了對 typing 的核心支援。

而對於向前引用,看下面的例子:

    class User:
      def __init__(self, name: str, prev_user: User) -> None:
        pass
    

錯誤在於 User型別還沒有被宣告,此時的 prev_user 不能定義為 User 型別。

為了解決這個問題,Python3.7 將註解的評估進行了推遲。並且,這項改動向後不相容,需要先匯入annotations,只有到Python
4.0後才會成為預設行為。

    from __future__ import annotations
    
    class User: 
      def __init__(self, name: str, prev_user: User) -> None:
        pass
    
    

或者如下面的例子:

    class C:
      def validate_b(self, obj: B) -> bool:
        ...
    class B:
      ...
    

3、新增dataclasses模組

這個特性可能是 Python3.7以後比較常用的,它有什麼作用呢?

假如我們需要編寫一個下面的類:

    from datetime import datetime
    import dateutil
    
    class Article(object):
      def __init__(self, _id, author_id, title, text, tags=None, 
             created=datetime.now(), edited=datetime.now()):
      self._id = _id
      self.author_id = author_id
      self.title = title
      self.text = text
      self.tags = list() if tags is None else tags
      self.created = created
      self.edited = edited
    
      if type(self.created) is str:
        self.created = dateutil.parser.parse(self.created)
    
      if type(self.edited) is str:
        self.edited = dateutil.parser.parse(self.edited)
    
      def __eq__(self, other):
        if not isinstance(other, self.__class__):
          return NotImplemented
        return (self._id, self.author_id) == (other._id, other.author_id)
    
      def __lt__(self, other):
        if not isinstance(other, self.__class__):
          return NotImplemented
        return (self._id, self.author_id) < (other._id, other.author_id)
    
      def __repr__(self):
        return '{}(id={}, author_id={}, title={})'.format(
            self.__class__.__name__, self._id, self.author_id, self.title)
    
    

大量的初始化屬性要定義預設值,可能還需要重寫一堆魔法方法,來實現類例項的列印、比較、排序和去重等功能。

如果使用dataclasses進行改造,可以寫成這個樣子:

    from dataclasses import dataclass, field
    from typing import List
    from datetime import datetime
    import dateutil
    
    @dataclass(order=True)  //注意這裡
    class Article(object):
      _id: int
      author_id: int
      title: str = field(compare=False)
      text: str = field(repr=False, compare=False)
      tags: List[str] = field(default=list(), repr=False, compare=False)
      created: datetime = field(default=datetime.now(), repr=False, compare=False)
      edited: datetime = field(default=datetime.now(), repr=False, compare=False)
    
      def __post_init__(self):
        if type(self.created) is str:
          self.created = dateutil.parser.parse(self.created)
    
        if type(self.edited) is str:
          self.edited = dateutil.parser.parse(self.edited)
    
    

這使得類不僅容易設定,而且當我們建立一個例項並打印出來時,它還可以自動生成優美的字串。在與其他類例項進行比較時,它也會有適當的行為。這是因為dataclasses除了幫我們自動生成
init 方法外,還生成了一些其他特殊方法,如 repr、eq 和 hash 等。

Dataclasses 使用欄位 field來完提供預設值,手動構造一個 field() 函式能夠訪問其他選項,從而更改預設值。例如,這裡將 field
中的 default_factory 設定為一個 lambda 函式,該函式提示使用者輸入其名稱。

    from dataclasses import dataclass, field
    class User:
      name: str = field(default_factory=lambda: input("enter name"))

4、生成器異常處理

在Python
3.7中,生成器引發StopIteration異常後,StopIteration異常將被轉換成RuntimeError異常,那樣它不會悄悄一路影響應用程式的堆疊框架。這意味著如何處理生成器的行為方面不太敏銳的一些程式會在Python
3.7中丟擲RuntimeError。在Python 3.6中,這種行為生成一個棄用警告;在Python 3.7中,它將生成一個完整的錯誤。