1、函數
相比于C++,python中寫一個函數需要一個關鍵字def。其主要結構如下:
def 函數名(函數參數):
"""函數文檔,docstring"""
函數體
return
如果一個函數是一個類的一部分,它就稱為一個方法。
注意函數沒有類型信息,無論是函數參數還是返回值,都不需要類型信息,當然為了便于閱讀,可以使用函數注解。
函數注解是py3新增的一項功能,類似注釋,它們只負責提高程序的可讀性,而不會對程序功能做出任何改變。
推薦python代碼為了可讀性,遵循PEP8的規范。以下是一個例程:
def search4vowels(word):
"""Display any vowels found in an asked-for word."""
vowels=set('aeiou')
found=vowels.intersection(set(word))
for vowel in found:
print(vowel)
效果如下:
可以看得出來,犯了很多愚蠢的錯誤。首先,最重要的是,雖然沒有為函數的參數指定類型,但函數參數必須有類型,像我第一次那樣,參數直接是chy是肯定不行的,單獨的chy會被編譯器認為是一個變量而不是一個字符串;另外,第二次沒有輸入參數,會報缺少參數的錯誤,第三次直接把函數名打錯了,最后一次,加了單引號,得到正確結果。
在python中,每一個對象都有一個與之關聯的真值,表示這個對象為True或者False。如果該對象計算為0、值為None、是一個空串或一個空的數據結構,那么就是False,否則是True。使用函數bool來計算對象的真值。嚴格來說,所有的非空數據結構都計算為True。
默認來說,函數使用return關鍵字來返回值,只能返回一個值,若想返回多個值,就需要使用數據結構,這和C++中類似。
將上面的函數改為返回word中的元音,如下:
def search4vowels(word):
"""Display any vowels found in an asked-for word."""
vowels=set('aeiou')
return vowels.intersection(set(word))
效果如下:
注意,當word中不含元音時,應該返回一個空集合。由于集合使用{}包圍,那么空集合應該返回{}。但是返回的是set()。這是因為,{}沒法區分是空集合還是空字典,因此用{}表示空字典,用set()表示空集合。
上述代碼加入注解后的效果如下:
def search4vowels(word:str)->set:
"""Display any vowels found in an asked-for word."""
vowels=set('aeiou')
return vowels.intersection(set(word))
在運行時,加不加注解沒有任何區別,只有在使用help時有區別:
help會把注解顯示,還會把docstring也顯示。
接下來,定義一個更泛用的函數,用于找到兩個字符串中所有相同字符的集合,代碼如下:
def search4vowels(phrase:str)->set:
"""Display any vowels found in a supplied phrase."""
vowels=set('aeiou')
return vowels.intersection(set(word))
def search4letters(phrase:str,letters:str)->set:
"""Return a aet of the 'letters' found in 'phrase'."""
return set(letters).intersection(set(phrase))
可以在一個文件中定義多個函數,注意,根據PEP8,兩個函數間要空兩行。這里的第二個函數search4letters用于實現找到兩個字符串的交集。注意,第一個函數是第二個函數在letters=‘aeiou’情況下的特例,因此,如果能夠讓letters默認為aeiou,那么就可以實現函數二對函數一的完全代替。這樣就可以實現函數的可重用。
如何實現呢?很簡單,在參數注解的后面加入默認值即可,如下:
def search4letters(phrase:str,letters:str='aeiou')->set:
"""Return a aet of the 'letters' found in 'phrase'."""
return set(letters).intersection(set(phrase))
這樣,當我們在調用search4letters函數時,若只用了一個參數,那么就默認第二個參數是aeiou;若用了兩個參數,那么letters就不再是默認值。默認值與初值不同,不要搞混。默認值只有在def行才能修改。
與C++不同的是,python提供了兩種參數賦值方法,位置賦值與關鍵字賦值。其中,位置賦值的情況下,對函數的調用與C++中相同,即函數f(A,B,C)調用時就是f(1,2,3),其中A=1,B=2,C=3。然而,關鍵字賦值的情況下,參數順序就沒用了,調用f可以是f(C=3,A=1,B=2),即點名某個參數等于多少。這樣就不用記住參數順序了,當然缺點就是要多打一些字。
2、模塊
好,既然在1中,我們已經寫好了函數,那么,如何實現它的可重用呢?
如果調用函數的代碼就在函數文件里,那直接用就好了,但是如果多個文件都要用這個函數,那每個文件里都把這個函數重新寫一遍,那就太蠢了。要使用import。
我們已經知道了,import是用來引入模塊的,也就是說,已經有了函數,還要把它打包成一個模塊,才能在其他文件中調用這個函數。
首先來看import如何引入模塊。
使用import A引入一個名為A的模塊,編譯器會去三個地方找這個叫A的模塊:首先在當前工作目錄找,然后去解釋器的site-package找,最后去標準庫位置找。找到了的話,直接引入,沒找到就報錯。
當前工作目錄,這個概念我是在linux中了解到的。在win中,用cmd進入命令行
那么這里的C:\Users\Amarantine就是當前工作目錄,可以用cd來換工作目錄。
如果我們import的模塊不在工作目錄中,那就會報錯。
因此,要想使用一個模塊,可以把它放在我們的當前工作目錄里,但是這樣太麻煩了。更好的方法是放在site-package里,這里放著的都是第三方的python模塊。標準庫模塊不要想了,那不是我們能染指的。
在py3.4中,引入了一個新的模塊,setuptools,使用這個模塊可以較為方便的在site-package中加入我們自己的模塊,僅需三步:
①、創建一個發布描述
至少需要兩個描述文件,一個是setup.py安裝文件,一個是README.txt解釋文件。這兩個文件必須放在和函數源碼文件相同的文件夾中。setup文件中的代碼幾乎是定死的,以發布vsearch這個模塊為例,其代碼如下:
from setuptools import setup
setup(
name='vsearch',
version='1.0',
description='The Head First Python Search Tools',
author='Osiris',
author_email='123456@qq.com',
url='baidu.com',
py_modules=['vsearch'],
)
重要的參數有兩個,一個是name,其值是發布的包的名字;另一個是py_modules,其值是文件夾中所有py文件的列表。注意,這里py_modules即使填寫錯誤,也不會報錯。我第一次就是把vsearch打成了vresearch導致安裝成功卻import失敗。很是蛋疼。
txt文件可以為空。
如圖,文件夾中有三個文件:
②、生成一個發布文件
這一步要使用命令行來完成。
在當前工作目錄移到setup文件所在的文件夾內后,使用命令
py -3 setup.py sdist
來生成發布文件。發布文件是一個壓縮包,保存在工作路徑下的一個叫disk的文件夾中。
③、安裝發布文件
py3.4包含了一個名為pip的工具,可以用來安裝包。將工作目錄設置為disk后,按下圖輸入命令。
可以看出,已經成功安裝。
需要驗證是否安裝成功,只靠上面的顯示是不行的。要用import來驗證。
如圖可以看出,在我把setup文件改好之后,模塊真正安裝成功了。
注意,site-package實際上是一個文件夾,位于...\python\Lib\site-packages,真正安裝好后,會有一個文件夾,名字中帶有安裝的模塊,另外,模塊中函數的源碼也存在該文件夾中,如下:
如圖,可以看見vsearch-1.0-py3.7.egg-info文件夾和vsearch.py文件。
3、函數的按值調用與按引用調用
在學C的時候,我們已經清楚了按值調用和按引用調用的區別,這是一個重要考點。
按值調用不會改變函數參數的值,但按引用調用會改變。
在python中,函數既支持按值調用,又支持按引用調用。也就是說,在某些情況下,函數的行為看起來像按值調用,在另外一些情況下,則是按引用調用。其本質則是因為,python中變量都是對象引用,變量中存儲的是值的內存地址,而不是真正的值,從這一點看,所有的變量都是指針。
當變量指向的值是可變的值:列表、字典、集合時,按引用調用來行為;
當變量指向的值是不變的值:字符串、正數、元組時,按值調用來行為。
然而有一點要注意,參見下面兩個函數:
#按值調用
def double(arg):
print('Before: ',arg)
arg=arg*2
print('After: ',arg)
#按引用調用
def change(arg):
print('Before: ',arg)
arg.append('More data')
print('After: ',arg)
當arg為整數時,調用double函數,參數為a,看上去a會不變,因為a是一個整數,屬于不變的值,應按值調用,實際也是這樣:
但是當arg為一個列表時,按原來的想法,參數為b,是一個列表,列表屬于可變的值,因此b應該翻倍,因為是按引用調用,實際卻不是這樣:
可以看出,arg的確翻倍了,但是b沒有翻倍。
這是python中=的特性決定的。
在python中,=是賦值操作,在賦值時,首先執行=右邊的代碼,創建一個新的對象,然后將這個新的對象賦給=左邊的變量。也就是說,在上面的代碼中,是對b的內容進行*2的操作,形成一個新的對象,賦給arg,然而b本身一點變化都沒有。因此b不會變化。
其實還有一個好玩的事,前面說過,python中變量都是指針,那么參見下圖:
我們將a初始化為一個整數1,b初始化為2,然后觀察a和b的地址,它們是不同的;
然后將a加1,現在a=2,按C的理解,應該是140734582055568這個地址中的內容從1變成了2,但實際不是這樣,是a指向了b的地址,原因是b=2。這也可以看出,對于不能改變的量,python賦值采用的是改變指向的操作完成的。這也導致了在C中很容易做的數字處理變得麻煩。
4、檢查PEP8的兼容性
若想檢查代碼是否符合PEP8的規范,需要使用pytest框架和pep8插件。同樣的,我們使用pip進行安裝。
按如下執行代碼:
如下圖表示安裝成功。
接下來按照同樣方法安裝pep8。
安裝成功之后,前往需要檢查的代碼文件所在的文件夾,此處就是vsearch.py所在的文件夾,執行如下命令:
py.test --pep8 vsearch.py
效果如下:
可以看出,有五處不合格,三處是代碼中間的冒號:后面沒有加空格,一處是逗號,后面沒有加空格,一處是等號=左右沒有加空格。更改。然后再次檢查。如下:
右側的100%和1 passed說明已經沒有錯誤。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
