先從一個(gè)低級(jí)錯(cuò)誤說(shuō)起
上周茶敏,在項(xiàng)目中使用SQLAchemy操作數(shù)據(jù)庫(kù)時(shí)壤靶,代碼中出現(xiàn)了數(shù)據(jù)修改操作無(wú)法寫(xiě)入數(shù)據(jù)庫(kù)的bug。為簡(jiǎn)化問(wèn)題惊搏,就不貼原本的代碼了贮乳,代碼邏輯結(jié)構(gòu)大致如下:
try:
results = db.session.query(all_record).all()
for result in results:
result.nmae = 'new name'
print(result.nmae)
db.session.commit()
except Exception as e:
print(e)
執(zhí)行結(jié)果:
new name
大家知道,SQLAchemy是Python的一個(gè)用于實(shí)現(xiàn)ORM庫(kù)(Object Relational Mapping恬惯,即對(duì)象關(guān)系映射)向拆,我們可以藉由它通過(guò)編程語(yǔ)言對(duì)多種類型的數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)表定義創(chuàng)建和對(duì)數(shù)據(jù)的增刪改查。它將數(shù)據(jù)庫(kù)中的關(guān)系轉(zhuǎn)化為程序語(yǔ)言中的對(duì)象酪耳,因此所有對(duì)數(shù)據(jù)庫(kù)的操作都體現(xiàn)為對(duì)對(duì)象屬性的修改浓恳。
然而這段代碼執(zhí)行完后,并沒(méi)有將數(shù)據(jù)寫(xiě)入數(shù)據(jù)表的對(duì)應(yīng)字段,這種情況一般來(lái)說(shuō)是沒(méi)有執(zhí)行commit提交事務(wù)奖蔓,導(dǎo)致數(shù)據(jù)修改的結(jié)果并沒(méi)有真正寫(xiě)入數(shù)據(jù)庫(kù)赞草。
但是在這里,我是明明寫(xiě)了commit()語(yǔ)句并且用斷點(diǎn)的方式檢查卻是執(zhí)行了的吆鹤,而且也沒(méi)有拋出異常厨疙。
排除了許多可能后,我才猛然發(fā)現(xiàn)疑务,犯了一個(gè)寫(xiě)錯(cuò)變量名的低級(jí)錯(cuò)誤沾凄。比如,這里代碼操作的屬性是 result.nmae 知允,在數(shù)據(jù)庫(kù)中定義的本應(yīng)該是name撒蟀,在對(duì)象所對(duì)應(yīng)的類里的屬性自然也是name,故而在寫(xiě)入的時(shí)候沒(méi)有相應(yīng)字段可以寫(xiě)入温鸽,所以我去數(shù)據(jù)庫(kù)查看name依然是以前的值保屯。可是它也沒(méi)有拋出異常涤垫,這個(gè)大概就是SQLAchemy的坑了姑尺。
但是,問(wèn)題就來(lái)了蝠猬,既然類中沒(méi)有nmae這個(gè)屬性切蟋,為什么可以被定義和賦值?
這似乎顛覆了我對(duì)面向?qū)ο蟮恼J(rèn)知榆芦。經(jīng)某大牛同學(xué)Talk is cheap 柄粹,Show me the code式的點(diǎn)播,我才猛然認(rèn)識(shí)到這是Python作為一門(mén)動(dòng)態(tài)語(yǔ)言的特性匆绣。
大牛code如下:
In [16]: class test:
...: def __init__(self):
...: self.A = 1
...:
...:
In [17]: t = test()
In [18]: t.A
Out[18]: 1
In [19]: t.B = 2
In [20]: t.__dict__
Out[20]: {'A': 1, 'B': 2}
In [21]: t.B
Out[21]: 2
Python類由類變量和實(shí)例變量驻右、方法以及可以看作特殊變量的數(shù)據(jù)成員(類變量或者實(shí)例變量, 用于處理類及其實(shí)例對(duì)象的相關(guān)的數(shù)據(jù))構(gòu)成。其中犬绒,類變量為所有類產(chǎn)生實(shí)例所共用旺入,比如一個(gè)類對(duì)應(yīng)的實(shí)例個(gè)數(shù)的計(jì)數(shù)器。想要修改類變量凯力,必須通過(guò)類內(nèi)部特定的修改方法來(lái)修改茵瘾。而作為實(shí)例變量,直接像上面那樣咐鹤,在實(shí)例化后賦值即可拗秘,甚至也可以定義一個(gè)類中不存在的新的對(duì)象屬性。作為一個(gè)從JAVA入門(mén)面向?qū)ο蟮某绦騿T來(lái)說(shuō)祈惶,卻是有些難以理解雕旨,因?yàn)镴AVA并沒(méi)有這種常規(guī)操作扮匠,javassit這種非常規(guī)技術(shù)就不說(shuō)了。
造成這種差異和誤解的根本原因是JAVA是一門(mén)靜態(tài)語(yǔ)言凡涩,而Python是一門(mén)動(dòng)態(tài)語(yǔ)言棒搜。而恰好,JAVA又是一門(mén)編譯型的語(yǔ)言活箕,而Python是一個(gè)用解釋器執(zhí)行的解釋型語(yǔ)言力麸。于是有人告訴我,編譯型語(yǔ)言就是靜態(tài)語(yǔ)言育韩,而解釋型語(yǔ)言就是動(dòng)態(tài)語(yǔ)言克蚂。
事實(shí)果真如此么?我們一起來(lái)梳理下面幾個(gè)概念筋讨!
編譯型語(yǔ)言
需通過(guò)編譯器(compiler)將源代碼編譯成機(jī)器碼埃叭,之后才能執(zhí)行的語(yǔ)言。一般需經(jīng)過(guò)編譯(compile)悉罕、鏈接(linker)這兩個(gè)步驟赤屋。編譯是把源代碼編譯成機(jī)器碼,鏈接是把各個(gè)模塊的機(jī)器碼和依賴庫(kù)串連起來(lái)生成可執(zhí)行文件蛮粮。
- 優(yōu)點(diǎn):一次編譯益缎,重復(fù)可用,不需要每次執(zhí)行時(shí)再編譯一遍然想,由于編譯后生成的是能直接被機(jī)器執(zhí)行的機(jī)器碼,所以通常情況下執(zhí)行效率會(huì)更高欣范。
- 缺點(diǎn):編譯之后如果需要修改就需要整個(gè)模塊重新編譯变泄。編譯的時(shí)候根據(jù)對(duì)應(yīng)的運(yùn)行環(huán)境生成機(jī)器碼,不同的操作系統(tǒng)之間移植就會(huì)有問(wèn)題恼琼,需要根據(jù)運(yùn)行的操作系統(tǒng)環(huán)境編譯不同的可執(zhí)行文件(比如Windows上的.exe文件妨蛹,CentOS上的.rpm文件)。
- 代表語(yǔ)言:C晴竞、C++蛙卤、Pascal、Object-C以及最近很火的蘋(píng)果新語(yǔ)言swift
解釋型語(yǔ)言
解釋性語(yǔ)言的程序不需要編譯噩死,相比編譯型語(yǔ)言省了道工序颤难,解釋性語(yǔ)言在運(yùn)行程序的時(shí)候才逐行翻譯為機(jī)器碼來(lái)給機(jī)器執(zhí)行。
- 優(yōu)點(diǎn):可以在任何安裝了解釋器(虛擬機(jī))的機(jī)器上運(yùn)行已维,不用關(guān)心用了什么操作系統(tǒng)行嗤。靈活,修改代碼的時(shí)候直接修改就可以垛耳,可以快速部署栅屏,不用停機(jī)維護(hù)飘千。
- 缺點(diǎn):每次運(yùn)行的時(shí)候都要解釋一遍,性能上不如編譯型語(yǔ)言栈雳。
- 代表語(yǔ)言:JavaScript护奈、Python、Erlang哥纫、PHP逆济、Perl、Ruby
混合型語(yǔ)言
既然編譯型和解釋型各有缺點(diǎn)就會(huì)有人想到把兩種類型整合起來(lái)磺箕,取其精華去其糟粕奖慌。就出現(xiàn)了半編譯型語(yǔ)言。比如C#,C#在編譯的時(shí)候不是直接編譯成機(jī)器碼而是中間碼松靡,.NET平臺(tái)提供了中間語(yǔ)言運(yùn)行庫(kù)運(yùn)行中間碼简僧,中間語(yǔ)言運(yùn)行庫(kù)類似于Java虛擬機(jī)。.net在編譯成IL代碼后雕欺,保存在dll中岛马,首次運(yùn)行時(shí)由JIT在編譯成機(jī)器碼緩存在內(nèi)存中,下次直接執(zhí)行屠列。Java先生成字節(jié)碼再在Java虛擬機(jī)中解釋執(zhí)行啦逆。嚴(yán)格來(lái)說(shuō)混合型語(yǔ)言屬于解釋型語(yǔ)言。C#更接近編譯型語(yǔ)言笛洛。
動(dòng)態(tài)語(yǔ)言和靜態(tài)語(yǔ)言
動(dòng)態(tài)語(yǔ)言
是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語(yǔ)言:例如新的函數(shù)夏志、對(duì)象、甚至代碼可以被引進(jìn)苛让,已有的函數(shù)可以被刪除或是其他結(jié)構(gòu)上的變化沟蔑。通俗點(diǎn)說(shuō)就是在運(yùn)行時(shí)代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)。
主要?jiǎng)討B(tài)語(yǔ)言:Object-C狱杰、C#瘦材、JavaScript、PHP仿畸、Python食棕、Erlang。
靜態(tài)語(yǔ)言
與動(dòng)態(tài)語(yǔ)言相對(duì)應(yīng)的错沽,運(yùn)行時(shí)結(jié)構(gòu)不可變的語(yǔ)言就是靜態(tài)語(yǔ)言簿晓。如Java、C甥捺、C++抢蚀。
走出誤區(qū)
很多人認(rèn)為解釋型語(yǔ)言都是動(dòng)態(tài)語(yǔ)言,這個(gè)觀點(diǎn)是錯(cuò)的镰禾!Java是解釋型語(yǔ)言但是不是動(dòng)態(tài)語(yǔ)言皿曲,Java不能在運(yùn)行的時(shí)候改變自己結(jié)構(gòu)唱逢。反之成立嗎?動(dòng)態(tài)語(yǔ)言都是解釋型語(yǔ)言屋休。也是錯(cuò)的坞古!Object-C是編譯型語(yǔ)言,但是他是動(dòng)態(tài)語(yǔ)言劫樟。得益于特有的run time機(jī)制(準(zhǔn)確說(shuō)run time不是語(yǔ)法特性是運(yùn)行時(shí)環(huán)境痪枫,這里不展開(kāi))OC代碼是可以在運(yùn)行的時(shí)候插入、替換方法的叠艳。
靜態(tài)類型語(yǔ)言和動(dòng)態(tài)類型語(yǔ)言
為了不混淆動(dòng)態(tài)語(yǔ)言和動(dòng)態(tài)類型語(yǔ)言奶陈,下面還補(bǔ)充說(shuō)明以下幾個(gè)概念
動(dòng)態(tài)類型語(yǔ)言
很多網(wǎng)上資料把動(dòng)態(tài)類型語(yǔ)言和動(dòng)態(tài)語(yǔ)言混為一談,簡(jiǎn)直是誤人子弟附较。動(dòng)態(tài)類型語(yǔ)言和動(dòng)態(tài)語(yǔ)言是完全不同的兩個(gè)概念吃粒。動(dòng)態(tài)類型語(yǔ)言是指在運(yùn)行期間才去做數(shù)據(jù)類型檢查的語(yǔ)言,說(shuō)的是數(shù)據(jù)類型拒课,動(dòng)態(tài)語(yǔ)言說(shuō)的是運(yùn)行是改變結(jié)構(gòu)徐勃,說(shuō)的是代碼結(jié)構(gòu)。
動(dòng)態(tài)類型語(yǔ)言的數(shù)據(jù)類型不是在編譯階段決定的早像,而是把類型綁定延后到了運(yùn)行階段僻肖。
主要語(yǔ)言:Python、Ruby卢鹦、Erlang臀脏、JavaScript、swift法挨、PHP谁榜、Perl。
靜態(tài)類型語(yǔ)言
靜態(tài)語(yǔ)言的數(shù)據(jù)類型是在編譯其間確定的或者說(shuō)運(yùn)行之前確定的凡纳,編寫(xiě)代碼的時(shí)候要明確確定變量的數(shù)據(jù)類型。
主要語(yǔ)言:C帝蒿、C++荐糜、C#、Java葛超、Object-C暴氏。
注意
解釋型語(yǔ)言并不都是動(dòng)態(tài)類型語(yǔ)言,編譯型語(yǔ)言也不全是靜態(tài)類型語(yǔ)言绣张。swift是編譯型語(yǔ)言但是它也是動(dòng)態(tài)類型語(yǔ)言答渔。C#和Java是解釋型語(yǔ)言也是靜態(tài)類型語(yǔ)言,雖然它們看作解釋型語(yǔ)言侥涵,但是它們也有編譯過(guò)程沼撕,會(huì)在編譯過(guò)程中做數(shù)據(jù)類型的檢查
所以宋雏,動(dòng)態(tài)類型語(yǔ)言是數(shù)據(jù)類型檢查的動(dòng)態(tài)(運(yùn)行時(shí)檢查),而不是像動(dòng)態(tài)語(yǔ)言一樣代碼邏輯結(jié)構(gòu)的在運(yùn)行時(shí)可變更务豺,靜態(tài)則相反磨总。
強(qiáng)類型語(yǔ)言和弱類型語(yǔ)言
說(shuō)到數(shù)據(jù)類型,就不得不提從數(shù)據(jù)類型是否嚴(yán)格定義的角度產(chǎn)生的另外兩個(gè)概念:強(qiáng)類型語(yǔ)言和弱類型語(yǔ)言
強(qiáng)類型語(yǔ)言:
強(qiáng)類型語(yǔ)言笼沥,一旦一個(gè)變量被指定了某個(gè)數(shù)據(jù)類型蚪燕,如果不經(jīng)過(guò)強(qiáng)制類型轉(zhuǎn)換,那么它就永遠(yuǎn)是這個(gè)數(shù)據(jù)類型奔浅。你不能把一個(gè)整形變量當(dāng)成一個(gè)字符串來(lái)處理馆纳。
主要語(yǔ)言:Java、C#汹桦、Python鲁驶、Object-C、Ruby
弱類型語(yǔ)言:
數(shù)據(jù)類型可以被忽略营勤,一個(gè)變量可以賦不同數(shù)據(jù)類型的值灵嫌。一旦給一個(gè)整型變量a賦一個(gè)字符串值,那么a就變成字符類型葛作。
主要語(yǔ)言:JavaScript寿羞、PHP
3、注意:
一個(gè)語(yǔ)言是不是強(qiáng)類型語(yǔ)言和是不是動(dòng)態(tài)類型語(yǔ)言也沒(méi)有必然聯(lián)系赂蠢。Python是動(dòng)態(tài)類型語(yǔ)言绪穆,是強(qiáng)類型語(yǔ)言。JavaScript是動(dòng)態(tài)類型語(yǔ)言虱岂,是弱類型語(yǔ)言玖院。Java是靜態(tài)類型語(yǔ)言,是強(qiáng)類型語(yǔ)言第岖。
附錄概念對(duì)照表
概念 | 劃分角度 | 特點(diǎn) |
---|---|---|
編譯型語(yǔ)言 | 執(zhí)行方式 | 將源代碼編譯為機(jī)器碼給機(jī)器執(zhí)行 |
解釋型語(yǔ)言 | 執(zhí)行方式 | 將源代碼交由解釋器解釋為機(jī)器碼執(zhí)行 |
混合型語(yǔ)言 | 執(zhí)行方式 | 將源代碼編譯為“中間碼”难菌,再交由解釋器(虛擬機(jī))解釋為機(jī)器碼執(zhí)行 |
動(dòng)態(tài)語(yǔ)言 | 代碼邏輯結(jié)構(gòu)是否可以在運(yùn)行時(shí)改變 | 可以改變 |
靜態(tài)語(yǔ)言 | 代碼邏輯結(jié)構(gòu)是否可以在運(yùn)行時(shí)改變 | 不可改變 |
動(dòng)態(tài)類型語(yǔ)言 | 代碼的數(shù)據(jù)類型何時(shí)檢查 | 運(yùn)行時(shí)檢查 |
靜態(tài)類型語(yǔ)言 | 代碼的數(shù)據(jù)類型何時(shí)檢查 | 運(yùn)行時(shí)不檢查 |
強(qiáng)類型語(yǔ)言 | 變量定義后是否可以隨意改變數(shù)據(jù)類型 | 不可以 |
弱類型語(yǔ)言 | 變量定義后是否可以隨意改變數(shù)據(jù)類型 | 可以 |