溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

適合Java開(kāi)發(fā)者學(xué)習(xí)的Python入門(mén)教程是怎么樣的

發(fā)布時(shí)間:2021-10-26 17:20:57 來(lái)源:億速云 閱讀:141 作者:柒染 欄目:編程語(yǔ)言

適合Java開(kāi)發(fā)者學(xué)習(xí)的Python入門(mén)教程是怎么樣的,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

從哲學(xué)的角度來(lái)講,Python幾乎是與Java截然相反。它拋棄了靜態(tài)類(lèi)型和剛性結(jié)構(gòu),而是使用了一個(gè)松散的沙盒,在這里面你可以自由的做任何你想做的事情。也許Python是關(guān)于你能夠做什么,而Java則是關(guān)于你可以做什么。

然而,兩種語(yǔ)言都從C語(yǔ)言里吸取了大量的靈感。他們都是命令式語(yǔ)言,擁有塊、循環(huán)、方法、賦值以及中綴算術(shù)(infix  math)。兩者都大量使用了類(lèi)、對(duì)象、繼承以及多態(tài)性。兩者的功能異常都相當(dāng)優(yōu)秀。  兩者都能自動(dòng)管理內(nèi)存。它們都是編譯成可以運(yùn)行在某種虛擬機(jī)上的字節(jié)碼,盡管Python是透明的進(jìn)行編譯。 Python甚至從Java汲取了一些營(yíng)養(yǎng):比如基本庫(kù)的  logging 和 unittest 模塊分別是受到了log4j 和JUnit的啟發(fā)。

鑒于以上的技術(shù)重疊,我認(rèn)為Java開(kāi)發(fā)人員在使用Python時(shí)理應(yīng)感到賓至如歸。 所以我來(lái)給你一些簡(jiǎn)單的Python介紹。  我會(huì)可以告訴你什么使得Python與Java不同,以及為什么我覺(jué)得這些差異有吸引力。 至少,您可能會(huì)發(fā)現(xiàn)一些有趣的想法使您回到Java生態(tài)系統(tǒng)。

(如果你想要一個(gè)Python教程,Python文檔是一個(gè)很好的選擇,而且這是從Python 3的角度編寫(xiě)的,Python  2的使用還是很常見(jiàn)的,它與Python 3有一些語(yǔ)法上的差異

語(yǔ)法

我們先把語(yǔ)法弄明白。下面是 hello world入門(mén)程序:

print("Hello, world!")

嗯, 并不是很有啟發(fā)性。 好吧,再來(lái)看一個(gè)函數(shù),看看如何在一個(gè)文件中找到最常見(jiàn)的10個(gè)單詞。在這里我取巧使用了標(biāo)準(zhǔn)庫(kù)的 Counter  類(lèi)型,但是它就是這么的好用。

from collections import Counter  def count_words(path):     words = Counter()     with open(path) as f:         for line in f:             for word in line.strip().split():                 words[word] += 1      for word, count in words.most_common(10):         print(f"{word} x{count}")

Python由空格分隔。人們經(jīng)常對(duì)此有強(qiáng)烈的意見(jiàn)。當(dāng)我***次看到它的時(shí)候,我  甚至認(rèn)為這是異端邪說(shuō)?,F(xiàn)在,十多年過(guò)去了,這種寫(xiě)法似乎自然到我很難再回到大括號(hào)式的寫(xiě)法。如果你因此逃避,我甚至懷疑我可以說(shuō)服你,不過(guò)我勸你至少暫時(shí)忽略一下它;實(shí)際上它并沒(méi)有造成任何嚴(yán)重的問(wèn)題,反而消除了一大堆的干擾。此外,Python開(kāi)發(fā)人員從來(lái)不必爭(zhēng)論{應(yīng)該放在哪里。

除了審美上的差異之外,其他方面應(yīng)該看起來(lái)很熟悉。我們有一些數(shù)字,一些賦值和一些方法調(diào)用。import  語(yǔ)句的工作方式有些不同,但它具有相同的“使這些內(nèi)容可用”的一般含義。  Python的for循環(huán)與Java的for-each循環(huán)非常相似,只是少了點(diǎn)標(biāo)點(diǎn)符號(hào)。函數(shù)本身使用def而不是類(lèi)型進(jìn)行分隔,但它正是按照你期望的方式工作:您可以使用參數(shù)調(diào)用它,然后返回一個(gè)值(盡管某些函數(shù)不返回值)。

只有兩件事情是很不尋常的。 一個(gè)是 with 塊,非常類(lèi)似于Java 7的“try-with-resources” –  它保證文件在塊的結(jié)尾處關(guān)閉,即使會(huì)拋出一個(gè)異常。 另一個(gè)是f“…”語(yǔ)法,這是一個(gè)相當(dāng)新的功能,允許將表達(dá)式直接插入到字符串中。

就是這樣! 你已經(jīng)讀了一些Python的內(nèi)容。 至少,它不是來(lái)自一個(gè)完全不同的星球的語(yǔ)言。

動(dòng)態(tài)類(lèi)型

看這個(gè)例子可能很明顯,但是Python代碼里沒(méi)有太多的類(lèi)型聲明。 變量聲明上沒(méi)有,參數(shù)或返回類(lèi)型上沒(méi)有,對(duì)象上也沒(méi)有。  任何值在任何時(shí)候都可以是任何類(lèi)型的。 我還沒(méi)有顯示一個(gè)類(lèi)定義,所以這里只是一個(gè)簡(jiǎn)單的定義。

class Point:     def __init__(self, x, y):         self.x = x         self.y = y      def magnitude(self):         return (self.x ** 2 + self.y ** 2) ** 0.5  point = Point(3, 4) print(point.x)  # 3 print(point.magnitude())  # 5.0

盡管x和y有并沒(méi)有定義為屬性,它們的存在是因?yàn)闃?gòu)造器中創(chuàng)建了它們。沒(méi)有誰(shuí)強(qiáng)制我必須傳個(gè)整型參數(shù),我也可以傳小數(shù)和分?jǐn)?shù)。

