python2遷移python3的問題

使用 pathlib 模塊來更好地處理路徑php

pathlib 是 Python 3默認的用於處理數據路徑的模塊,它可以幫助咱們避免使用大量的 os.path.joins語句:css

from pathlib import Path dataset = 'wiki_images' datasets_root = Path('/path/to/datasets/') train_path = datasets_root / dataset / 'train' test_path = datasets_root / dataset / 'test' for image_path in train_path.iterdir(): with image_path.open() as f: # note, open is a method of Path object # do something with an image
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

向左滑動查看完整代碼html

在Python2中,咱們須要經過級聯字符串的造成來實現路徑的拼接。而如今有了pathlib模塊後,數據路徑處理將變得更加安全、準確,可讀性更強。python

此外,pathlib.Path含有大量的方法,這樣Python的初學者將再也不須要搜索每一個方法:git

p.exists() p.is_dir() p.parts() p.with_name('sibling.png') # only change the name, but keep the folder p.with_suffix('.jpg') # only change the extension, but keep the folder and the name p.chmod(mode) p.rmdir()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用pathlib還將大大節約你的時間。更多功能請查看:github

官方文檔 - https://docs.python.org/3/library/pathlib.html 
參考信息 - https://pymotw.com/3/pathlib/算法

類型提示(Type hinting)成爲Python3中的新成員sql

下面是在編譯器PyCharm 中,類型提示功能的一個示例: 
 
這裏寫圖片描述express

Python 不僅是一門腳本的語言,現在的數據流程還包括大量的邏輯步驟,每一步都包括不一樣的框架(有時也包括不一樣的邏輯)。django

Python3中引入了類型提示工具包來處理複雜的大型項目,使機器能夠更好地對代碼進行驗證。而在這以前,不一樣的模塊須要使用自定義的方式,對文檔中的字符串指定類型 (注意:PyCharm能夠將舊的文檔字符串轉換成新的類型提示)。

下面是一個簡單的代碼示例,利用類型提示功能來處理不一樣類型的數據:

def repeat_each_entry(data): """ Each entry in the data is doubled <blah blah nobody reads the documentation till the end> """ index = numpy.repeat(numpy.arange(len(data)), 2) return data[index]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上述代碼對多維的 numpy.array、astropy.Table 和 astropy.Column、bcolz、cupy、mxnet.ndarray 等操做一樣適用。

這段代碼還可用於 pandas.Series 操做,可是這種形式是錯誤的:

repeat_each_entry(pandas.Series(data=[0, 1, 2], index=[3, 4, 5])) # returns Series with Nones inside
  • 1

這僅僅是一段兩行的代碼。因此,複雜系統的行爲是很是難預測的,有時一個函數就可能致使整個系統的錯誤。所以,明確地瞭解哪些類型方法,並在這些類型方法未獲得相應參數的時候發出錯誤提示,這對於大型系統的運做是頗有幫助的。

def repeat_each_entry(data: Union[numpy.ndarray, bcolz.carray]):
  • 1

若是你有一個很棒的代碼庫,諸如 MyPy這樣的類型提示工具將可能成爲一個大型項目的集成流程中的一部分。不幸的是,類型提示功能還沒辦法強大到爲 ndarrays/tensors 這種細粒度類型發出提示。或許,不久的未來咱們就能夠擁有這樣全面的的類型提示工具,這將成爲數據科學領域須要的強大功能。

從類型提示(運行前)到類型檢查(運行時)

默認狀況下,函數的註釋對於代碼的運行是沒有影響的,它只是幫你指出每段代碼所要作的工做。

在代碼運行階段,不少時候類型提示工具是不起做用的。這種狀況你可使用 enforce 等工具,強制性對代碼進行類型檢查,同時也能夠幫助你調試代碼。

