編寫一個用于記錄日志的函數(shù) log(msg),該函數(shù)將 msg 寫入到日志文件 log.txt 中。每次寫日志文件時,需要:保留日志文件的原有內(nèi)容將新的記錄添加到日志文件的尾部文件 log.py 的內(nèi)容如下:from datetime import datetimedef log(msg): dt = datetime.today() now = dt.strftime("%Y-%m-%d %H:%M:%S") line = '%s: %s\n' % (now, msg) log_file.write(line)log_file = open('log.txt', 'a')log('hello')log('world')log_file.close()在第 3 行,編寫函數(shù) log(msg),用于將 msg 追加到日志文件 log.txt在第 4 行到第 5 行,獲取當前時間 now在第 6 行,將當前時間 now 和信息 msg 追加到日志文件在第 9 行,以追加模式 a 打開文件 log.txt向文件寫數(shù)據(jù)時,數(shù)據(jù)會被追加到文件尾部在第 10 行,向日志中寫入 hello在第 11 行,向日志中寫入 world在第 12 行,關(guān)閉日志文件運行 log.py,命令如下:C:\> python log.pyC:\> dir2020/06/04 11:47 <DIR> .2020/06/04 11:47 <DIR> ..2020/06/04 11:49 249 log.py2020/06/04 11:50 56 log.txt在第 6 行,顯示當前目錄下存在文件 log.txt以追加模式 a 打開文件 log.txt,如果文件不存在則創(chuàng)建文件此時文件 log.txt 的內(nèi)容如下,文件中包含有兩行文本:2020-06-04 11:49:54: hello2020-06-04 11:49:54: world再次運行程序 log.py,命令如下:C:\> python log.py此時文件 log.txt 的內(nèi)容如下,文件中包含有四行文本:2020-06-04 11:49:54: hello2020-06-04 11:49:54: world2020-06-04 11:50:06: hello2020-06-04 11:50:06: world第 1 行和第 2 行,是第一次運行 log.py 產(chǎn)生的記錄第 3 行和第 4 行,是第二次運行 log.py 產(chǎn)生的記錄
管道(pipe),默認指無名管道。管道在兩個進程之間建立一個通道,一個進程向這個通道寫入字節(jié)流,另一個進程從這個通道讀取字節(jié)流。用 C 語言描述管道示例:#include <unistd.h> // 引入linux頭文件int pipe(int fd[2]); // 返回:如果成功返回0,失敗則返回-1 上述定義的 fd 對象,其中 fd[0] 表示讀文件描述符,f[1] 表示寫文件描述符。管道的寫和讀操作假設(shè)存在兩個進程,分別為進程 A 和進程 B,那么進程 A 往 f[1] 寫入,進程 B 則從自身的 f[0] 讀取內(nèi)容。需要注意管道是半雙工通信,也就是數(shù)據(jù)的流向是固定的,必須有一端是寫入端,另一端是讀取端。
編寫程序 raise.py 如下:try: text = input('Please input digit: ') if not text.isdigit(): info = '"%s" is not digit' % text raise ValueError(info)except ValueError as e: print('except ValueError: %s' % e) 在第 2 行,提示用戶輸入數(shù)字;在第 3 行,如果用戶輸入的不是數(shù)字;在第 4 行,拼接字符串 info 用于描述錯誤的具體信息;在第 5 行,ValueError(info) 創(chuàng)建了一個對象,包括:異常類型和錯誤信息,使用 raise 拋出該異常對象;在第 6 行,捕獲程序拋出的 ValueError 類型的異常,變量 e 指向 raise 語句拋出的異常。程序輸出如下:C:\> python raise.pyPlease input digit: abctry:except ValuseError: abc is not digit在第 2 行,用戶輸入 abc在第 4 行,提示用戶的輸入錯誤: “abc is not digit”,具體的錯誤信息對用戶要友好
如果要使用手動安裝,請下載并解壓該應(yīng)用程序,例如將其解壓縮到 opt 目錄中,這一步可能需要 sudo 權(quán)限。復(fù)制到本地然后解壓:cp /mnt/HOMESHARE/Postman-linux-x64-7.17.0.tar.gz ~/MySpace/SoftwareIstP/tar -xzvf SoftwareIstP/Postman-linux-x64-7.17.0.tar.gz -C Software/要想從啟動圖標啟動應(yīng)用程序,需要創(chuàng)建一個桌面文件,命名為 Postman.desktop,并保存在以下位置:~/.local/share/applications/Postman.desktop在文件中輸入以下內(nèi)容改完之后保存:[Desktop Entry]Encoding=UTF-8 //編碼Name=Postman //應(yīng)用名稱Exec=/opt/Postman/app/Postman %U //執(zhí)行命令I(lǐng)con=/opt/Postman/app/resources/app/assets/icon.png //圖標Terminal=false //命令行窗口設(shè)為不啟動Type=Application //類型為“應(yīng)用”Categories=Development; //分類為“開發(fā)”Tips:如果你把文件解壓到了別的目錄,需要替換掉上面的文件路徑。
Python 中常見的 MySQL 的 驅(qū)動模塊有:MySQLdb: 它是對 C 語言操作 MySQL 數(shù)據(jù)庫的一個簡單封裝。遵循并實現(xiàn)了 Python DB API v2 協(xié)議。但是只支持 Python2, 目前還不支持 Python3;mysqlclient: 是 MySQLdb 的另外一個分支。支持 Python3 并且修復(fù)了一些 bug;pymysql: 純 Python 實現(xiàn)的一個驅(qū)動。因為是純 Python 編寫的,因此執(zhí)行效率不如前面二者;MySQL Connector/Python: MySQL 官方推出的使用純 Python 連接 MySQL 的驅(qū)動。同樣是純 Python 開發(fā)的,效率也不高。其中 mysqlclient 和 pymysql 是在 python 開發(fā)中最常使用的 MySQL 驅(qū)動模塊。而在 Django 內(nèi)部,我們接下來會看到,它的 ORM 模型其實是在 mysqlclient 基礎(chǔ)上再次封裝起來的。
上面講解了 NioEventLoop 的初始化流程,那么它到底在什么時候開始執(zhí)行的呢?源碼入口:serverBootstrap.bind(80);第一步: 抽象類 AbstractBootstrappublic abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable { public ChannelFuture bind(int inetPort) { return this.bind(new InetSocketAddress(inetPort)); } public ChannelFuture bind(SocketAddress localAddress) { this.validate(); if (localAddress == null) { throw new NullPointerException("localAddress"); } else { //繼續(xù)跟進 return this.doBind(localAddress); } } private ChannelFuture doBind(final SocketAddress localAddress) { //繼續(xù)跟進 final ChannelFuture regFuture = this.initAndRegister(); } final ChannelFuture initAndRegister() { //繼續(xù)跟進 this.init(channel); } //抽象方法 abstract void init(Channel var1) throws Exception;}第二步: 實現(xiàn)類 ServerBootstrappublic class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> { void init(Channel channel) throws Exception { //1.把 ChannelHandler 添加到 ChannelPipeline 里,組成一條雙向業(yè)務(wù)鏈表 p.addLast(new ChannelHandler[]{new ChannelInitializer<Channel>() { public void initChannel(Channel ch) throws Exception { //1.1.管道 final ChannelPipeline pipeline = ch.pipeline(); //1.2.添加到管道 ChannelHandler handler = ServerBootstrap.this.config.handler(); if (handler != null) { pipeline.addLast(new ChannelHandler[]{handler}); } //1.3.執(zhí)行線程池的 “execute()”,核心入口 ch.eventLoop().execute(new Runnable() { public void run() { pipeline.addLast( new ChannelHandler[]{ new ServerBootstrap.ServerBootstrapAcceptor( currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs) } ); } }); } }}); }}這里是在 init () 方法里面進行一序列的初始化工作,并且執(zhí)行上面初始化好的 NioEventLoop 的 execute () 方法。第三步: 執(zhí)行 SingleThreadEventExecutor 的 execute () 方法public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor { public void execute(Runnable task) { //是否是當前線程 boolean inEventLoop = this.inEventLoop(); if (inEventLoop) { //如果是當前線程,則添加任務(wù)到隊列 this.addTask(task); } else { //如果不是當前線程,則先啟動線程 this.startThread(); //把任務(wù)添加到任務(wù)隊列 this.addTask(task); //如果線程已經(jīng)關(guān)閉并且該任務(wù)已經(jīng)被移除了 if (this.isShutdown() && this.removeTask(task)) { //執(zhí)行拒絕策略 reject(); } } } private void startThread() { this.doStartThread(); } private void doStartThread() { this.executor.execute(new Runnable() { public void run() { //執(zhí)行 run() 方法 SingleThreadEventExecutor.this.run(); } }); } //抽象方法 protected abstract void run();}第四步: 子類 NioEventLoop 實現(xiàn)抽象方法 run (),這里是 run () 方法是一個死循環(huán),并且執(zhí)行三個核心事件,分別是 “監(jiān)聽端口”、“處理端口事件”、“處理隊列事件”。public final class NioEventLoop extends SingleThreadEventLoop { protected void run() { while(true) { //省略 } }}run () 方法里面核心執(zhí)行了 this.processSelectedKeys() 和 this.runAllTasks()。
JVM 是 Java 語言的一大關(guān)鍵亮點,對于 JVM 的作用,我們這里介紹兩個主要的作用,來體現(xiàn) JVM 的價值所在??缙脚_性:Java 語言之所以有跨平臺的優(yōu)點,完全是 JVM 的功勞,跨平臺性是 JVM 存在的最大的亮點。以上一個知識點部分所舉出例子來說,Windows 操作系統(tǒng)安裝上 JVM 之后,可以支持 Java 程序的運行; Linux 操作系統(tǒng)安裝上 JVM 之后,可以支持 Java 程序的運行;同理,Unix 操作系統(tǒng)等等所有我們熟悉的操作系統(tǒng),安裝上 JVM 之后,都可以支持 Java 程序的運行。這大大提升了 Java 語言的平臺靈活性,能夠在眾多語言爭鳴的時代,脫穎而出。優(yōu)秀的垃圾回收機制: Java 語言的誕生,極大的降低了軟件開發(fā)人員的學習難度,除了 Java 面向?qū)ο缶幊痰奶匦阅軌蚪档蛯W習難度以外,還有一個比較重要的點,就是在進行 Java 編程的時候,可以更少的去考慮垃圾回收機制。學習過 C 語言的技術(shù)人員都能夠體會這一點,因為 C 語言編程過程中,要通過代碼手動實現(xiàn)內(nèi)存垃圾的回收與空間釋放,這提升了編程的難度,因為考慮內(nèi)存空間釋放,更多的會涉及到底層的知識,這是非常高的一個門檻。從 JVM 的角度來說,JVM 擁有自己的垃圾回收機制,為開發(fā)人員分擔了部分工作。Tips:JVM 在 Java 語言中占據(jù)了非常重要的地位,學習 JVM 是 Java 技術(shù)人員必須要做的事情,目前企業(yè)對于 Java 從業(yè)者對 JVM 的掌握程度要求非常高,是重點學習內(nèi)容。
前面我們介紹的 XPath 基本的語法和基本操作,下面我們通過 lxml 庫,來熟悉一下 XPath 的使用。首先,我們需要一個測試文件,文件如下,文件名為 xpath_test.xml:<!--- 這是一個測試數(shù)據(jù),方便我們后面進行解析--><div><ul><li class='item-0'><a href='a.html'>python item</a></li><li class='item-1'><a href='b.html'>java item</a></li><li class='item-inactive'><a href='c.html'><span class='bold'>C item</span></a></li><li class='item-1'><a href='d.html'>java item</a></li><li class='item-0'><a href='b.html'>java item</a></li><li class='item-0'><a href='b.html'>java item</a></li></ul></div>
Ruby 中的序列范圍用于創(chuàng)建一系列連續(xù)值-由開始值,結(jié)束值和介于兩者之間的值范圍組成。實例:1..10 # 1 ~ 10 的序列1...10 # 1 - 9 的序列解釋:有兩個運算符可用于創(chuàng)建此類范圍,包括兩個點(..)的運算符和三個點(...)的運算符,他們的區(qū)別是兩個點的運算符包括范圍的第一個值和最后一個值,三個點的運算符不包括最后一個值。實例:# 我們可以通過to_a方法將序列轉(zhuǎn)換成數(shù)組> (1..10).to_a=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]> (1...10).to_a=> [1, 2, 3, 4, 5, 6, 7, 8, 9]我們還可以創(chuàng)建一個基于字符的序列:> ('a'..'l').to_a=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
命令選項說明-b加載可供查看的備用日志緩沖區(qū),例如 events 或 radio。-c清除(清空)所選的緩沖區(qū)并退出。–regex只輸出日志消息與正則表達式匹配的行。-m輸出特定行后退出。–print與 --regex 和 --max-count 配對,使內(nèi)容繞過正則表達式過濾器。-d將日志轉(zhuǎn)儲到屏幕并退出。-f將日志消息輸出寫入 。-g輸出指定日志緩沖區(qū)的大小并退出。-n設(shè)置輪替日志的數(shù)量上限。-r每輸出特定字節(jié)時輪替日志文件。-s相當于過濾器表達式 ‘*:S’。-v設(shè)置日志消息的輸出格式。-D輸出各個日志緩沖區(qū)之間的分隔線。–pid僅輸出來自給定 PID 的日志。
我們知道 Java 是面向?qū)ο蟮木幊陶Z言,但為了便于開發(fā)者上手,Java 沿用了 C 語言的基本數(shù)據(jù)類型,因此 Java 數(shù)據(jù)類型被分為了基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。對于簡單的運算,開發(fā)者可以直接使用基本數(shù)據(jù)類型。但對于需要對象化交互的場景(例如將基本數(shù)據(jù)類型存入集合中),就需要將基本數(shù)據(jù)類型封裝成 Java 對象,這是因為基本數(shù)據(jù)類型不具備對象的一些特征,沒有對象的屬性和方法,也不能使用面向?qū)ο蟮木幊趟枷雭斫M織代碼。出于這個原因,包裝類就產(chǎn)生了。包裝類就是一個類,因此它有屬性、方法,可以對象化交互。
C 語言中的常量可能和我之前所認識的常量不太一樣。其實叫做字面值更為貼切一點。它是由整數(shù)數(shù)字,浮點數(shù)字,字符,字符串等組成。而與常量這個英文單詞對應(yīng)的確實也是一個常量,只不過這個常量是一個不可以變化的量。只是用一個特定的名稱,代表了另外一個字面值。這個常量是恒定的。不可以嘗試改變一個常量,因為會產(chǎn)生錯誤。而常量的定義也會有兩種不同的方式。一種是采用預(yù)處理的方式,而另外一種則是采用了關(guān)鍵字的定義。兩者定義的位置也是不一樣的,我們在使用的時候要注意這一點。
在 Python 中使用反斜杠 \ 加字母的組合來表示一些特殊字符,例如:\n 表示換行,\ 之后的字符 n 的含義已經(jīng)不再是原來 ASCII 字符的含義了,所以被稱為轉(zhuǎn)義字符。常用的轉(zhuǎn)義字符如下所示:轉(zhuǎn)義字符描述 \t 制表 \n 換行 \’’\""\\\ 使用 \t 的示例 >>> print('a\tb\tc')a b c>>> print('aa\tbb\tcc')aa bb cc\t 表示跳到下一個制表符位置,可以使得輸出縱向?qū)R。使用 \n 的示例 >>> print('a\nb')ab使用 \’ 的示例 >>> print('\'')'在單引號字符串中使用 \’ 表示單引號使用 \" 的示例 >>> print("\"")"在雙引號字符串中使用 \" 表示雙引號使用 \\ 的示例 >>> print('\\')\在字符串中使用 ‘\’ 表示轉(zhuǎn)義字符 \
指針作為 C 語言中相對比較抽象的部分,是很多初學者最難逾越的部分。但是請大家放松心情。因為指針其實和普通變量沒有什么本質(zhì)的區(qū)別,其實叫做指針變量更為貼切一點。指針是一個變量,里面存儲的內(nèi)容是另外一個變量的內(nèi)存地址。每個變量都會有地址,包括指針變量自己本身也是有地址存在的。我們在賦值的時候是將別的變量通過變量名獲得的該變量的地址存儲到指針變量里,而后獲取這個被存儲到指針變量里的地址的數(shù)值,或者繼續(xù)將該變量的地址賦值給另外一個指針變量。指針變量的存在只是一個變量的賦值,切記理解為玄學。
下面列舉了一些 rpm 命令參數(shù)作用:rpm 命令參數(shù)名稱功能與作用描述-a查詢所有的軟件包-b設(shè)置包裝套件的完成階段,并指定套件檔的文件名稱-c只列出組態(tài)配置文件,本參數(shù)需配合 -l 參數(shù)使用-d只列出文本文件,本參數(shù)需配合 -l 參數(shù)使用-e卸載軟件包-f查詢文件或命令屬于哪個軟件包-h安裝軟件包時列出標記-i顯示軟件包的相關(guān)信息–install安裝軟件包-l顯示軟件包的文件列表-p查詢指定的 rpm 軟件包-q查詢軟件包-R顯示軟件包的依賴關(guān)系-s顯示文件狀態(tài),本參數(shù)需配合 -l 參數(shù)使用-U升級軟件包-v顯示命令執(zhí)行過程
export 是導(dǎo)出語法,import 是導(dǎo)入語法。看下面的實例:// a.jsexport let x = 1;export let y = 2;// main.jsimport {x, y} from './a.js';console.log(x, y)上面代碼中,a.js 文件中使用 export 導(dǎo)出 x 和 y 兩個變量,在 mian.js 文件中使用 import 進行導(dǎo)入。a.js 中還可以使用對象的方式導(dǎo)出:let a = 1;let b = 2;export { a, b,}從上面的 main.js 文件中可以看出,export 使用的是引用方式進行導(dǎo)出的,導(dǎo)出的是一個接口,所以不能直接導(dǎo)出一個值。我們?nèi)缦聦嵗簂et a = 1;export a; // 編譯報錯// 正確的方式如下Export let a = 1;雖然使用 export 不能直接導(dǎo)出一個值,但是可以使用 export default 導(dǎo)出一個特定的值:export default 100;export 模塊導(dǎo)出的是一個接口,在模塊內(nèi)如果數(shù)據(jù)更新,則所依賴的地方的值都是最新的。// a.jslet a = 1;setInterval(() => { a++})export { a}// main.jsimport { a } from './a.js';setInterval(() => { consolog.log(a)})import 有聲明的特點,類似 var 的特點,可以實現(xiàn)變量提升,但是不能修改變量對應(yīng)的值。// main.jsconsole.log(a)import { a } from './a.js';a = 100; // 這樣賦值是錯誤的使用 export + from 命令的方式,提供了一種便捷的方式在當前的模塊導(dǎo)出其他模塊的內(nèi)容,不能在當前模塊下使用導(dǎo)出的變量。// b.jslet a = 1;let b = 2;export { a, b,}// a.jsexport {a,b} from './b.js';export c = () => {}// 等價于使用import 先導(dǎo)入,然后再使用 export 導(dǎo)出import { a, b } from './b';export { a, b,}// main.jsimport {a, b, c} from './a.js'export 和 import 命令規(guī)定要處于模塊頂層,一旦出現(xiàn)在塊級作用域內(nèi),就會報錯。// a.js{ export let a = 1;}// main.js{ import { a } from './a';}//控制臺答應(yīng)錯誤內(nèi)容: 'import' and 'export' may only appear at the top level上面的代碼中 export 和 import 都放在塊級作用域中的,執(zhí)行時會報錯,提升你 export 和 import 只能在頂級出現(xiàn)。
2.1.1 原生開發(fā)原生開發(fā)一般是指用原生語言進行開發(fā),原生開發(fā)語言就是官方提供的開發(fā)語言,比如 IOS 是利用 Objective-C 和 Xcode 進行開發(fā)、小程序是用微信小程序原生語言和微信開發(fā)者工具進行開發(fā)。2.1.2 混合開發(fā)混合開發(fā)是指開發(fā)一個項目應(yīng)用時,為了提高運行效率或者開發(fā)效率,將各種開發(fā)技術(shù)進行混合應(yīng)用的開發(fā)形式?;旌祥_發(fā)將各種開發(fā)方式取長補短,雖然比原生開發(fā)更難上手,但是架不住開發(fā)效率和運行效率的優(yōu)勢,現(xiàn)在比較大型的應(yīng)用項目都會用混合開發(fā)來進行項目開發(fā)。uni-app 的混合開發(fā)主要是為了實現(xiàn)一些 uni-app 框架無法實現(xiàn)的功能,或者擴展接入一些第三方的 SDK。
在 JavaScript 中是沒有類的概念的。有其他面向?qū)ο箝_發(fā)經(jīng)驗的同學可能會被 new 關(guān)鍵字誤導(dǎo)。JavaScript 中采用的是原型的機制,很多文獻會稱其為 原型代理,但個人認為對于初學者使用 原型繼承 的方式會更好理解一點,日常討論中其實是一個意思,不需要過多糾正其說法。類和原型是兩種不同的機制。有關(guān)于類的內(nèi)容,篇幅很大,如果不熟悉但又感興趣,可以嘗試著接觸一下其他面向?qū)ο蟮恼Z言,如 Python、Java、C++。ES6 提供了 class 關(guān)鍵字,引入了一些類相關(guān)的概念,但其底層運行機制依然是原型這一套,所以即便是有了 class 關(guān)鍵字來幫助開發(fā)者提升開發(fā)體驗,但其本質(zhì)依然不是類,只是一種原型寫法的語法糖。
Httpd 是 C 語言編寫的遵從 Http 協(xié)議的服務(wù)器,是一個高度模塊化軟件,由 Server 和 Module 組成。這些模塊大都是動態(tài)模塊,因此可以隨時加載。源碼開源地址:https://github.com/apache/httpd ;官網(wǎng)地址:https://httpd.apache.org;Httpd 作為起步比較早的一個 Web 開源項目,代碼的穩(wěn)定性/社區(qū)/文檔 都是比較可靠的,他支持的功能非常豐富,并且可以按需地引入自己所需要的模塊。Httpd 一般比較的對象是 Nginx 服務(wù)器,他們兩個是靜態(tài)資源服務(wù)器的首選:Nginx 輕量且并發(fā)能力高于 Httpd;Nginx 能夠?qū)崿F(xiàn)負載均衡;Httpd 支持的功能模塊比較豐富;Httpd 的 rewrite 功能強于 Nginx。當然,也有的網(wǎng)站架構(gòu)同時用到了 Nginx 和 Httpd ,用 Nginx 作為負載均衡,將流量分發(fā)到后面的 Httpd Web服務(wù)端。
從集合的最后一項開始取出滿足條件元素,這樣操作一直持續(xù)到出現(xiàn)第一個不滿足條件元素出現(xiàn)為止,暫停取元素,返回取出元素的集合。源碼定義public inline fun <T> List<T>.takeLastWhile(predicate: (T) -> Boolean): List<T> { if (isEmpty())//如果當前集合是一個空的,那么直接返回空集合 return emptyList() val iterator = listIterator(size)//表示從集合index = size開始迭代,那么size - 1也是最后一個元素,也即是迭代器的previous,也就是從集合尾部開始向頭部迭代 while (iterator.hasPrevious()) {//含有上一個元素的元素繼續(xù)進入迭代 if (!predicate(iterator.previous())) {//直到某個元素的前一個元素不符合條件,也是從最后一項開始遇到第一個不符合條件的元素,不進入以下操作 iterator.next() val expectedSize = size - iterator.nextIndex()//由于從尾部開始迭代,那么符合條件元素集合的expectedSize等于原集合size與當前下一個元素的index的差值 if (expectedSize == 0) return emptyList()//差值為0的話說明,在原集合尾部開始迭代就不符合條件被終止,所以返回空集合 return ArrayList<T>(expectedSize).apply {//拿到符合條件元素集合size,創(chuàng)建expectedSize大小新集合,并把迭代器中的元素遍歷加入到新集合中 while (iterator.hasNext()) add(iterator.next()) } } } return toList()}源碼解析takeLastWhile 操作符是一個集合的擴展內(nèi)聯(lián)函數(shù),也是一個高階函數(shù),它接收一個以接收T泛型參數(shù)返回一個 Boolean 類型的 Lambda 表達式,也是即是 takeLastWhile 取元素的條件的實現(xiàn)。原理圖解使用場景適用于取出集合中后半部分具有相同特征的元素場景。fun main(args: Array<String>) { val strList = listOf("java", "javaScript", "kotlin", "C", "C++", "javaFx", "python","Go", "Swift", "Scala") strList.takeLastWhile { it.startsWith("S") }.forEach { print("$it ") }}
Flask 程序可以運行在 Debug 模式下,Debug 模式提供了如下功能:當 Flask 程序出現(xiàn)錯誤時,在瀏覽器中提示錯誤的詳細信息修改 Flask 程序代碼后,F(xiàn)lask 程序會重新加載,不需要重啟 Flask 程序,即可在瀏覽器中看到修改后的效果調(diào)用 Flask 應(yīng)用的 run 方法時,設(shè)置參數(shù) Debug = True,啟動 Flask 程序的調(diào)試模式。編寫程序 debug-on.py 如下:from flask import Flaskapp = Flask(__name__)if __name__ == '__main__': app.run(debug = True)在第 5 行,app.run(debug = True),啟動 Flask 程序的調(diào)試模式。運行程序,輸出如下:$ python3 debug.py * Serving Flask app "debug" (lazy loading) * Environment: production * Debug mode: on * Restarting with stat * Debugger is active! * Debugger PIN: 316-471-540 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)在第 4 行,Debug mode: on,表示 Flask 程序已經(jīng)進入了調(diào)試模式。
我們將以最常用的FileInputStream實現(xiàn)類為例進行學習。其他實現(xiàn)類大同小異,如有需要可翻閱官方文檔。FileInputStream就是從文件流中讀取數(shù)據(jù),我們在imooc目錄下新建一個文本文檔Hello.txt,并輸入如下內(nèi)容:讀取Hello.txt文件中數(shù)據(jù)的實例代碼如下:import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class FileInputStreamDemo1 { public static void main(String[] args) throws IOException { // 實例化文件流 FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Colorful\\Desktop\\imooc\\Hello.txt"); for (;;) { int n = fileInputStream.read(); if (n == -1) { // read() 方法返回-1 則跳出循環(huán) break; } // 將n強制轉(zhuǎn)換為 char 類型 System.out.print((char) n); } // 關(guān)閉文件流 fileInputStream.close(); }}運行結(jié)果:Hello imooc!如果我們打開了一個文件并進行操作,不要忘記使用close()方法來及時關(guān)閉。這樣可以讓系統(tǒng)釋放資源。
根據(jù)傳入數(shù)值 n,表示從右到左倒序地刪除 n 個集合中的元素,并返回集合中剩余的元素。源碼定義public fun <T> List<T>.dropLast(n: Int): List<T> { require(n >= 0) { "Requested element count $n is less than zero." } return take((size - n).coerceAtLeast(0))//這里應(yīng)該是this.take(),this指代List,然后傳入(size - n)必須滿足大于或等于0}//這是一個Int類型的擴展函數(shù),用于判斷某個值是否大于傳入默認最小值,如果大于就直接返回這個值,否則返回這個默認最小值public fun Int.coerceAtLeast(minimumValue: Int): Int { return if (this < minimumValue) minimumValue else this}//take也是一種操作符public fun <T> Iterable<T>.take(n: Int): List<T> { require(n >= 0) { "Requested element count $n is less than zero." } if (n == 0) return emptyList()//這里n 實際上是size - dropLast傳入n的差值,n為0表示dropLast傳入n為原集合size,相當于刪除原集合size個數(shù)元素,那么剩下就是空集合了 if (this is Collection<T>) {//如果是一個只讀類型集合,就可以確定該集合的size if (n >= size) return toList()//如果這里n等于size表示dropLast傳入n為0,那么表示刪除集合元素個數(shù)為0,那么剩下來就是整個原集合了 if (n == 1) return listOf(first())//如果n等于1,表示dropLasr傳入n為size-1,那么表示刪除集合個數(shù)size-1個,由于刪除順序是倒序的,自然原集合剩下的元素就是第一個元素了。 } //以下是針對this是一個可變集合,由于可變集合的size不太好確定,所以采用另一方式實現(xiàn)dropLast功能。 var count = 0 val list = ArrayList<T>(n)//創(chuàng)建剩余集合元素size大小n的可變集合 for (item in this) {//由于是從右到左遞增刪除的,取剩余,現(xiàn)在是采用逆向方式,從左到右加入新的集合中,一直等待count計數(shù)器自增到n為止。 if (count++ == n) break list.add(item) } return list.optimizeReadOnlyList()}原理圖解使用場景使用的場景和 drop 相反,但是整體作用和 drop 類似。fun main(args: Array<String>) { val strList = listOf("kotlin", "java", "javaScript", "C", "C++", "python", "Swift", "Go", "Scala") strList.dropLast(3).forEach { print("$it ") }}
上面我們講了在 Ruby 環(huán)境中安裝 Sass ,但我們前端在 Ruby 環(huán)境下開發(fā)是非常少的,我們前端現(xiàn)在基本都使用 Webpack 構(gòu)建,一般都是在 Node 環(huán)境開發(fā),那在前端項目里是如何安裝 Sass 呢?首先你要知道 node-sass 和 dart-sass ,這兩個都是提供好的類庫,是 Sass 的實現(xiàn),本身 Sass 是使用 Ruby 語言寫的,但是它提供了很多接口以方便其他語言來集成和封裝,node-sass 和 dart-sass 就是基于 LibSass( Sass 的 C 版本) 封裝而來的。它們和 LibSass 的關(guān)系就是橘子和橘子汁的關(guān)系,我們前端基本也都是通過這兩個庫來使用 Sass ,我們畫個圖來看下它們的關(guān)系:本章節(jié)我們以 node-sass 為例,本教程中所有的內(nèi)容都是以 node-sass 為例的。
tooltip 組件的核心作用是展示數(shù)據(jù)項相關(guān)的信息,提示內(nèi)容可以通過 tooltip.formatter項進行配置。tooltip.formatter接受模板字符串、模板函數(shù)兩種類型的值:2.4.1 使用模板字符串定制提示框內(nèi)容模板字符串行人如 {a}: <br />{c}其中 {}為 echarts 提供的模板變量,不同圖表所提供的變量集合不同,但通常有:a:表示系列名;b:表示數(shù)據(jù)名;c:表示數(shù)據(jù)值。更多信息可參考 官網(wǎng)解釋。模板字符串支持傳入 html 標簽,這在 tooltip.renderModel = html時會被渲染為標準的 DOM 結(jié)構(gòu),例如:1325示例中設(shè)定提示框的格式為 'Data Item:<br /> {b0}: <strong>{c0}</strong>'渲染結(jié)果:Tips:模板字符串存在一些明顯的缺陷:功能單一,只實現(xiàn)了變量替換功能,格式化時只能沿用 echarts 所提供的變量集合,不能做進一步計算,即使是很簡單的百分比格式化也無法實現(xiàn);變量的類型、數(shù)量、順序與 tooltip 所在位置強相關(guān),模板與圖表強耦合,若實際應(yīng)用中變更了圖表類型,可能導(dǎo)致模板失效;變量名均為 a、b、c 等沒有語義的字符,這在某種程度上增加了記憶強度;格式化文本在不同渲染模式下可能渲染出不同的效果,詳見 2.5 控制提示框渲染方式 一節(jié)。模板字符串實現(xiàn)的非常雞肋,無法承擔較復(fù)雜的格式化需求,建議盡量使用模板函數(shù)方式。當圖表上有多個數(shù)據(jù)序列,傳入的變量名會出現(xiàn)有點變化,例如 :a0:a 代表系列名,0 代表系列下標,根據(jù)傳入的系列數(shù)量還會有 a1a2等;b0:b 代表數(shù)據(jù)名,0 位系列下標;c0:c 代表數(shù)據(jù)值,0 代表系列下標。例如下例中:1326示例包含兩個折線圖,此時 tooltip.formatter為 'Data Item:<br /> {a0}: <strong>{c0}</strong> <br /> {a1}: <strong>{c1}</strong>'指定了 a0、a1 等變量,渲染結(jié)果:2.4.2 使用模板函數(shù)定制提示框內(nèi)容tooltip.formatter還支持傳入函數(shù)值,簽名形如:(params: Object|Array, ticket: string, callback: (ticket: string, html: string)) => stringTips:模板函數(shù)需返回字符串值,與模板字符串相似,若渲染模式 tooltip.renderMode = html則字符串中支持 html 標簽。其中包含參數(shù):params:上下文環(huán)境,包含提示框所在位置的關(guān)鍵信息ticket:異步回調(diào)令牌,若提示框內(nèi)容需要以異步形式計算時,需使用令牌實現(xiàn)回調(diào);callback:異步回調(diào)函數(shù)。params 形態(tài)不定,根據(jù)觸發(fā)方式、圖表類型會有些差異,但通常包含如下屬性:{ componentType: 'series', // 系列類型,如 line、pie、bar seriesType: string, // 系列在傳入的 option.series 中的 index seriesIndex: number, // 系列名稱 seriesName: string, // 數(shù)據(jù)名,類目名 name: string, // 數(shù)據(jù)在傳入的 data 數(shù)組中的 index dataIndex: number, // 傳入的原始數(shù)據(jù)項 data: number|Array|Object, // 傳入的數(shù)據(jù)值。在多數(shù)系列下它和 data 相同。在一些系列下是 data 中的分量(如 map、radar 中) value: number|Array|Object, // 坐標軸 encode 編碼方式 encode: Object, // 維度名列表 dimensionNames: Array<String>, // 數(shù)據(jù)的維度 index,如 0 或 1 或 2 ... // 僅在雷達圖中使用。 dimensionIndex: number, // 數(shù)據(jù)圖形的顏色 color: string, // 餅圖的百分比 percent: number,}示例:1327示例效果:Tips:params 參數(shù)的值與提示框所在位置強相關(guān),建議開發(fā)時使用 debugger、console.dir 等手段進一步確認。模板函數(shù)支持異步形式,需要配合使用 ticket、callback 兩個參數(shù),在上例基礎(chǔ)上,修改 tooltip 配置:formatter(params, ticket, cb) { // 執(zhí)行異步操作 setTimeout(() => { // 異步操作完成后,需調(diào)用 cb 回調(diào) // 傳入 ticket 及提示內(nèi)容字符串 cb(ticket, 'Async Success'); }, 1000); // 立即返回過渡態(tài)的提示內(nèi)容 return 'Loading';},其中,ticket 為 ECharts 內(nèi)部令牌,無需關(guān)注;callback 為異步回調(diào)函數(shù),示例效果:Tips:模板函數(shù)在每次激活提示框時都會被觸發(fā),ECharts 沒有對函數(shù)的執(zhí)行做任何性能優(yōu)化,這可能導(dǎo)致:如果模板函數(shù)的執(zhí)行時間長,會導(dǎo)致頁面卡頓,影響交互效果。如果模板函數(shù)包含了異步操作,比如調(diào)用 ajax 接口,則接口會隨提示框的激活而多次被調(diào)用。
我們知道,Array 是 JavaScript 內(nèi)置的構(gòu)造函數(shù),構(gòu)造函數(shù)屬性(prototype)的 constructor 指向構(gòu)造函數(shù)(見下圖),那么通過 constructor 屬性也可以判斷是否為一個數(shù)組。var arr = new Array('a', 'b', 'c');arr.constructor === Array; //true下面我們通過構(gòu)造函數(shù)的示意圖來進行分析:由上面的示意圖可以知道,我們 new 出來的實例對象上的原型對象有 constructor 屬性指向構(gòu)造函數(shù) Array,由此我們可以判斷一個數(shù)組類型。但是 constructor 是可以被重寫,所以不能確保一定是數(shù)組,如下示例:var str = 'abc';str.constructor = Array;str.constructor === Array // true上面的代碼中,str 顯然不是數(shù)組,但是可以把 constructor 指向 Array 構(gòu)造函數(shù),這樣再去進行判斷就是有問題的了。constructor 和 instanceof 也存在同樣問題,不同執(zhí)行環(huán)境下,constructor 的判斷也有可能不正確,可以參考 instanceof 的例子。
其實在 Maven 的世界中,生命周期只是一個抽象的模型,其本身并不會直接去做事情,真正幫我們完成事情的是 Maven 的插件。Maven 的插件也屬于構(gòu)件的一種,也是可以放到 Maven 倉庫當中的。通常情況下,一個插件可以做 A、B、C 等等不止一件事情,但是我們又沒有必要為每一個功能都做一個單獨的插件。這種時候,我們一般會給這個插件綁定不同的目標,而這些目標則是對應(yīng)其不同的功能。當我們使用一個插件的目標的時候,我們可以執(zhí)行命令:mvn pluginName:goalName。例如當我們執(zhí)行dependency插件的 list 目標的時候,我們可以執(zhí)行命令:mvn dependency:list。使用該插件目標,我們可以看到目前我們項目中所有依賴的情況。
在了解了如何使用 go mod 之后,接下介紹一個在項目中使用的例子。在任意文件夾中創(chuàng)建Go文件,然后錄入以下代碼:package mainimport "github.com/gin-gonic/gin"func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello Codey!", }) }) r.Run() // listen and serve on 0.0.0.0:8080}隨后打開控制臺,輸入:go mod initgo mod tidy你就會看到 go mod 會下載很多相關(guān)依賴包:Tips:你們下載的依賴包可能會比我多的多,因為我只是為了展示隨機刪了幾個依賴包重新下載。同時文件夾下多了個 go.mod 和 go.sum ,其中 go.sum 不用在意,其中的內(nèi)容為導(dǎo)入包的路徑、版本和它的hash ,是自動生成自動更新的。go.mod 的內(nèi)容為:module firstgogo 1.13require github.com/gin-gonic/gin v1.6.3自動記錄了你引用的包路徑和版本。如果要修改使用版本,直接修改版本號即可。
目前大部分的互聯(lián)網(wǎng)公司都支持遠程筆試和面試,具體的流程如下:筆試流程在候選人投遞簡歷之后,企業(yè)會提前發(fā)送筆試郵件告知候選人。因為候選人投遞簡歷的時間比較分散,所以互聯(lián)網(wǎng)企業(yè)一般會將候選人分為不同的批次,被分到同一批次的用戶參加同一場筆試,筆試題型一般分為選擇題、問答題、編程題,筆試時間一般是一個半小時到兩個小時。因為問答題需要人為改卷,選擇題和編程題都可以系統(tǒng)自動判定分數(shù),所以選擇題+編程題的出題方式比較常見,其中編程題大多是2到4道,主要都是算法題,完成語言不限制(一般都支持C++、Java、Python、Javascript這幾種語言)。
在生成 ssh key 之前我們可以先到本地磁盤看看有沒有之前生成過的。點擊鼠標右鍵,然后點擊 “git bash”,可以調(diào)出 git 命令行,輸入 cd ~/.ssh,進入到目錄后輸入 ls ,查看是否存在密鑰。如果沒有,則需要手動生成,可以看到我這邊現(xiàn)在是沒有密鑰文件的;通過下面的命令生成 ssh key:ssh-keygen -t rsa -C "youremail@example.com",這里的郵箱可以就填你 github 綁定的郵箱,然后一直回車即可完成當前步驟,完成后再查看一下目錄,可以看到已經(jīng)生成好了兩個密鑰,id_rsa 文件是私鑰,不需要上傳,id_rsa.pub 文件是公鑰,是需要上傳的;登錄 Github 。打開 setting -> SSH keys,點擊右上角 New SSH key,把生成好的公鑰 id_rsa.pub 放進 key 輸入框中,再為當前的 key 起一個 title 即可。