如果你以前只用過(guò)靜態(tài)語(yǔ)言,這可能看起來(lái)一片混亂。類(lèi)型是溫暖的懶惰的以及令人滿(mǎn)意的。他們保證···(好吧,或許不會(huì))代碼實(shí)際能工作(雖然有人不同意)。但是當(dāng)你都不知道什么是正確類(lèi)型的時(shí)候,你又怎么能依靠代碼呢?

但是等等 – Java也沒(méi)有這樣的保證! 畢竟任何對(duì)象可能都是null,對(duì)吧? 實(shí)際上幾乎從來(lái)沒(méi)有一個(gè)正確類(lèi)型的對(duì)象。

您可能會(huì)將動(dòng)態(tài)類(lèi)型視為對(duì)null問(wèn)題的徹底放棄。 如果我們必須處理它,我們不妨擁抱它,并讓它為我們工作 – 通過(guò)將一切 延遲到運(yùn)行時(shí)。  類(lèi)型錯(cuò)誤變成正常的邏輯錯(cuò)誤,您可以以相同的方式處理它們。

(對(duì)于相反的方法,請(qǐng)參閱 Rust,它沒(méi)有空值 – 或者異常,我還是寧愿寫(xiě)Python,但是我很欣賞Rust的類(lèi)型系統(tǒng)并不總是對(duì)我說(shuō)謊。)

在我的magnitude方法中,self.x是int或float或任何其他類(lèi)型,這都不重要。 它只需要支持**運(yùn)算符并返回支持+運(yùn)算符的內(nèi)容。  (Python支持操作符重載,所以這可能是任何內(nèi)容。)同樣適用于普通方法調(diào)用:任何類(lèi)型都可以接受,只要它實(shí)際上可以工作。

這意味著Python不需要泛型; 一切都是按照泛型工作的。 不需要接口; 一切都已經(jīng)是多態(tài)的。  沒(méi)有向下轉(zhuǎn)型(downcasts),沒(méi)有向上轉(zhuǎn)型(upcasts),在類(lèi)型系統(tǒng)中沒(méi)有逃生艙口(escape hatches)。  當(dāng)它們可以和任意Iterable工作良好時(shí),運(yùn)行時(shí)API不需要List。

許多常見(jiàn)的模式變得更加容易。 您可以創(chuàng)建包裝器對(duì)象和代理,而無(wú)需更改消費(fèi)代碼。  您可以使用組合而不是繼承來(lái)擴(kuò)展第三方類(lèi)型,而不需要為保留多態(tài)做任何特殊的操作。 靈活的API不需要將每個(gè)類(lèi)作為接口復(fù)制; 一切都已經(jīng)作為一個(gè)隱式接口了。

動(dòng)態(tài)類(lèi)型哲學(xué)

使用靜態(tài)類(lèi)型,無(wú)論誰(shuí)編寫(xiě) 某些代碼來(lái)選擇這些類(lèi)型,編譯器都會(huì)檢查它們是否正常工作。 使用動(dòng)態(tài)類(lèi)型,無(wú)論誰(shuí)使用 某些代碼來(lái)選擇這些類(lèi)型,運(yùn)行時(shí)都會(huì)嘗試一下。  這就是對(duì)立的哲學(xué)在起作用:類(lèi)型系統(tǒng)著重于你可以 做什么,而不是你可能 做什么。