@enforce.runtime_validation def foo(text: str) -> None: print(text) foo('Hi') # ok foo(5) # fails @enforce.runtime_validation def any2(x: List[bool]) -> bool: return any(x) any ([False, False, True, False]) # True any2([False, False, True, False]) # True any (['False']) # True any2(['False']) # fails any ([False, None, "", 0]) # False any2([False, None, "", 0]) # fails
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

函數註釋的其餘用途

正如上面咱們提到的,函數的註釋部分不只不會影響代碼的執行,還會提供能夠隨時使用的一些元信息(meta-information)。

例如,計量單位是科學界的一個廣泛難題,Python3中的astropy包提供了一個簡單的裝飾器(Decorator)來控制輸入的計量單位,並將輸出轉換成相應的單位。

#Python 3 from astropy import units as u @u.quantity_input() def frequency(speed: u.meter / u.s, wavelength: u.m) -> u.terahertz: return speed / wavelength frequency(speed=300_000 * u.km / u.s, wavelength=555 * u.nm) # output: 540.5405405405404 THz, frequency of green visible light
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

若是你須要用Python處理表格類型的科學數據,你能夠嘗試astropy包,體驗一下計量單位隨意轉換的方便性。你還能夠針對某個應用專門定義一個裝飾器,用一樣的方式來控制或轉換輸入和輸出的計量單位。

經過 @ 實現矩陣乘法

下面,咱們實現一個最簡單的機器學習模型,即帶 L2 正則化的線性迴歸 (如嶺迴歸模型),來對比 Python2 和 Python3 之間的差異:

# l2-regularized linear regression: || AX - b ||^2 + alpha * ||x||^2 -> min # Python 2 X = np.linalg.inv(np.dot(A.T, A) + alpha * np.eye(A.shape[1])).dot(A.T.dot(b)) # Python 3 X = np.linalg.inv(A.T @ A + alpha * np.eye(A.shape[1])) @ (A.T @ b)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在 Python3 中,以@做爲矩陣乘法符號使得代碼總體的可讀性更強,且更容易在不一樣的深度學習框架間進行轉譯:由於一些代碼如 X @ W + b[None, :]在 numpy、cupy、pytorch 和 tensorflow 等不一樣庫中都表示單層感知機。

▌使用**做爲通配符

Python2 中使用遞歸文件夾的通配符並非很方便,所以能夠經過定製的 glob2 模塊來解決這個問題。遞歸 flag 在 Python 3.6 中獲得了支持。

import glob # Python 2 found_images = \ glob.glob('/path/*.jpg') \ + glob.glob('/path/*/*.jpg') \ + glob.glob('/path/*/*/*.jpg') \ + glob.glob('/path/*/*/*/*.jpg') \ + glob.glob('/path/*/*/*/*/*.jpg') # Python 3 found_images = glob.glob('/path/**/*.jpg', recursive=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Python3 中更好的選擇是使用 pathlib:(缺乏個import)

# Python 3 found_images = pathlib.Path('/path/').glob('**/*.jpg')
  • 1
  • 2

Python3中的print函數

誠然,print 在 Python3 中是一個函數,使用 print 須要加上圓括弧(),雖然這是個麻煩的操做,但它仍是具備一些優勢:

使用文件描述符的簡單句法:

print >>sys.stderr, "critical error" # Python 2 print("critical error", file=sys.stderr) # Python 3
  • 1
  • 2

在不使用str.join狀況下可以輸出 tab-aligned 表格:

# Python 3 print(*array, sep='\t') print(batch, epoch, loss, accuracy, time, sep='\t')
  • 1
  • 2
  • 3

修改與從新定義 print 函數的輸出:

# Python 3 _print = print # store the original print function def print(*args, **kargs): pass # do something useful, e.g. store output to some file
  • 1
  • 2
  • 3
  • 4

在 Jupyter notebook 中,這種形式可以記錄每個獨立的文檔輸出,並在出現錯誤的時候追蹤到報錯的文檔。這能方便咱們快速定位並解決錯誤信息。所以咱們能夠重寫 print 函數。

在下面的代碼中,咱們可使用上下文管理器來重寫 print 函數的行爲:

