Python3.8已經發布了將近一個月了,距離Python3.0第一個版本發佈也將超過10年了。相信不少人仍是依舊在使用Python2.7版本,想要遷移到最新版本殊不知道怎麼可以快速掌握其中最Amazing的方法。下面這篇文章,我會給你們推薦3.0版本依賴最最新潮的函數和語法,讓大家可以在Review代碼時候「脫穎而出」!python
首先咱們先來說幾個時間點:git
Python2.7正式中止維護時間 2020年1月1日,距今還有1個多月 github
Python3.8正式開始發佈時間 2019年10月14日,距今將近1個多月 算法
從這兩個數字咱們能夠看出,Python3這個大版本已經發展很長的時間了,而距離Python2.7的結束也愈來愈近了。在距離Python2.7中止維護的一年內,不少優秀開源項目都已經中止了對 2.7 的支持,例如到今年 1 月份,NumPy 將中止支持 Python 2;到今年年底,Ipython、Cython 和 Pandas 等等都將陸續中止支持 Python 2。編程
因此,爲了響應號召,順應趨勢。咱們慢慢的向Python3.X去遷移,那咱們如何可以快速的掌握Python3.X版本的精髓呢?下面咱們從幾個有趣的新特性入手,這些特性或方法都是 Python 3 各個版本中新加的,它們相比傳統的 Python 方法,更容易解決實踐中的一些問題。緩存
全部的示例都是在 Python 3.7 的環境下編寫的,每一個特性示例都給出了其正常工做所需的最低的 Python 版本。bash
「如何格式化字符串」這個話題我想是每一個開發者在接觸一門新語言的時候都會去學習的語法,而在Python中格式化語法的方式你們一般都會偏向於【Format】或者 【%S】這兩種方法,操做以下:架構
print("My name is %s" % ('phithon', ))
print("My name is %(name)s" % {'name':'phithon'})
print("My name is {}".format("bob"))
print("My name is {name}".format(name="bob"))
複製代碼
而到了Python3.6版本,推出了新的格式化字符串的靈活方法【f-string】,使用【f-string】編寫的與上面功能相同的代碼是這樣的app
name="bob"
print(f"My name is {name}")
複製代碼
咱們對比這幾種格式化字符串的方法,能夠發現相比於常見的字符串格式符【%S】 或 【Format】 方法,【f-string】 直接在佔位符中插入變量顯得更加方便,也更好理解,關於格式化速度方面能夠參考這個博文看看詳細的解釋。編程語言
從上個特性能夠看出【f-string】 確實很是強大和美觀,而在文件路徑方面,Python遵循了他們的開發理念:萬物皆是對象,因此他們把路徑也單拎出來搞了一個路徑對象庫,也就是一個處理文件路徑的抽象庫【pathlib】。若是你不知道爲何應該使用 【pathlib】,請參閱下面這篇 Trey Hunner 編寫的炒雞棒的博文以及它的後續版本,下面咱們對比同一案例的新舊兩個版本Python的實現:
from glob import glob
file_contents = []
for filename in glob('**/*.py', recursive=True):
with open(filename) as python_file:
file_contents.append(python_file.read())
複製代碼
from pathlib import Path
file_contents = [
path.read_text()
for path in Path.cwd().rglob('*.py')
]s') 複製代碼
如上所示,您能夠read_text對Path對象使用方法和列表理解,將文件內容所有讀入一個新列表中,相比於使用舊版本Python的實現,在語法和美觀上無疑是更加出色!
編程語言有不少類型,靜態編譯型語言和動態解釋型語言的對比是軟件工程中一個熱門的話題,幾乎每一個人對此有本身的見解。在靜態語言中類型標註無疑是讓人又愛又恨,愛的是編譯速度加快,團隊合做中準確瞭解函數方法的入參類型,恨的是Coding時極其繁瑣的標註。不過,標註這種極其符合團隊文化的操做仍是在Python3中被引入,而且很快獲得了人們的喜好。
def print_yes_or_no(codition: str) -> bool:
pass
複製代碼
你們在寫Java或者C語言的時候都會接觸到枚舉這個特性,枚舉也是幫咱們節省了不少時間,也讓咱們的代碼更加美觀。舊版本Python中你們想要實現枚舉的話實現方法五花八門,「八仙過海,各顯神通」,充分發揮了Python的動態語言特性。咱們下面舉些例子:
#利用type自建類的騷操做
def enum(**enums):
return type('Enum', (), enums)
Numbers = enum(ONE=1, TWO=2, THREE='three')
# Numbers.ONE == 1, Numbers.TWO == 2 and Numbers.THREE == 'three'
複製代碼
#利用type自建類的騷操做升級版
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
Numbers = enum('ZERO', 'ONE', 'TWO')
# Numbers.ZERO == 0 and Numbers.ONE == 1
複製代碼
#有帶值到名稱映射的
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
reverse = dict((value, key) for key, value in enums.iteritems())
enums['reverse_mapping'] = reverse
return type('Enum', (), enums)
# Numbers.reverse_mapping['three'] == 'THREE'
複製代碼
# 更有甚者,利用namedtuple實現的
from collections import namedtuple
def enum(*keys):
return namedtuple('Enum', keys)(*keys)
MyEnum = enum('FOO', 'BAR', 'BAZ')
# 帶字符數字映射的,像C/C++
def enum(*keys):
return namedtuple('Enum', keys)(*range(len(keys)))
# 帶字典映射的,能夠映射出各類類型,不侷限於數字
def enum(**kwargs):
return namedtuple('Enum', kwargs.keys())(*kwargs.values())
複製代碼
看過了以上這麼多騷操做,如今Python3給你淨化一下眼睛,Python3.4新推出經過「Enum」類編寫枚舉的簡單方法。
from enum import Enum, auto
class Monster(Enum):
ZOMBIE = auto()
WARRIOR = auto()
BEAR = auto()
print(Monster.ZOMBIE)
for i in Monster:
print(i)
#Monster.ZOMBIE
#Monster.ZOMBIE
#Monster.WARRIOR
#Monster.BEAR
複製代碼
以上咱們能夠看出枚舉是符號名稱(成員)的集合,這些符號名稱與惟一的常量值綁定在一塊兒。在枚舉中,能夠經過標識對成員進行比較操做,枚舉自己也能夠被遍歷。
緩存是你們在開發中都會用到的一個特性,若是咱們準確的使用好它,它會節省咱們不少時間和成本。相信不少人初學Python裝飾器的時候都會去實現一個緩存的裝飾器來節省斐波那契函數的計算時間。而Python 3 以後將 LRU(最近最少使用算法)緩存做爲一個名爲「lru_cache」的裝飾器,使得對緩存的使用很是簡單。
下面是一個簡單的斐波那契函數,咱們知道使用緩存將有助於該函數的計算,由於它會經過遞歸屢次執行相同的工做。
import time
def fib(number: int) -> int:
if number == 0:
return 0
if number == 1:
return 1
return fib(number-1) + fib(number-2)
start = time.time()
fib(40)
print(f'Duration: {time.time() - start}s')
# Duration: 30.684099674224854s
複製代碼
咱們看到,咱們沒用緩存裝飾器的時候計算的時間是30秒左右,如今,咱們可使用「lru_cache」來優化它(這種優化技術被稱爲「memoization」)。經過這種優化,咱們將執行時間從幾秒下降到了幾納秒。
from functools import lru_cache
@lru_cache(maxsize=512)
def fib_memoization(number: int) -> int:
if number == 0:
return 0
if number == 1:
return 1
return fib_memoization(number-1) + fib_memoization(number-2)
start = time.time()
fib_memoization(40)
print(f'Duration: {time.time() - start}s')
# Duration: 6.866455078125e-05s
複製代碼
能夠看出,咱們在開發計算函數的時候使用緩存裝飾器是多麼提升成本的一種手段,另外,在新版本Python3.8以後,lru_cache如今可直接做爲裝飾器而不是做爲返回裝飾器的函數。 所以這兩種寫法如今都被支持:
@lru_cache
def f(x):
...
@lru_cache(maxsize=256)
def f(x):
...
複製代碼
Python解包相信在咱們初學Python的時候都有所瞭解,若是咱們不少地掌握這個特性,相信是一件很是酷的事情。那什麼是擴展的解包呢?咱們能夠從pep3132中瞭解更多,舉個例子:
# Python 3.4 中 print 函數 不容許多個 * 操做
>>> print(*[1,2,3], *[3,4])
File "<stdin>", line 1
print(*[1,2,3], *[3,4])
^
SyntaxError: invalid syntax
>>>
複製代碼
# 再來看看 python3.5以上版本
# 可使用任意多個解包操做
>>> print(*[1], *[2], 3)
1 2 3
>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}
複製代碼
咱們能夠看到,解包這個操做也算的上Python中極其潮流的玩法了,耍的一手好解包,真的會秀翻全場啊!
Python 3.7 引入了【data class】,新特性大大簡化了定義類對象的代碼量,代碼簡潔明晰。經過使用@dataclass裝飾器
來修飾類的設計,能夠用來減小對樣板代碼的使用,由於裝飾器會自動生成諸如「__init__()
」和「__repr()__
」這樣的特殊方法。在官方的文檔中,它們被描述爲「帶有缺省值的可變命名元組」。
from dataclasses import dataclass
@dataclass
class DataClassCard:
rank: str
suit: str
#生成實例
queen_of_hearts = DataClassCard('Q', 'Hearts')
print(queen_of_hearts.rank)
print(queen_of_hearts)
print(queen_of_hearts == DataClassCard('Q', 'Hearts'))
#Q
#DataClassCard(rank='Q', suit='Hearts')
#True
複製代碼
而常規的類,按照Python 3.7以前的語法相似於這樣
class RegularCard
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
queen_of_hearts = RegularCard('Q', 'Hearts')
print(queen_of_hearts.rank)
print(queen_of_hearts)
print(queen_of_hearts == RegularCard('Q', 'Hearts'))
#'Q'
#<__main__.RegularCard object at 0x7fb6eee35d30>
#False
複製代碼
雖然這種寫法並無使用更多的代碼量,可是咱們很容易看到爲了初始化,僅僅只是爲了初始化一個對象,rank和suit已經重複了三次。此外,若是你試圖使用這個RegularCard類,你會注意到對象的表示不是很具描述性,而且已有的類與新聲明的類是沒法比較是否相同的。由於每次聲明都會使用一個新的內存地址,而「==」不止比較類存儲的信息,還比較內存地址是否相同。
dataclass還在底層給咱們作了更多的有用的封裝。默認狀況下dataclass實現了__repr__
方法,能夠很好的提供字符串表示;也是了__eq__
方法,能夠作基本的對象比較。而若是RegularCard想實現上面的功能須要寫大量的聲明,代碼量多的嚇人。
class RegularCard(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __repr__(self):
#能夠將類的信息打印出來
return (f'{self.__class__.__name__}'
f'(rank={self.rank!r}, suit={self.suit!r})')
#你們能夠試着將「!r」去掉或者將其中的r改變爲s或a,看看輸出結果會有什麼變化
#conversion character: expected 's', 'r', or 'a'
def __eq__(self, other):
#能夠比較類是否相同(不考慮內存地址)
if other.__class__ is not self.__class__:
return NotImplemented
return (self.rank, self.suit) == (other.rank, other.suit)
複製代碼
一種組織 Python 代碼文件的方式是將它們封裝在程序包中(包含一個「init.py」的文件夾)。下面是官方文檔提供的示例。
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py .
..
effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
複製代碼
在 Python 2 中,上面每一個文件夾都必須包含將文件夾轉化爲 Python 程序包的「init.py」文件。在 Python 3 中,隨着隱式命名空間包的引入,這些文件再也不是必須的了。
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
equalizer.py
vocoder.py
karaoke.py
...
複製代碼
正若有些人說的那樣,這項工做並無像這篇文章說的那麼簡單,官方文檔「PEP 420 Specification」指出,常規的程序包仍然須要「init.py」,把它從一個文件夾中刪除會將該文件夾變成一個本地命名空間包,這會帶來一些額外的限制。本地命名空間包的官方文檔給出了一個很好的示例,而且明確指出了全部的限制。
上面給出的幾個很潮流的特性可能並非很全,更多的還須要你們去探索符合本身和團隊的玩法,這篇文章只是向你們展現一些比較好玩的Python新功能,掌握它能夠幫助你寫出更加Pythonic的代碼。
前兩年在二線大廠工做,目前在創業公司搬磚
接觸方向是爬蟲和雲原生架構方面
有豐富的反爬攻克經驗以及雲原生二次開發經驗
其餘諸如數據分析、黑客增加也有所涉獵
作過百餘人的商業分享以及屢次開辦培訓課程
目前也是CSDN博客專家和華爲雲享專家
往期精彩回顧
深刻理解Python的TLS機制和Threading.local()
下一代容器架構已出,Docker何去何處?看看這裏的6問6答!!
公衆號內回覆「私藏資料」便可領取爬蟲高級逆向教學視頻以及多平臺的中文數據集