這樣使用動(dòng)態(tài)類(lèi)型有時(shí)被稱(chēng)為 “鴨子類(lèi)型”(duck typing),這是基于這種思想: “如果它走起來(lái)像鴨子,而且叫起來(lái)也像鴨子,那么它就是一個(gè)鴨子?!? 換言之,就是說(shuō),如果你想要的是能像鴨子一樣呱呱叫的東西的話(huà),你不用強(qiáng)制你的代碼必須接收一個(gè)鴨子對(duì)象,相反的你可以接收任何給你的東西,并且讓它能夠呱呱叫即可。如果它可以達(dá)成你的目標(biāo)的話(huà),那么它就跟鴨子一樣好用。(否則如果它無(wú)法如你所愿,會(huì)拋出AttributeError的錯(cuò)誤,但是這也沒(méi)什么大不了的。)

同時(shí)也要注意Python是強(qiáng)類(lèi)型的。這個(gè)詞有點(diǎn)模糊,它通常意味著變量的值在運(yùn)行時(shí)會(huì)一直保持其類(lèi)型不變。一個(gè)典型的例子是,Python不會(huì)讓你把一個(gè)字符串賦值給一個(gè)數(shù)字類(lèi)型的變量,而像弱類(lèi)型語(yǔ)言,如JavaScript  則可以將一種類(lèi)型靜默的轉(zhuǎn)換成另一種,這里使用了優(yōu)先級(jí)的規(guī)則,跟你的預(yù)期會(huì)有所不同。

與大多數(shù)動(dòng)態(tài)語(yǔ)言不同的是,Python 在運(yùn)行之前就可以捕獲錯(cuò)誤信息。例如讀取一個(gè)不存在的變量會(huì)產(chǎn)生一個(gè)異常,包括從一個(gè)字典對(duì)象(如  Map)中讀取一個(gè)不存在的鍵時(shí)也會(huì)報(bào)錯(cuò)。在 JavaScript 、Lua 和類(lèi)似語(yǔ)言中,上面兩種情況是返回 null 值。(Java 也是返回 null  值)。如果想在值不存在時(shí)返回默認(rèn)值,字典類(lèi)提供了更加明確的方法調(diào)用。

這里絕對(duì)是一個(gè)權(quán)衡的結(jié)果,是否值得取決于不同的項(xiàng)目和人。對(duì)我來(lái)說(shuō),至少非常適合用來(lái)設(shè)計(jì)一個(gè)更加可靠的系統(tǒng),以為我可以看到它實(shí)際執(zhí)行的情況。但是靜態(tài)類(lèi)型的語(yǔ)言都期待有一個(gè)預(yù)先的設(shè)計(jì)。靜態(tài)類(lèi)型很難嘗試各種不同的想法,更難發(fā)揮。

你確實(shí)擁有了更少的靜態(tài)檢查保證,但根據(jù)我的經(jīng)驗(yàn),大多數(shù)的類(lèi)型錯(cuò)誤可以正確被捕獲……因?yàn)槲覍?xiě)完代碼的***件事就是嘗試去運(yùn)行它!其它任何錯(cuò)誤應(yīng)該可以被你的測(cè)試所捕獲—測(cè)試應(yīng)該在所有語(yǔ)言中都有,并且python語(yǔ)言寫(xiě)測(cè)試相對(duì)來(lái)說(shuō)更容易。

一個(gè)混合的范例

Python 和 Java 都是命令式和對(duì)象導(dǎo)向的:它們的工作方式是執(zhí)行指令,它們把每件事物塑造為對(duì)象。

在最近的發(fā)行版本中,Java增加了一些函數(shù)式語(yǔ)言特征,我認(rèn)為這是一件好事。Python也有函數(shù)式語(yǔ)言特征,但是實(shí)現(xiàn)的方式不太一樣。它提供了一些內(nèi)置的函數(shù)如map和reduce,但是它并不是基于串聯(lián)許多小函數(shù)的思想來(lái)設(shè)計(jì)的。

相反,Python混合了其它東西。我不知道Python采用的方法的通用名稱(chēng)。我認(rèn)為它把“函數(shù)鏈”的思想分為兩個(gè):作用于序列上和使函數(shù)自身更加有用。

序列

序列和迭代在Python有重要的地位。序列是最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),作為于之上的工具非常有價(jià)值。我認(rèn)為Python對(duì)于函數(shù)式編程的實(shí)現(xiàn)如下:Python首先使得使用命令式代碼來(lái)操作序列非常容易,而不是使得結(jié)合許多小函數(shù)然后應(yīng)用于序列非常容易。