@contextlib.contextmanager def replace_print(): import builtins _print = print # saving old print function # or use some other function here builtins.print = lambda *args, **kwargs: _print('new printing', *args, **kwargs) yield builtins.print = _print with replace_print(): <code here will invoke other print function>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可是,重寫print函數的行爲,咱們並不推薦,由於它會引發系統的不穩定。

print函數能夠結合列表生成器或其它語言結構一塊兒使用。

# Python 3 result = process(x) if is_valid(x) else print('invalid item: ', x)
  • 1
  • 2

f-strings 可做爲簡單和可靠的格式化

默認的格式化系統提供了一些靈活性操做。但在數據實驗中這些操做不只不是必須的,還會致使代碼的修改變得冗長和瑣碎。

而數據科學一般須要以固定的格式,迭代地打印出一些日誌信息,所使用的代碼以下:

# Python 2 print('{batch:3} {epoch:3} / {total_epochs:3} accuracy: {acc_mean:0.4f}±{acc_std:0.4f} time: {avg_time:3.2f}'.format( batch=batch, epoch=epoch, total_epochs=total_epochs, acc_mean=numpy.mean(accuracies), acc_std=numpy.std(accuracies), avg_time=time / len(data_batch) )) # Python 2 (too error-prone during fast modifications, please avoid): print('{:3} {:3} / {:3} accuracy: {:0.4f}±{:0.4f} time: {:3.2f}'.format( batch, epoch, total_epochs, numpy.mean(accuracies), numpy.std(accuracies), time / len(data_batch) ))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

樣本輸出爲:

120 12 / 300 accuracy: 0.8180±0.4649 time: 56.60
  • 1

Python 3.6 中引入了格式化字符串 (f-strings):

# Python 3.6+ print(f'{batch:3} {epoch:3} / {total_epochs:3} accuracy: {numpy.mean(accuracies):0.4f}±{numpy.std(accuracies):0.4f} time: {time / len(data_batch):3.2f}')
  • 1
  • 2

另外,這對於查詢語句的書寫也是很是方便的:

query = f"INSERT INTO STATION VALUES (13, '{city}', '{state}', {latitude}, {longitude})"
  • 1

「true division」和「integer division」之間的明顯區別

雖說對於系統編程來講,Python3所提供的改進還遠遠不夠,但這些便利對於數據科學來講已經足夠。

data = pandas.read_csv('timing.csv') velocity = data['distance'] / data['time']
  • 1
  • 2

Python 2 中的結果依賴於『時間』和『距離』(例如,以米和秒爲單位),關注其是否被保存爲整數。

而在 Python 3 中,結果的表示都是精確的,由於除法運算獲得的都是精確的浮點數。

另外一個例子是整數除法,如今已經做爲明確的運算:

n_gifts = money // gift_price  # correct for int and float arguments
  • 1

值得注意的是,整除運算能夠應用到Python的內建類型和由numpy、pandas等數據包提供的自定義類型。

嚴格排序

下面是一個嚴格排序的例子:

# All these comparisons are illegal in Python 3 3 < '3' 2 < None (3, 4) < (3, None) (4, 5) < [4, 5] # False in both Python 2 and Python 3 (4, 5) == [4, 5]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

嚴格排序的主要功能有:

防止不一樣類型實例之間的偶然性排序。 
「` 
sorted([2, ‘1’, 3]) # invalid for Python 3, in Python 2 returns [2, 3, ‘1’]

在處理原始數據時幫助咱們發現存在的問題。此外,嚴格排序對None值的合適性檢查是(這對於兩個版本的 Python 都適用): ``` if a is not None: pass if a: # WRONG check for None pass
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

天然語言處理中的Unicode編碼

下面來看一個天然語言處理任務:

s = '您好' print(len(s)) print(s[:2])
  • 1
  • 2
  • 3

比較兩個版本Python的輸出:

Python2: 6\n�� 
Python3: 2\n 您好

再來看個例子:

x = u'со' x += 'co' # ok x += 'со' # fail
  • 1
  • 2
  • 3

在這裏,Python 2 會報錯,而 Python 3 可以正常工做。由於我在字符串中使用了俄文字母,對於Python2 是沒法識別或編碼這樣的字符。

Python 3 中的 strs 是 Unicode 字符串,這對非英語文本的天然語言處理任務來講將更加地方便。還有些其它有趣的應用,例如:

'a' < type < u'a' # Python 2: True 'a' < u'a' # Python 2: False
  • 1
  • 2

from collections import Counter Counter('Möbelstück')
  • 1
  • 2

Python 2: Counter({‘\xc3’: 2, ‘b’: 1, ‘e’: 1, ‘c’: 1, ‘k’: 1, ‘M’: 1, ‘l’: 1, ‘s’: 1, ‘t’: 1, ‘\xb6’: 1, ‘\xbc’: 1}) 
Python 3: Counter({‘M’: 1, ‘ö’: 1, ‘b’: 1, ‘e’: 1, ‘l’: 1, ‘s’: 1, ‘t’: 1, ‘ü’: 1, ‘c’: 1, ‘k’: 1})

對於這些,Python 2 也能正常地工做,但 Python 3 的支持更爲友好。

▌保留詞典和**kwargs 的順序

CPython 3.6+ 的版本中字典的默認行爲是一種相似 OrderedDict 的類,但最新的 Python3.7 版本,此類已經獲得了全面的支持。這就要求在字典理解、json 序列化/反序列化等操做中保持字典原先的順序。

下面來看個例子:

import json x = {str(i):i for i in range(5)} json.loads(json.dumps(x)) # Python 2 {u'1': 1, u'0': 0, u'3': 3, u'2': 2, u'4': 4} # Python 3 {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這種保順性一樣適用於 Python3.6 版本中的 **kwargs:它們的順序就像參數中顯示的那樣。當設計數據流程時,參數的順序相當重要。

之前,咱們必須以這樣繁瑣的方式來編寫:

from torch import nn # Python 2 model = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ])) # Python 3.6+, how it *can* be done, not supported right now in pytorch model = nn.Sequential( conv1=nn.Conv2d(1,20,5), relu1=nn.ReLU(), conv2=nn.Conv2d(20,64,5), relu2=nn.ReLU()) ) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

注意到了嗎?名稱的惟一性也會被自動檢查。

迭代拆封

Python3 中引入迭代式拆封功能,下面來看一段代碼:

# handy when amount of additional stored info may vary between experiments, but the same code can be used in all cases
model_paramteres, optimizer_parameters, *other_params = load(checkpoint_name) # picking two last values from a sequence *prev, next_to_last, last = values_history # This also works with any iterables, so if you have a function that yields e.g. qualities, # below is a simple way to take only last two values from a list *prev, next_to_last, last = iter_train(args) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

默認的 pickle 引擎爲數組提供更好的壓縮

Python3 中引入 pickle 引擎,爲數組提供更好的壓縮,節省參數空間:

# Python 2 import cPickle as pickle import numpy print len(pickle.dumps(numpy.random.normal(size=[1000, 1000]))) # result: 23691675 # Python 3 import pickle import numpy len(pickle.dumps(numpy.random.normal(size=[1000, 1000]))) # result: 8000162 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這個小的改進節省了3倍的空間,並且運行階段速度更快。實際上,若是不關心速度的話,相似的壓縮性能也能夠經過設置參數 protocol=2 來實現,可是用戶常常會忽略這個選項或者根本不瞭解這個功能。

更安全的解析功能

Python3 能爲代碼提供更安全的解析,提升代碼的可讀性。具體以下段代碼所示:

labels = <initial_value>
predictions = [model.predict(data) for data, labels in dataset] # labels are overwritten in Python 2 # labels are not affected by comprehension in Python 3 
  • 1
  • 2
  • 3
  • 4
  • 5

關於 super(),simply super()