回到本文開(kāi)始的地方,我曾寫(xiě)下這一行代碼:

for word, count in words.most_common(10):

for循環(huán)對(duì)我們來(lái)說(shuō)很熟悉,但是這行代碼一次迭代了兩個(gè)變量。 實(shí)際發(fā)生的是,列表most_common中的每個(gè)元素返回一個(gè)元組,它們是一組按順序區(qū)分的值。  真正發(fā)生的是元組可以通過(guò)將它們分配給元組變量名來(lái)解包。 元組通常用于在Python中返回多個(gè)值,但它們偶爾也可用于特殊結(jié)構(gòu)。  在Java中,您需要一個(gè)完整的類(lèi)和幾行分配值的代碼。

任何可以迭代的東西同樣可以解包。 解包支持任意嵌套,所以a,(b,c)= …按照它看起來(lái)的樣子解包。  對(duì)于未知長(zhǎng)度的序列,一個(gè)*leftovers元素可以出現(xiàn)在任何地方,并且將根據(jù)需要獲取盡可能多的元素。 也許你真的會(huì)喜歡LISP?

values = [5, 7, 9] head, *tail = values print(head)  # 5 print(tail)  # (7, 9)

Python還具有通過(guò)簡(jiǎn)單表達(dá)式創(chuàng)建列表的語(yǔ)法 – 所謂的“列表解析” – 這比函數(shù)方法如map更為常見(jiàn)。 存在類(lèi)似的語(yǔ)法用于創(chuàng)建分組和集合。  整個(gè)循環(huán)可以減少到一個(gè)單一的表達(dá)式,只突出你真正感興趣的部分。

values = [3, 4, 5]  values2 = [val * 2 for val in values if val != 4]  print(values2) # [6, 10]

標(biāo)準(zhǔn)庫(kù)還在itertools模塊中包含了許多有趣的迭代,組合器和用法。

***,Python具有用于生成命令行代碼的延遲序列的生成器。 包含yield關(guān)鍵字的函數(shù)在被調(diào)用時(shí)不立即執(zhí)行; 而是返回一個(gè)生成器對(duì)象。  當(dāng)生成器迭代結(jié)束時(shí),該函數(shù)運(yùn)行直到它遇到一個(gè)yield,此時(shí)它暫停; 生成的值將成為下一個(gè)迭代值。

def odd_numbers():     n = 1     while True:         yield n         n += 2  for x in odd_numbers():     print(x)     if x > 4:         break # 1 # 3 # 5

因?yàn)樯善餮舆t運(yùn)行,它們可以產(chǎn)生***序列或在中途中斷。 他們可以產(chǎn)生大量的大型對(duì)象,不會(huì)因?yàn)樽屗鼈內(nèi)看婊疃囊淮蠖褍?nèi)存。  它們也可以作為“鏈?zhǔn)健焙瘮?shù)編程風(fēng)格的一般替代。 您可以編寫(xiě)熟悉的命令行代碼,而不是組合map和filter。

# This is the pathlib.Path API from the standard library def iter_child_filenames(dirpath):     for child in dirpath.iterdir():         if child.is_file():             yield child.name

要在Java中表示完全任意的惰性迭代器,您需要編寫(xiě)一個(gè)手動(dòng)跟蹤其狀態(tài)的Iterator。除了最簡(jiǎn)單的情況之外,這一切都會(huì)變得相當(dāng)棘手。  Python也有一個(gè)迭代接口,所以您仍然可以使用這種方法,但是生成器非常容易使用,以至于大多數(shù)自定義迭代都是用它們編寫(xiě)的。

而且因?yàn)樯善骺梢宰约簳和#运鼈冊(cè)谄渌恍┥舷挛闹惺怯杏玫摹? 通過(guò)手動(dòng)調(diào)用生成器(而不是僅用一個(gè)for循環(huán)來(lái)一次性全部迭代),就可以在一段時(shí)間內(nèi)運(yùn)行一個(gè)功能,讓它在某一點(diǎn)停止,并在恢復(fù)該函數(shù)之前運(yùn)行其他代碼。  Python充分利用這一點(diǎn)來(lái)添加對(duì)異步I/O(不使用線(xiàn)程的非阻塞網(wǎng)絡(luò))的支持,盡管現(xiàn)在它具有專(zhuān)用的async 和await 語(yǔ)法。

函數(shù)