Python2 中的 super() 方法,是常見的錯誤代碼。咱們來看這段代碼:

# Python 2 class MySubClass(MySuperClass): def __init__(self, name, **options): super(MySubClass, self).__init__(name='subclass', **options) # Python 3 class MySubClass(MySuperClass): def __init__(self, name, **options): super().__init__(name='subclass', **options)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

有關 super() 方法及方法解析順序的更多內容,參見 stackoverflow: 
https://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods

更好的 IDE 會給出變量註釋

編程過程當中使用一個好的IDE,可以給初學者一種更好的編程體驗。一個好的IDE可以給不一樣的編程語言如Java、C#等,提供友好的編程環境及很是有用的編程建議,由於在執行代碼以前,全部標識符的類型都是已知的。

對於 Python,雖然這些 IDE 的功能是很難實現,可是代碼的註釋可以在編程過程幫助到咱們:

  • 以清晰的形式提示你下一步想要作的
  • 從 IDE 獲取良好的建議

這是 PyCharm IDE 的一個示例。雖然例子中所使用的函數不帶註釋,可是這些帶註釋的變量,利用代碼的後向兼容性,也能保證程序的正常工做。

多種拆封(unpacking)

下面是 Python3 中字典融合的代碼示例:

x = dict(a=1, b=2) y = dict(b=3, d=4) # Python 3.5+ z = {**x, **y} # z = {'a': 1, 'b': 3, 'd': 4}, note that value for `b` is taken from the latter dict.
  • 1
  • 2
  • 3
  • 4
  • 5

若是你想對比兩個版本之間的差別性,能夠參考如下這個連接來了解更多的信息: 
https://stackoverflow.com/questions/38987/how-to-merge-two-dictionaries-in-a-single-expression

aame 方法對於 Python 中的列表(list)、元組(tuple)和集合(set)等類型都是有效的,經過下面這段代碼咱們可以更清楚地瞭解它們的工做原理,其中a、b、c是任意的可迭代對象:

[*a, *b, *c] # list, concatenating (*a, *b, *c) # tuple, concatenating {*a, *b, *c} # set, union 
  • 1
  • 2
  • 3

此外,函數一樣支持 *args 和 **kwargs 的 unpacking 過程:

Python 3.5+ do_something(**{**default_settings, **custom_settings}) # Also possible, this code also checks there is no intersection between keys of dictionaries do_something(**first_args, **second_args)
  • 1
  • 2
  • 3
  • 4

不會過期的技術—只帶關鍵字參數的 API

咱們來看這段代碼:

model = sklearn.svm.SVC(2, 'poly', 2, 4, 0.5)
  • 1

顯而易見,這段代碼的做者還不熟悉 Python 的代碼風格,極可能剛從 C++ 或 rust語言轉 Python。代碼風格不只是我的偏好的問題,還由於在 SVC 接口中改變參數順序(adding/deleting)會使代碼無效。特別是對於 sklearn,常常要經過從新排序或重命名大量的算法參數以提供一致的 API。而每次的重構均可能使代碼失效。

在 Python3中依賴庫的編寫者一般會須要使用*以明確地命名參數:

class SVC(BaseSVC):
    def __init__(self, *, C=1.0, kernel='rbf', degree=3, gamma='auto', coef0=0.0, ... )
  • 1
  • 2

使用時,用戶須要明確規定 sklearn.svm.SVC(C=2, kernel=’poly’, degree=2, gamma=4, coef0=0.5) 中參數的命名。 
這種參數命名機制使得 API 同時兼具可靠性和靈活性。

微調:math模塊中的常量

Python3 中 math 模塊的改動,能夠查看下面這段代碼:

# Python 3 math.inf # 'largest' number math.nan # not a number max_quality = -math.inf # no more magic initial values! for model in trained_models: max_quality = max(max_quality, compute_quality(model, data))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

微調:單精度整數類型