乍一看,Python的函數(shù)看上去非常面熟。你可以使用參數(shù)來(lái)調(diào)用它們。傳遞風(fēng)格與Java完全相同—Python既沒(méi)有引用也沒(méi)有隱式復(fù)制。  Python甚至有“docstrings”,類(lèi)似于Javadoc注釋?zhuān)莾?nèi)置的語(yǔ)法并且在運(yùn)行時(shí)可見(jiàn)。

def foo(a, b, c):  """Print out the arguments. Not a very useful function, really."""  print("I got", a, b, c)  foo(1, 2, 3) # I got 1 2 3

Java具有args …語(yǔ)法的可變函數(shù); Python使用* args可以實(shí)現(xiàn)類(lèi)似的功能。  (用于解包的*leftovers語(yǔ)法靈感來(lái)源于函數(shù)語(yǔ)法。)但是,Python還有一些技巧。任何參數(shù)都可以有一個(gè)默認(rèn)值,使其成為可選項(xiàng)。任何參數(shù)也可以通過(guò)名稱(chēng)  給出 – 我之前使用Point(x = 3,y = 4)演示了這點(diǎn)。在調(diào)用 任何函數(shù)時(shí),可以使用*  args語(yǔ)法,它將傳遞一個(gè)序列,就像它是單獨(dú)的參數(shù)一樣,并且有一個(gè)等價(jià)的**  kwargs將命名參數(shù)作為一個(gè)dict接受或傳遞。一個(gè)參數(shù)可以作為“僅關(guān)鍵字(keyword-only)”,所以它必須  通過(guò)名稱(chēng)傳遞,這對(duì)于可選的布爾值是非常好的。

Python當(dāng)然沒(méi)有 函數(shù)重載,但是你使用它實(shí)現(xiàn)的功能大部分都可以被鴨子類(lèi)型(duck typing)和可選參數(shù)替換。

這是現(xiàn)階段Python***大的功能之一。 與動(dòng)態(tài)類(lèi)型一樣,您可以通過(guò)包裝器或代理透明地替換對(duì)象,* args和** kwargs允許任何函數(shù)  被透明地包裝。

def log_calls(old_function):      def new_function(*args, **kwargs):          print("i'm being called!", args, kwargs)          return old_function(*args, **kwargs)      return new_function  @log_calls  def foo(a, b, c=3):      print(f"a = {a}, b = , c = {c}")  foo(1, b=2)  # i'm being called! (1,) {'b': 2}  # a = 1, b = 2, c = 3

這有點(diǎn)難以理解,對(duì)不起。 不用擔(dān)心它究竟是如何工作的; 要點(diǎn)是,foo被一個(gè)new_function替換,它將所有的參數(shù)轉(zhuǎn)發(fā)到foo。  foo和調(diào)用者都不需要知道哪些事情有哪些不同。

我不能低估它有多么強(qiáng)大。 它可用于記錄,調(diào)試,管理資源,緩存,訪(fǎng)問(wèn)控制,驗(yàn)證等。  它與其他元編程功能工作良好,同樣地,它可以讓您分解結(jié)構(gòu),而不僅僅是代碼。

適合Java開(kāi)發(fā)者學(xué)習(xí)的Python入門(mén)教程是怎么樣的

對(duì)象和動(dòng)態(tài)運(yùn)行時(shí)

動(dòng)態(tài)運(yùn)行時(shí)是一種在背后驅(qū)動(dòng)語(yǔ)言核心部分的東西 — 它可以在運(yùn)行時(shí)被執(zhí)行。像 C 或者 C++  這類(lèi)的語(yǔ)言絕不會(huì)具有動(dòng)態(tài)運(yùn)行時(shí);它們?cè)创a的結(jié)構(gòu)被“烘焙”成編譯輸出,并且后續(xù)沒(méi)有明顯的方法來(lái)改變它的行為。從另一方面,Java的確具有動(dòng)態(tài)運(yùn)行時(shí)!它甚至帶有一整個(gè)專(zhuān)門(mén)用于反射的包。

Python 當(dāng)然也有反射。很多簡(jiǎn)單的函數(shù)通過(guò)反射被構(gòu)建,用來(lái)聯(lián)機(jī)檢查或修改對(duì)象的屬性,這對(duì)調(diào)試以及偶爾的欺騙非常有用。

但 Python 對(duì)此更深入一點(diǎn)。 因?yàn)橐磺卸荚谶\(yùn)行時(shí)完成,Python 暴露了很多擴(kuò)展點(diǎn)來(lái)自定義它的語(yǔ)義。 你不能改變語(yǔ)法,代碼依然看起來(lái)像  Python,但你可以分解結(jié)構(gòu) — 這在一種更死板的語(yǔ)言是非常難以做到的。

舉個(gè)極端的例子,看下 pytest, 它聰明的處理了 Python 的 assert 聲明。 通常, assert x == 1 為 false  時(shí)只會(huì)簡(jiǎn)單的拋出一個(gè) AssertionError 異常,導(dǎo)致你不知道錯(cuò)誤是什么或者哪里出錯(cuò)了。這就是為什么 Python 內(nèi)置的單元測(cè)試模塊 — 像  JUnit 和很多其他的測(cè)試工具 — 提供很多專(zhuān)門(mén)的工具函數(shù),比如 assertEquals。不幸的是,這些工具函數(shù)使得測(cè)試初看到時(shí),更加復(fù)雜更難以讀懂。但在  pytest 中, assert x == 1 是好用的。如果失敗,pytest 將告訴你 x 是… 或者兩個(gè)列表哪里有分歧,或者兩個(gè)集合哪些元素不同,  或者其他的。所有的這些都是基于比較完成和運(yùn)算對(duì)象的類(lèi)型自動(dòng)發(fā)生的。

pytest是如何工作的呢? 你確實(shí)不想知道。你不需要知道如何用pytest寫(xiě)測(cè)試 — 這使人很開(kāi)心。

這就是動(dòng)態(tài)運(yùn)行時(shí)的真正優(yōu)勢(shì)所在。 就自己而言,可能沒(méi)有使用這些功能。但是你能從這些庫(kù)中收獲巨大的好處,你使用這些庫(kù)并不需要關(guān)心它們?nèi)绾芜\(yùn)行。 甚至  Python 本身依靠使用自己的擴(kuò)展點(diǎn)實(shí)現(xiàn)了很多額外的特性 — 這些不需要改變語(yǔ)法或者解釋器。

對(duì)象

屬性(在C#中一般翻譯成特性)存取是我喜歡舉的一個(gè)簡(jiǎn)單例子。在 Java 中,一個(gè) Point 類(lèi)可能選擇用 getX() 和 setX()  方法而不是一個(gè)簡(jiǎn)單直白的 x 屬性。原因是如果你需要改變 x 的讀或?qū)懀悴恍枰茐慕涌?。?Python 中,你不需要擔(dān)心前面的這些,  因?yàn)楸匾獣r(shí)你能解釋屬性存取。

class Point:     def __init__(self, x, y):         self._x = x         self._y = y      @property     def x(self):         return self._x      # ... same for y ...  point = Point(3, 4) print(point.x)  # 3

有趣的 @property 語(yǔ)法是一種修飾,看來(lái)就像 Java  的注解,但它可以更直接地修改函數(shù)或類(lèi)。這是完全透明的調(diào)用代碼——和讀其它屬性沒(méi)什么區(qū)別——但是對(duì)象可以根據(jù)自己的需要干預(yù)或處理它。與 Java  不同,屬性訪(fǎng)問(wèn)是類(lèi) API 的一部分,可以自由的定義。(注意這個(gè)示例讓 x  成為只讀的,因?yàn)槲覜](méi)有指定寫(xiě)方法!可寫(xiě)屬性的語(yǔ)法有點(diǎn)滑稽,這里暫時(shí)不管它如何工作。但你可以具體規(guī)定只有奇數(shù)可以賦值給 point.x。)

這個(gè)特性也存在于其它靜態(tài)語(yǔ)言中,比如 C#,所以這并不是什么大不了的東西。關(guān)于 Python  真正有趣的部分是,屬性并沒(méi)什么特別。它是一個(gè)正常的內(nèi)建類(lèi)型,一段純粹而且不滿(mǎn)一屏的 Python 程序。它的工作原理是 Python  類(lèi)可以自定義其屬性訪(fǎng)問(wèn),包括一般的和按屬性的。包裝、代理和組合很容易實(shí)現(xiàn):你可以將所有訪(fǎng)問(wèn)調(diào)用轉(zhuǎn)發(fā)到底層對(duì)象,而不必知道它有什么方法。

相同的鉤子屬性可用于懶加載屬性或者自動(dòng)持有弱引用的屬性——這對(duì)調(diào)用代碼完全透明,通過(guò)純 Python 就能實(shí)現(xiàn)。

你可能已經(jīng)注意到我的代碼沒(méi)有使用 public 或 private 修飾符。事實(shí)上,Python  中不存在這個(gè)概念。按照慣例,用一個(gè)下劃線(xiàn)開(kāi)頭表示“私有”——或者更準(zhǔn)備地說(shuō),“不打算成為穩(wěn)定公開(kāi) API 的一部分”。但這并沒(méi)有語(yǔ)言上的意義,Phthon  本身不會(huì)阻止偵查或修改這樣的屬性(如果是方法的話(huà),則是調(diào)用)。同樣,也沒(méi)有final 、static或const。