Python 2 中提供了兩種基本的整數類型,即 int(64 位符號整數)和用於長整型數值計算的 long 類型(長整型)。而在 Python 3 中對單精度的整型數據有個微小的改動,使其包含長整型(long) 的運算。下面這段代碼教你如何查看整型值:

isinstance(x, numbers.Integral) # Python 2, the canonical way isinstance(x, (long, int)) # Python 2 isinstance(x, int) # Python 3, easier to remember
  • 1
  • 2
  • 3

其餘改動

  • Enums 的改動具備理論價值,是由於字符串輸入已普遍應用在 python 數據棧中。Enums - 雖然不與 numpy 庫交互,可是在 pandas 中有良好的兼容性。
  • 協同程序將頗有可能用於數據流程的處理,雖然目前尚未大規模應用的出現。
  • Python 3 有穩定的 ABI。
  • Python 3 支持 unicode 編碼格式,如 ω = Δφ / Δt 也是能夠容許的,但最好使用兼容性更好的舊 ASCII 名稱。
  • 一些庫好比 jupyterhub(jupyter in cloud)、django 和新版 ipython 都只支持 Python 3,所以這些用處不大的庫對你來說,可能只會偶爾使用一次。

數據科學中代碼遷移所會碰到的問題及解決方案

放棄對嵌套參數的支持:

map(lambda x, (y, z): x, z, dict.items())
  • 1

然而,它依然可以完美地適用於不一樣的理解:

{x:z for x, (y, z) in d.items()}
  • 1

一般,理解在 Python2 和 3 之間差別可以幫助咱們更好地‘轉義’代碼。

map(), .keys(), .values(), .items() 等等,返回的是迭代器而不是列表。迭代器的主要問題包括:沒有瑣碎的分割,以及沒法進行二次迭代。將返回的結果轉化爲列表幾乎能夠解決全部問題。

如遇到其餘問題請參見這篇有關 Python 的問答:「如何將 Python3 移植到個人程序中?」( https://eev.ee/blog/2016/07/31/python-faq-how-do-i-port-to-python-3/)

Python 機器學習和 python 數據科學領域所會碰到的主要問題

這些課程的做者首先要花點時間解釋 python 中什麼是迭代器,爲何它不能像字符串那樣被分片/級聯/相乘/二次迭代(以及如何處理它)。

我相信大多數課程的做者都很但願可以避開這些繁瑣的細節,可是如今看來這幾乎是個不可避免的話題。

結論

Python 的兩個版本( Python2 與 Python3 )共存了近10年的時間。時至今日,咱們不得不說:是時候該轉向 Python 3 了。

科學研究和實際生產中,代碼應該更短,可讀性更強,而且在遷移到 Python 3 後的代碼庫將更加得安全。

目前 Python 的大多數庫仍同時支持 2.x 和 3.x 兩個版本。但咱們不該等到這些依賴庫開始中止支持 Python 2 纔開始轉向 Python3,咱們如今就能夠享受新語言的功能。

遷移到 Python3 後,我敢保證你的程序運行會更加順暢:「咱們不會再作向後不兼容的事情了(https://snarky.ca/why-python-3-exists/)」。

參考內容:

Key differences between Python 2.7 and Python 3.x 
http://sebastianraschka.com/Articles/2014_python_2_3_key_diff.html 
Python FAQ: How do I port to Python 3? 
https://eev.ee/blog/2016/07/31/python-faq-how-do-i-port-to-python-3/ 
10 awesome features of Python that you can’t use because you refuse to 
upgrade to Python 3 
http://www.asmeurer.com/python3-presentation/slides.html Trust me, 
python 3.3 is better than 2.7 (video) 
http://pyvideo.org/pycon-us-2013/python-33-trust-me-its-better-than-27.html 
Python 3 for scientists 
http://python-3-for-scientists.readthedocs.io/en/latest/

原文連接: 
https://github.com/arogozhnikov/python3_with_pleasure#future-proof-apis-with-keyword-only-arguments

相關文章
相關標籤/搜索