這是同樣的工作原理:核心 Python  通常不會(huì)妨礙你做任何事情。如果你需要,它會(huì)非常有用。我已經(jīng)通過(guò)啟動(dòng)時(shí)調(diào)用或重寫(xiě),甚至重新定義私有方法來(lái)修補(bǔ)第三方庫(kù)的  BUG。這樣我不需要重新做一個(gè)項(xiàng)目的本地分支,可以省不少事。而且一量官方修復(fù)了 BUG,可很容易就能刪掉自己的補(bǔ)丁代碼。

同樣,你可以輕松地編寫(xiě)依賴(lài)外部狀態(tài)的測(cè)試代碼——比如當(dāng)前時(shí)間。如果重構(gòu)不可行,你可以在測(cè)試時(shí)把 time.time()  替換為模擬函數(shù)。庫(kù)函數(shù)只是模塊模塊的屬性(就像 Java 的包),而且 Python  模塊是種對(duì)象,和其它對(duì)象一樣,所以它們可以以同樣的方式偵查到或被修改。

類(lèi)

Java 的類(lèi)由 Class 對(duì)象支持,但二者并不有完全互換。比如 Foo 類(lèi)的 Class 對(duì)象是 Foo.class。我不認(rèn)為 Foo  可以被它自己使用,因?yàn)樗艘粋€(gè)類(lèi)型,Java 在處理類(lèi)型和值的時(shí)候會(huì)有一些微妙的區(qū)別。

Python  中,類(lèi)是對(duì)象,是類(lèi)型的實(shí)例(它本身就是對(duì)象,是它自己的實(shí)例,想起來(lái)很有趣。)類(lèi)可以當(dāng)作其它值一樣使用:作為參數(shù)、保存一某個(gè)更大的數(shù)據(jù)結(jié)構(gòu)中、檢查、操作。有時(shí)候把類(lèi)作為字典的鍵特別有用。而且因?yàn)轭?lèi)是實(shí)例化的,可以簡(jiǎn)單地調(diào)用它們——Python  沒(méi)有 new 關(guān)鍵字 —— 很多情況下 new 和簡(jiǎn)單的函數(shù)調(diào)用可以互換。這樣一來(lái),一些常見(jiàn)的模式,比如工廠模式,就太簡(jiǎn)單了,幾乎不需要。

# 等等,Vehicle 是一個(gè)類(lèi)還是一個(gè)工廠函數(shù)?誰(shuí)管呢!  # 就算它在類(lèi)或工廠函數(shù)之間互換,也不會(huì)破壞這段代碼。  car = Vehicle(wheels=4, doors=4)

最近我好幾次把函數(shù)甚至常規(guī)代碼放在頂層,不在任何類(lèi)里面。這樣做不會(huì)有問(wèn)題,但是其含義有點(diǎn)微妙。Python 中甚至 class 和 def  都是常規(guī)代碼,在運(yùn)行的時(shí)候執(zhí)行。Python 文件從上往下執(zhí)行,class 和 def  并不特別。它們只是有特殊的語(yǔ)法,用來(lái)創(chuàng)建特定類(lèi)型的對(duì)象:類(lèi)和函數(shù)。

以下是真正酷的部分。類(lèi)是對(duì)象,它們的類(lèi)型是type,因此你可以子類(lèi)化并改變它的運(yùn)行。然后,你可以生成你的子類(lèi)的實(shí)例。

***次接觸仔細(xì)想想會(huì)感覺(jué)有點(diǎn)奇怪。但是再想下,你獲益于不需要知道它是如何運(yùn)行的。比如,Python沒(méi)有enum塊,但它確實(shí)有 enum  module:

class Animal(Enum):     cat = 0     dog = 1     mouse = 2     snake = 3  print(Animal.cat)           # <Animal.cat: 0> print(Animal.cat.value)     # 0 print(Animal(2))            # <Animal.mouse: 2> print(Animal['dog'])        # <Animal.dog: 1>

class  語(yǔ)句創(chuàng)建了一個(gè)對(duì)象,這意味著它在某處調(diào)用了構(gòu)造函數(shù),而且可以重寫(xiě)該構(gòu)造函數(shù)來(lái)改變類(lèi)的構(gòu)造方式。這里Enum創(chuàng)建了一個(gè)固定的實(shí)例集,而不是類(lèi)屬性。所有這些都是用普通的  Python 代碼的常規(guī)的 Python 語(yǔ)法實(shí)現(xiàn)的。

實(shí)體庫(kù)也是基于這個(gè)思路來(lái)構(gòu)建的。你討厭在構(gòu)造函數(shù)中單調(diào)的干 self.foo = foo  這種事情嗎?然后純手工定義相等性比較、哈希和克隆和開(kāi)發(fā)可讀的列表?Java 需要編譯器支持,這個(gè)支持可能來(lái)自 Amber 項(xiàng)目。Python  非常靈活,所以社區(qū)中有 attrs 庫(kù)解決了這個(gè)問(wèn)題。

import attr  @attr.s class Point:     x = attr.ib()     y = attr.ib()  p = Point(3, 4) q = Point(x=3, y=4) p == q  # True, which it wouldn't have been before! print(p)  # Point(x=3, y=4)

或者采用 SQLAlchemy 這個(gè)功能強(qiáng)大的 Python 數(shù)據(jù)庫(kù)封裝庫(kù)。它包含一個(gè)靈感來(lái)自 Hibernate 的  ORM,但不需要在配置文件里定義表結(jié)構(gòu)或者通過(guò)其他冗長(zhǎng)的注解,你可直接在類(lèi)里編寫(xiě)數(shù)據(jù)庫(kù)映射代碼:

class Order(Table):     id = Column(Integer, primary_key=True)     order_number = Column(Integer, index=True)     status = Column(Enum('pending', 'complete'), default='pending')     ...

它的基本思想類(lèi)同Enum,但SQLAlchemy也使用和property同樣的鉤子,自然而然地,你可以修改欄位值。

order.order_number = 5  session.commit()

***,類(lèi)本身可以在運(yùn)行中被創(chuàng)建。 這有點(diǎn)好處,可是 thriftpy 創(chuàng)建的整個(gè) module, 里面全是基于 Thrift 定義文件的類(lèi)。  在Java中,你得需要代碼生成,這就增加了全新的編譯步驟,從而導(dǎo)致不同步。

所有這些示例依賴(lài)于Python現(xiàn)存的語(yǔ)法,但也在里面吸收了新的含義。它能做的,沒(méi)有你在Java或者其他語(yǔ)言中不能做的。但它刪減了結(jié)構(gòu)性的重復(fù) &mdash;  正是這些使得編碼更易寫(xiě)、易讀,以及產(chǎn)生更少的bug。

結(jié)語(yǔ)

Python  有很多和Java相同的基本概念,但處理方向非常不同,它加入了一些全新的思想。Java關(guān)注于穩(wěn)定性和可靠性,而Python關(guān)注于可表達(dá)性和靈活性。它是一種完全不同的思想方式來(lái)思考命令式編程。

我有理由相信Python將讓你在Java所擅長(zhǎng)的領(lǐng)域替換Java。Python可能不會(huì)贏在速度競(jìng)賽上,比如(Pypy,一種即時(shí)編譯的Python)。Java擁有對(duì)線(xiàn)程的原生支持,而Python界大都回避了這些問(wèn)題。規(guī)模龐大復(fù)雜又有很多死角的軟件更喜歡靜態(tài)類(lèi)型提供的合理性檢查(比如mypy,一種Python靜態(tài)類(lèi)型檢查器)。

也許某些 Java 并不擅長(zhǎng)的領(lǐng)域剛好是 Python  的強(qiáng)項(xiàng)。例如大量的軟件對(duì)性能的要求并不高,也不需要并行執(zhí)行,我們只需要考慮具體實(shí)現(xiàn)業(yè)務(wù)的問(wèn)題。我發(fā)現(xiàn)使用 Python  開(kāi)始一個(gè)項(xiàng)目非常簡(jiǎn)單而且快速。沒(méi)有單獨(dú)的編譯過(guò)程,編碼然后運(yùn)行的速度非???。代碼很簡(jiǎn)短,這也意味著代碼更容易被理解。嘗試不同的架構(gòu)方法看起來(lái)更加容易。而且有時(shí)候嘗試一些愚蠢的想法是如此的有趣,就好像  使用庫(kù)實(shí)現(xiàn) goto 功能。

我希望你能試試 Python ,自從我用了它之后,已經(jīng)從中獲得很多樂(lè)趣,我相信你也一樣。至少不用刻意的去拒絕它。

最壞的情況,還有個(gè) Pyjnius 可以讓你這樣做:

from jnius import autoclass  System = autoclass('java.lang.System') System.out.println('Hello, world!')

關(guān)于適合Java開(kāi)發(fā)者學(xué)習(xí)的Python入門(mén)教程是怎么樣的問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI